/*************************************************************************** * Copyright (C) 2007 by Tobias Koenig * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #ifndef _OKULAR_TEXTDOCUMENTGENERATOR_P_H_ #define _OKULAR_TEXTDOCUMENTGENERATOR_P_H_ #include #include #include #include "action.h" #include "debug_p.h" #include "document.h" #include "generator_p.h" #include "textdocumentgenerator.h" 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()); } static QVector calculateBoundingRects(QTextDocument *document, int startPosition, int endPosition) { QVector 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())); } // 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(); 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(); result.append(QRectF(x / pageSize.width(), y / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height())); } return result; } 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); } static Okular::DocumentViewport calculateViewport(QTextDocument *document, const QTextBlock &block) { const QSizeF pageSize = document->pageSize(); const QRectF rect = document->documentLayout()->blockBoundingRect(block); const int page = qRound(rect.y()) / qRound(pageSize.height()); const int offset = qRound(rect.y()) % qRound(pageSize.height()); 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; return viewport; } } class TextDocumentConverterPrivate { public: TextDocumentConverterPrivate() : mParent(nullptr) { } TextDocumentGeneratorPrivate *mParent; QTextDocument *mDocument; }; class TextDocumentGeneratorPrivate : public GeneratorPrivate { friend class TextDocumentConverter; 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 generateLinkInfos() const; QList generateAnnotationInfos() const; void generateTitleInfos(); TextDocumentConverter *mConverter; QTextDocument *mDocument; Okular::DocumentInfo mDocumentInfo; Okular::DocumentSynopsis mDocumentSynopsis; struct TitlePosition { int level; QString title; QTextBlock block; }; QList mTitlePositions; struct LinkPosition { int startPosition; int endPosition; Action *link; }; QList mLinkPositions; struct AnnotationPosition { int startPosition; int endPosition; Annotation *annotation; }; QList mAnnotationPositions; TextDocumentSettings *mGeneralSettings; QFont mFont; }; } #endif