diff --git a/autotests/data/pdf_with_internal_links.pdf b/autotests/data/pdf_with_internal_links.pdf new file mode 100644 index 000000000..fdbe62634 Binary files /dev/null and b/autotests/data/pdf_with_internal_links.pdf differ diff --git a/autotests/data/pdf_with_internal_links.tex b/autotests/data/pdf_with_internal_links.tex new file mode 100644 index 000000000..17894890d --- /dev/null +++ b/autotests/data/pdf_with_internal_links.tex @@ -0,0 +1,9 @@ +\documentclass{article} +\usepackage{hyperref,blindtext} + +\begin{document} + +\tableofcontents +\blinddocument + +\end{document} diff --git a/autotests/parttest.cpp b/autotests/parttest.cpp index db621111a..2722a265a 100644 --- a/autotests/parttest.cpp +++ b/autotests/parttest.cpp @@ -107,6 +107,7 @@ private slots: void testMouseModeMenu(); void testFullScreenRequest(); void testZoomInFacingPages(); + void testLinkWithCrop(); private: void simulateMouseSelection(double startX, double startY, double endX, double endY, QWidget *target); @@ -2110,6 +2111,49 @@ void PartTest::testZoomWithCrop() QVERIFY(!Okular::Settings::trimMargins()); } +void PartTest::testLinkWithCrop() +{ + // We test that link targets are correct with cropping, related to bug 198427 + + QVariantList dummyArgs; + Okular::Part part(nullptr, nullptr, dummyArgs); + QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_internal_links.pdf"))); + + KActionMenu *cropMenu = part.m_pageView->findChild(QStringLiteral("view_trim_mode")); + KToggleAction *cropAction = cropMenu->menu()->findChild(QStringLiteral("view_trim_selection")); + + part.widget()->resize(600, 400); + part.widget()->show(); + QVERIFY(QTest::qWaitForWindowExposed(part.widget())); + + // wait for pixmap + QTRY_VERIFY(part.m_document->page(0)->hasPixmap(part.m_pageView)); + + // Activate "Trim Margins" + cropAction->trigger(); + + const int width = part.m_pageView->viewport()->width(); + const int height = part.m_pageView->viewport()->height(); + + const int mouseStartY = height * 0.2; + const int mouseEndY = height * 0.8; + const int mouseStartX = width * 0.2; + const int mouseEndX = width * 0.8; + + // Trim the page + simulateMouseSelection(mouseStartX, mouseStartY, mouseEndX, mouseEndY, part.m_pageView->viewport()); + + // Click a link + const QPoint click(width * 0.2, height * 0.2); + QTest::mouseMove(part.m_pageView->viewport(), click); + QTest::mouseClick(part.m_pageView->viewport(), Qt::LeftButton, Qt::NoModifier, click); + + QTRY_VERIFY2_WITH_TIMEOUT(qAbs(part.m_document->viewport().rePos.normalizedY - 0.167102333237) < 0.01, qPrintable(QString("We are at %1").arg(part.m_document->viewport().rePos.normalizedY)), 500); + + // Deactivate "Trim Margins" + cropAction->trigger(); +} + } // namespace Okular int main(int argc, char *argv[]) diff --git a/part/pageview.cpp b/part/pageview.cpp index bfd576780..88e28a46d 100644 --- a/part/pageview.cpp +++ b/part/pageview.cpp @@ -3590,13 +3590,19 @@ QPoint PageView::viewportToContentArea(const Okular::DocumentViewport &vp) const QPoint c {r.left(), r.top()}; if (vp.rePos.enabled) { + // Convert the coordinates of vp to normalized coordinates on the cropped page. + // This is a no-op if the page isn't cropped. + const Okular::NormalizedRect &crop = d->items[vp.pageNumber]->crop(); + const double normalized_on_crop_x = (vp.rePos.normalizedX - crop.left) / (crop.right - crop.left); + const double normalized_on_crop_y = (vp.rePos.normalizedY - crop.top) / (crop.bottom - crop.top); + if (vp.rePos.pos == Okular::DocumentViewport::Center) { - c.rx() += qRound(normClamp(vp.rePos.normalizedX, 0.5) * (double)r.width()); - c.ry() += qRound(normClamp(vp.rePos.normalizedY, 0.0) * (double)r.height()); + c.rx() += qRound(normClamp(normalized_on_crop_x, 0.5) * (double)r.width()); + c.ry() += qRound(normClamp(normalized_on_crop_y, 0.0) * (double)r.height()); } else { // TopLeft - c.rx() += qRound(normClamp(vp.rePos.normalizedX, 0.0) * (double)r.width() + viewport()->width() / 2.0); - c.ry() += qRound(normClamp(vp.rePos.normalizedY, 0.0) * (double)r.height() + viewport()->height() / 2.0); + c.rx() += qRound(normClamp(normalized_on_crop_x, 0.0) * (double)r.width() + viewport()->width() / 2.0); + c.ry() += qRound(normClamp(normalized_on_crop_y, 0.0) * (double)r.height() + viewport()->height() / 2.0); } } else { // exact repositioning disabled, align page top margin with viewport top border by default @@ -4552,8 +4558,12 @@ void PageView::slotRequestVisiblePixmaps(int newValue) nearPageNumber = i->pageNumber(); minDistance = distance; if (geometry.height() > 0 && geometry.width() > 0) { + // Compute normalized coordinates w.r.t. cropped page focusedX = (viewportCenterX - (double)geometry.left()) / (double)geometry.width(); focusedY = (viewportCenterY - (double)geometry.top()) / (double)geometry.height(); + // Convert to normalized coordinates w.r.t. full page (no-op if not cropped) + focusedX = i->crop().left + focusedX * i->crop().width(); + focusedY = i->crop().top + focusedY * i->crop().height(); } } }