2021-05-24 07:25:56 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org>
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2007-01-13 23:15:28 +00:00
|
|
|
|
|
|
|
#ifndef _OKULAR_TEXTDOCUMENTGENERATOR_P_H_
|
|
|
|
#define _OKULAR_TEXTDOCUMENTGENERATOR_P_H_
|
|
|
|
|
2018-08-31 09:23:45 +00:00
|
|
|
#include <QAbstractTextDocumentLayout>
|
|
|
|
#include <QTextBlock>
|
|
|
|
#include <QTextDocument>
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2007-04-20 12:37:12 +00:00
|
|
|
#include "action.h"
|
2020-07-10 22:15:05 +00:00
|
|
|
#include "debug_p.h"
|
2007-01-13 23:15:28 +00:00
|
|
|
#include "document.h"
|
2007-09-14 13:31:55 +00:00
|
|
|
#include "generator_p.h"
|
2007-01-13 23:15:28 +00:00
|
|
|
#include "textdocumentgenerator.h"
|
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
namespace Okular
|
|
|
|
{
|
|
|
|
namespace TextDocumentUtils
|
|
|
|
{
|
|
|
|
static void calculateBoundingRect(QTextDocument *document, int startPosition, int endPosition, QRectF &rect, int &page)
|
|
|
|
{
|
|
|
|
const QSizeF pageSize = document->pageSize();
|
|
|
|
|
|
|
|
const QTextBlock startBlock = document->findBlock(startPosition);
|
|
|
|
const QRectF startBoundingRect = document->documentLayout()->blockBoundingRect(startBlock);
|
|
|
|
|
|
|
|
const QTextBlock endBlock = document->findBlock(endPosition);
|
|
|
|
const QRectF endBoundingRect = document->documentLayout()->blockBoundingRect(endBlock);
|
|
|
|
|
|
|
|
const QTextLayout *startLayout = startBlock.layout();
|
|
|
|
const QTextLayout *endLayout = endBlock.layout();
|
|
|
|
if (!startLayout || !endLayout) {
|
|
|
|
qCWarning(OkularCoreDebug) << "Start or end layout not found" << startLayout << endLayout;
|
|
|
|
page = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int startPos = startPosition - startBlock.position();
|
|
|
|
const int endPos = endPosition - endBlock.position();
|
|
|
|
const QTextLine startLine = startLayout->lineForTextPosition(startPos);
|
|
|
|
const QTextLine endLine = endLayout->lineForTextPosition(endPos);
|
|
|
|
|
|
|
|
const double x = startBoundingRect.x() + startLine.cursorToX(startPos);
|
|
|
|
const double y = startBoundingRect.y() + startLine.y();
|
|
|
|
const double r = endBoundingRect.x() + endLine.cursorToX(endPos);
|
|
|
|
const double b = endBoundingRect.y() + endLine.y() + endLine.height();
|
|
|
|
|
|
|
|
const int offset = qRound(y) % qRound(pageSize.height());
|
|
|
|
|
|
|
|
if (x > r) { // line break, so return a pseudo character on the start line
|
|
|
|
rect = QRectF(x / pageSize.width(), offset / pageSize.height(), 3 / pageSize.width(), startLine.height() / pageSize.height());
|
|
|
|
page = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
page = qRound(y) / qRound(pageSize.height());
|
|
|
|
rect = QRectF(x / pageSize.width(), offset / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height());
|
|
|
|
}
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
static QVector<QRectF> calculateBoundingRects(QTextDocument *document, int startPosition, int endPosition)
|
|
|
|
{
|
|
|
|
QVector<QRectF> result;
|
|
|
|
|
|
|
|
const QSizeF pageSize = document->pageSize();
|
|
|
|
|
|
|
|
const QTextBlock startBlock = document->findBlock(startPosition);
|
|
|
|
const QRectF startBoundingRect = document->documentLayout()->blockBoundingRect(startBlock);
|
|
|
|
|
|
|
|
const QTextBlock endBlock = document->findBlock(endPosition);
|
|
|
|
const QRectF endBoundingRect = document->documentLayout()->blockBoundingRect(endBlock);
|
|
|
|
|
|
|
|
const QTextLayout *startLayout = startBlock.layout();
|
|
|
|
const QTextLayout *endLayout = endBlock.layout();
|
|
|
|
if (!startLayout || !endLayout) {
|
|
|
|
qCWarning(OkularCoreDebug) << "Start or end layout not found" << startLayout << endLayout;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
const int startPos = startPosition - startBlock.position();
|
|
|
|
const int endPos = endPosition - endBlock.position();
|
|
|
|
const QTextLine startLine = startLayout->lineForTextPosition(startPos);
|
|
|
|
const QTextLine endLine = endLayout->lineForTextPosition(endPos);
|
|
|
|
|
|
|
|
// This only works if both start and end layout are the same
|
|
|
|
if (startLayout == endLayout) {
|
|
|
|
Q_ASSERT(startBoundingRect == endBoundingRect);
|
|
|
|
for (int i = startLine.lineNumber(); i < endLine.lineNumber(); ++i) {
|
|
|
|
const QTextLine line = startLayout->lineAt(i);
|
|
|
|
// using startPos and endPos is fine, if the pos is out of bounds for that line, it'll return beginning and end of line respectively
|
|
|
|
const double x = endBoundingRect.x() + line.cursorToX(startPos);
|
|
|
|
const double y = endBoundingRect.y() + line.y();
|
|
|
|
const double r = endBoundingRect.x() + line.cursorToX(endPos);
|
|
|
|
const double b = endBoundingRect.y() + line.y() + endLine.height();
|
|
|
|
|
|
|
|
result.append(QRectF(x / pageSize.width(), y / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height()));
|
|
|
|
}
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
// The last line
|
|
|
|
const double x = endBoundingRect.x() + endLine.cursorToX(startPos);
|
|
|
|
const double y = endBoundingRect.y() + endLine.y();
|
|
|
|
const double r = endBoundingRect.x() + endLine.cursorToX(endPos);
|
|
|
|
const double b = endBoundingRect.y() + endLine.y() + endLine.height();
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
result.append(QRectF(x / pageSize.width(), y / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height()));
|
|
|
|
} else {
|
|
|
|
const double x = startBoundingRect.x() + startLine.cursorToX(startPos);
|
|
|
|
const double y = startBoundingRect.y() + startLine.y();
|
|
|
|
const double r = endBoundingRect.x() + endLine.cursorToX(endPos);
|
|
|
|
const double b = endBoundingRect.y() + endLine.y() + endLine.height();
|
2007-05-12 08:17:02 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
result.append(QRectF(x / pageSize.width(), y / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height()));
|
|
|
|
}
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
return result;
|
|
|
|
}
|
2019-01-28 21:08:24 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
static void calculatePositions(QTextDocument *document, int page, int &start, int &end)
|
|
|
|
{
|
|
|
|
const QAbstractTextDocumentLayout *layout = document->documentLayout();
|
|
|
|
const QSizeF pageSize = document->pageSize();
|
|
|
|
const double margin = document->rootFrame()->frameFormat().margin();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Take the upper left and lower left corner including the margin
|
|
|
|
*/
|
|
|
|
start = layout->hitTest(QPointF(margin, (page * pageSize.height()) + margin), Qt::FuzzyHit);
|
|
|
|
end = layout->hitTest(QPointF(margin, ((page + 1) * pageSize.height()) - margin), Qt::FuzzyHit);
|
|
|
|
}
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
static Okular::DocumentViewport calculateViewport(QTextDocument *document, const QTextBlock &block)
|
|
|
|
{
|
|
|
|
const QSizeF pageSize = document->pageSize();
|
|
|
|
const QRectF rect = document->documentLayout()->blockBoundingRect(block);
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2022-08-25 21:38:42 +00:00
|
|
|
int page = qRound(rect.y()) / qRound(pageSize.height());
|
|
|
|
int offset = qRound(rect.y()) % qRound(pageSize.height());
|
|
|
|
if (rect.y() + rect.height() > pageSize.height()) {
|
|
|
|
page = page + 1;
|
|
|
|
offset = 0;
|
|
|
|
}
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
Okular::DocumentViewport viewport(page);
|
|
|
|
viewport.rePos.normalizedX = (double)rect.x() / (double)pageSize.width();
|
|
|
|
viewport.rePos.normalizedY = (double)offset / (double)pageSize.height();
|
|
|
|
viewport.rePos.enabled = true;
|
|
|
|
viewport.rePos.pos = Okular::DocumentViewport::Center;
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
return viewport;
|
|
|
|
}
|
2009-01-10 11:48:04 +00:00
|
|
|
}
|
2007-01-13 23:15:28 +00:00
|
|
|
|
2007-12-08 14:45:32 +00:00
|
|
|
class TextDocumentConverterPrivate
|
2007-01-13 23:15:28 +00:00
|
|
|
{
|
2020-07-10 22:15:05 +00:00
|
|
|
public:
|
|
|
|
TextDocumentConverterPrivate()
|
|
|
|
: mParent(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TextDocumentGeneratorPrivate *mParent;
|
|
|
|
QTextDocument *mDocument;
|
2007-01-13 23:15:28 +00:00
|
|
|
};
|
|
|
|
|
2007-09-14 13:31:55 +00:00
|
|
|
class TextDocumentGeneratorPrivate : public GeneratorPrivate
|
2007-01-13 23:15:28 +00:00
|
|
|
{
|
|
|
|
friend class TextDocumentConverter;
|
|
|
|
|
2020-07-10 22:15:05 +00:00
|
|
|
public:
|
|
|
|
explicit TextDocumentGeneratorPrivate(TextDocumentConverter *converter)
|
|
|
|
: mConverter(converter)
|
|
|
|
, mDocument(nullptr)
|
|
|
|
, mGeneralSettings(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~TextDocumentGeneratorPrivate() override
|
|
|
|
{
|
|
|
|
delete mConverter;
|
|
|
|
delete mDocument;
|
|
|
|
}
|
|
|
|
|
|
|
|
void initializeGenerator();
|
|
|
|
|
|
|
|
struct LinkInfo {
|
|
|
|
int page;
|
|
|
|
QRectF boundingRect;
|
|
|
|
Action *link;
|
|
|
|
bool ownsLink;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct AnnotationInfo {
|
|
|
|
int page;
|
|
|
|
QRectF boundingRect;
|
|
|
|
Annotation *annotation;
|
|
|
|
};
|
|
|
|
|
|
|
|
Q_DECLARE_PUBLIC(TextDocumentGenerator)
|
|
|
|
|
|
|
|
/* reimp */ QVariant metaData(const QString &key, const QVariant &option) const override;
|
|
|
|
/* reimp */ QImage image(PixmapRequest *) override;
|
|
|
|
|
|
|
|
void calculateBoundingRect(int startPosition, int endPosition, QRectF &rect, int &page) const;
|
|
|
|
void calculatePositions(int page, int &start, int &end) const;
|
|
|
|
Okular::TextPage *createTextPage(int) const;
|
|
|
|
|
|
|
|
void addAction(Action *action, int cursorBegin, int cursorEnd);
|
|
|
|
void addAnnotation(Annotation *annotation, int cursorBegin, int cursorEnd);
|
|
|
|
void addTitle(int level, const QString &title, const QTextBlock &block);
|
|
|
|
void addMetaData(const QString &key, const QString &value, const QString &title);
|
|
|
|
void addMetaData(DocumentInfo::Key, const QString &value);
|
|
|
|
|
|
|
|
QList<LinkInfo> generateLinkInfos() const;
|
|
|
|
QList<AnnotationInfo> generateAnnotationInfos() const;
|
|
|
|
void generateTitleInfos();
|
|
|
|
|
|
|
|
TextDocumentConverter *mConverter;
|
|
|
|
|
|
|
|
QTextDocument *mDocument;
|
|
|
|
Okular::DocumentInfo mDocumentInfo;
|
|
|
|
Okular::DocumentSynopsis mDocumentSynopsis;
|
|
|
|
|
|
|
|
struct TitlePosition {
|
|
|
|
int level;
|
|
|
|
QString title;
|
|
|
|
QTextBlock block;
|
|
|
|
};
|
|
|
|
QList<TitlePosition> mTitlePositions;
|
|
|
|
|
|
|
|
struct LinkPosition {
|
|
|
|
int startPosition;
|
|
|
|
int endPosition;
|
|
|
|
Action *link;
|
|
|
|
};
|
|
|
|
QList<LinkPosition> mLinkPositions;
|
|
|
|
|
|
|
|
struct AnnotationPosition {
|
|
|
|
int startPosition;
|
|
|
|
int endPosition;
|
|
|
|
Annotation *annotation;
|
|
|
|
};
|
|
|
|
QList<AnnotationPosition> mAnnotationPositions;
|
|
|
|
|
|
|
|
TextDocumentSettings *mGeneralSettings;
|
|
|
|
|
|
|
|
QFont mFont;
|
2007-01-13 23:15:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|