File indexing completed on 2024-04-28 04:32:46
0001 /* 0002 SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef _OKULAR_TEXTDOCUMENTGENERATOR_P_H_ 0008 #define _OKULAR_TEXTDOCUMENTGENERATOR_P_H_ 0009 0010 #include <QAbstractTextDocumentLayout> 0011 #include <QTextBlock> 0012 #include <QTextDocument> 0013 0014 #include "action.h" 0015 #include "debug_p.h" 0016 #include "document.h" 0017 #include "generator_p.h" 0018 #include "textdocumentgenerator.h" 0019 0020 namespace Okular 0021 { 0022 namespace TextDocumentUtils 0023 { 0024 static void calculateBoundingRect(QTextDocument *document, int startPosition, int endPosition, QRectF &rect, int &page) 0025 { 0026 const QSizeF pageSize = document->pageSize(); 0027 0028 const QTextBlock startBlock = document->findBlock(startPosition); 0029 const QRectF startBoundingRect = document->documentLayout()->blockBoundingRect(startBlock); 0030 0031 const QTextBlock endBlock = document->findBlock(endPosition); 0032 const QRectF endBoundingRect = document->documentLayout()->blockBoundingRect(endBlock); 0033 0034 const QTextLayout *startLayout = startBlock.layout(); 0035 const QTextLayout *endLayout = endBlock.layout(); 0036 if (!startLayout || !endLayout) { 0037 qCWarning(OkularCoreDebug) << "Start or end layout not found" << startLayout << endLayout; 0038 page = -1; 0039 return; 0040 } 0041 0042 const int startPos = startPosition - startBlock.position(); 0043 const int endPos = endPosition - endBlock.position(); 0044 const QTextLine startLine = startLayout->lineForTextPosition(startPos); 0045 const QTextLine endLine = endLayout->lineForTextPosition(endPos); 0046 0047 const double x = startBoundingRect.x() + startLine.cursorToX(startPos); 0048 const double y = startBoundingRect.y() + startLine.y(); 0049 const double r = endBoundingRect.x() + endLine.cursorToX(endPos); 0050 const double b = endBoundingRect.y() + endLine.y() + endLine.height(); 0051 0052 const int offset = qRound(y) % qRound(pageSize.height()); 0053 0054 if (x > r) { // line break, so return a pseudo character on the start line 0055 rect = QRectF(x / pageSize.width(), offset / pageSize.height(), 3 / pageSize.width(), startLine.height() / pageSize.height()); 0056 page = -1; 0057 return; 0058 } 0059 0060 page = qRound(y) / qRound(pageSize.height()); 0061 rect = QRectF(x / pageSize.width(), offset / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height()); 0062 } 0063 0064 static QVector<QRectF> calculateBoundingRects(QTextDocument *document, int startPosition, int endPosition) 0065 { 0066 QVector<QRectF> result; 0067 0068 const QSizeF pageSize = document->pageSize(); 0069 0070 const QTextBlock startBlock = document->findBlock(startPosition); 0071 const QRectF startBoundingRect = document->documentLayout()->blockBoundingRect(startBlock); 0072 0073 const QTextBlock endBlock = document->findBlock(endPosition); 0074 const QRectF endBoundingRect = document->documentLayout()->blockBoundingRect(endBlock); 0075 0076 const QTextLayout *startLayout = startBlock.layout(); 0077 const QTextLayout *endLayout = endBlock.layout(); 0078 if (!startLayout || !endLayout) { 0079 qCWarning(OkularCoreDebug) << "Start or end layout not found" << startLayout << endLayout; 0080 return {}; 0081 } 0082 0083 const int startPos = startPosition - startBlock.position(); 0084 const int endPos = endPosition - endBlock.position(); 0085 const QTextLine startLine = startLayout->lineForTextPosition(startPos); 0086 const QTextLine endLine = endLayout->lineForTextPosition(endPos); 0087 0088 // This only works if both start and end layout are the same 0089 if (startLayout == endLayout) { 0090 Q_ASSERT(startBoundingRect == endBoundingRect); 0091 for (int i = startLine.lineNumber(); i < endLine.lineNumber(); ++i) { 0092 const QTextLine line = startLayout->lineAt(i); 0093 // 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 0094 const double x = endBoundingRect.x() + line.cursorToX(startPos); 0095 const double y = endBoundingRect.y() + line.y(); 0096 const double r = endBoundingRect.x() + line.cursorToX(endPos); 0097 const double b = endBoundingRect.y() + line.y() + endLine.height(); 0098 0099 result.append(QRectF(x / pageSize.width(), y / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height())); 0100 } 0101 0102 // The last line 0103 const double x = endBoundingRect.x() + endLine.cursorToX(startPos); 0104 const double y = endBoundingRect.y() + endLine.y(); 0105 const double r = endBoundingRect.x() + endLine.cursorToX(endPos); 0106 const double b = endBoundingRect.y() + endLine.y() + endLine.height(); 0107 0108 result.append(QRectF(x / pageSize.width(), y / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height())); 0109 } else { 0110 const double x = startBoundingRect.x() + startLine.cursorToX(startPos); 0111 const double y = startBoundingRect.y() + startLine.y(); 0112 const double r = endBoundingRect.x() + endLine.cursorToX(endPos); 0113 const double b = endBoundingRect.y() + endLine.y() + endLine.height(); 0114 0115 result.append(QRectF(x / pageSize.width(), y / pageSize.height(), (r - x) / pageSize.width(), (b - y) / pageSize.height())); 0116 } 0117 0118 return result; 0119 } 0120 0121 static void calculatePositions(QTextDocument *document, int page, int &start, int &end) 0122 { 0123 const QAbstractTextDocumentLayout *layout = document->documentLayout(); 0124 const QSizeF pageSize = document->pageSize(); 0125 const double margin = document->rootFrame()->frameFormat().margin(); 0126 0127 /** 0128 * Take the upper left and lower left corner including the margin 0129 */ 0130 start = layout->hitTest(QPointF(margin, (page * pageSize.height()) + margin), Qt::FuzzyHit); 0131 end = layout->hitTest(QPointF(margin, ((page + 1) * pageSize.height()) - margin), Qt::FuzzyHit); 0132 } 0133 0134 static Okular::DocumentViewport calculateViewport(QTextDocument *document, const QTextBlock &block) 0135 { 0136 const QSizeF pageSize = document->pageSize(); 0137 const QRectF rect = document->documentLayout()->blockBoundingRect(block); 0138 0139 int page = qRound(rect.y()) / qRound(pageSize.height()); 0140 int offset = qRound(rect.y()) % qRound(pageSize.height()); 0141 if (rect.y() + rect.height() > pageSize.height()) { 0142 page = page + 1; 0143 offset = 0; 0144 } 0145 0146 Okular::DocumentViewport viewport(page); 0147 viewport.rePos.normalizedX = (double)rect.x() / (double)pageSize.width(); 0148 viewport.rePos.normalizedY = (double)offset / (double)pageSize.height(); 0149 viewport.rePos.enabled = true; 0150 viewport.rePos.pos = Okular::DocumentViewport::Center; 0151 0152 return viewport; 0153 } 0154 } 0155 0156 class TextDocumentConverterPrivate 0157 { 0158 public: 0159 TextDocumentConverterPrivate() 0160 : mParent(nullptr) 0161 { 0162 } 0163 0164 TextDocumentGeneratorPrivate *mParent; 0165 QTextDocument *mDocument; 0166 }; 0167 0168 class TextDocumentGeneratorPrivate : public GeneratorPrivate 0169 { 0170 friend class TextDocumentConverter; 0171 0172 public: 0173 explicit TextDocumentGeneratorPrivate(TextDocumentConverter *converter) 0174 : mConverter(converter) 0175 , mDocument(nullptr) 0176 , mGeneralSettings(nullptr) 0177 { 0178 } 0179 0180 ~TextDocumentGeneratorPrivate() override 0181 { 0182 delete mConverter; 0183 delete mDocument; 0184 } 0185 0186 void initializeGenerator(); 0187 0188 struct LinkInfo { 0189 int page; 0190 QRectF boundingRect; 0191 Action *link; 0192 bool ownsLink; 0193 }; 0194 0195 struct AnnotationInfo { 0196 int page; 0197 QRectF boundingRect; 0198 Annotation *annotation; 0199 }; 0200 0201 Q_DECLARE_PUBLIC(TextDocumentGenerator) 0202 0203 /* reimp */ QVariant metaData(const QString &key, const QVariant &option) const override; 0204 /* reimp */ QImage image(PixmapRequest *) override; 0205 0206 void calculateBoundingRect(int startPosition, int endPosition, QRectF &rect, int &page) const; 0207 void calculatePositions(int page, int &start, int &end) const; 0208 Okular::TextPage *createTextPage(int) const; 0209 0210 void addAction(Action *action, int cursorBegin, int cursorEnd); 0211 void addAnnotation(Annotation *annotation, int cursorBegin, int cursorEnd); 0212 void addTitle(int level, const QString &title, const QTextBlock &block); 0213 void addMetaData(const QString &key, const QString &value, const QString &title); 0214 void addMetaData(DocumentInfo::Key, const QString &value); 0215 0216 QList<LinkInfo> generateLinkInfos() const; 0217 QList<AnnotationInfo> generateAnnotationInfos() const; 0218 void generateTitleInfos(); 0219 0220 TextDocumentConverter *mConverter; 0221 0222 QTextDocument *mDocument; 0223 Okular::DocumentInfo mDocumentInfo; 0224 Okular::DocumentSynopsis mDocumentSynopsis; 0225 0226 struct TitlePosition { 0227 int level; 0228 QString title; 0229 QTextBlock block; 0230 }; 0231 QList<TitlePosition> mTitlePositions; 0232 0233 struct LinkPosition { 0234 int startPosition; 0235 int endPosition; 0236 Action *link; 0237 }; 0238 QList<LinkPosition> mLinkPositions; 0239 0240 struct AnnotationPosition { 0241 int startPosition; 0242 int endPosition; 0243 Annotation *annotation; 0244 }; 0245 QList<AnnotationPosition> mAnnotationPositions; 0246 0247 TextDocumentSettings *mGeneralSettings; 0248 0249 QFont mFont; 0250 }; 0251 0252 } 0253 0254 #endif