/*************************************************************************** * 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 "document.h" #include "generator_p.h" #include "textdocumentgenerator.h" #include "debug_p.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 ) { } virtual ~TextDocumentGeneratorPrivate() { 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 &position ); 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