diff --git a/core/area.h b/core/area.h --- a/core/area.h +++ b/core/area.h @@ -518,6 +518,17 @@ NormalizedPoint m_point; }; +/** + * This class is an object rect that doesn't own the given pointer, i.e. won't delete it on destruction + * @since 1.7 + */ +class OKULARCORE_EXPORT NonOwningObjectRect : public ObjectRect +{ + public: + NonOwningObjectRect( double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object ); + ~NonOwningObjectRect(); +}; + /// @cond PRIVATE /** @internal */ /** @internal */ diff --git a/core/area.cpp b/core/area.cpp --- a/core/area.cpp +++ b/core/area.cpp @@ -487,3 +487,16 @@ { return distanceSqr( x, y, xScale, yScale ) < ( pow( 7.0 / xScale, 2 ) + pow( 7.0 / yScale, 2 ) ); } + +/** class NonOwningObjectRect **/ + +NonOwningObjectRect::NonOwningObjectRect( double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object ) + : ObjectRect( left, top, right, bottom, ellipse, type, object ) +{ +} + +NonOwningObjectRect::~NonOwningObjectRect() +{ + // Set m_object so that ~ObjectRect() doesn't delete it + m_object = nullptr; +} diff --git a/core/textdocumentgenerator.cpp b/core/textdocumentgenerator.cpp --- a/core/textdocumentgenerator.cpp +++ b/core/textdocumentgenerator.cpp @@ -172,14 +172,18 @@ for ( int i = 0; i < mLinkPositions.count(); ++i ) { const LinkPosition &linkPosition = mLinkPositions[ i ]; - LinkInfo info; - info.link = linkPosition.link; + const QVector rects = TextDocumentUtils::calculateBoundingRects( mDocument, linkPosition.startPosition, linkPosition.endPosition ); - TextDocumentUtils::calculateBoundingRect( mDocument, linkPosition.startPosition, linkPosition.endPosition, - info.boundingRect, info.page ); + for ( int i = 0; i < rects.count(); ++i) { + const QRectF &rect = rects[ i ]; - if ( info.page >= 0 ) + LinkInfo info; + info.link = linkPosition.link; + info.ownsLink = i == 0; + info.page = std::floor( rect.y() ); + info.boundingRect = QRectF( rect.x(), rect.y() - info.page, rect.width(), rect.height() ); result.append( info ); + } } return result; @@ -332,8 +336,13 @@ continue; const QRectF rect = info.boundingRect; - objects[ info.page ].append( new Okular::ObjectRect( rect.left(), rect.top(), rect.right(), rect.bottom(), false, - Okular::ObjectRect::Action, info.link ) ); + if ( info.ownsLink ) { + objects[ info.page ].append( new Okular::ObjectRect( rect.left(), rect.top(), rect.right(), rect.bottom(), false, + Okular::ObjectRect::Action, info.link ) ); + } else { + objects[ info.page ].append( new Okular::NonOwningObjectRect( rect.left(), rect.top(), rect.right(), rect.bottom(), false, + Okular::ObjectRect::Action, info.link ) ); + } } QVector< QLinkedList > annots( d->mDocument->pageCount() ); diff --git a/core/textdocumentgenerator_p.h b/core/textdocumentgenerator_p.h --- a/core/textdocumentgenerator_p.h +++ b/core/textdocumentgenerator_p.h @@ -67,6 +67,66 @@ (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(); @@ -133,6 +193,7 @@ int page; QRectF boundingRect; Action *link; + bool ownsLink; }; struct AnnotationInfo