Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpdfview.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qpdfview.h"
6#include "qpdfview_p.h"
8#include <QtPdf/private/qtpdfglobal_p.h>
9
10#include <QGuiApplication>
11#include <QLoggingCategory>
12#include <QPainter>
13#include <QPaintEvent>
14#include <QPdfDocument>
15#include <QPdfPageNavigator>
16#include <QPdfSearchModel>
17#include <QScreen>
18#include <QScrollBar>
19
21
22Q_PDF_LOGGING_CATEGORY(qLcWLink, "qt.pdf.widgets.links")
23//#define DEBUG_LINKS
24
25static const QColor SearchResultHighlight("#80B0C4DE");
27static const int CurrentSearchResultWidth(2);
28
29QPdfViewPrivate::QPdfViewPrivate(QPdfView *q)
30 : q_ptr(q)
31 , m_document(nullptr)
32 , m_pageNavigator(nullptr)
33 , m_pageRenderer(nullptr)
34 , m_pageMode(QPdfView::PageMode::SinglePage)
35 , m_zoomMode(QPdfView::ZoomMode::Custom)
36 , m_zoomFactor(1.0)
37 , m_pageSpacing(3)
38 , m_documentMargins(6, 6, 6, 6)
41 , m_screenResolution(QGuiApplication::primaryScreen()->logicalDotsPerInch() / 72.0)
42{
43}
44
46{
47 Q_Q(QPdfView);
48
49 m_pageNavigator = new QPdfPageNavigator(q);
50 m_pageRenderer = new QPdfPageRenderer(q);
51 m_pageRenderer->setRenderMode(QPdfPageRenderer::RenderMode::MultiThreaded);
52}
53
54void QPdfViewPrivate::documentStatusChanged(QPdfDocument::Status status)
55{
56 if (status == QPdfDocument::Status::Ready)
59}
60
61void QPdfViewPrivate::currentPageChanged(int currentPage)
62{
63 Q_Q(QPdfView);
64
66 return;
67
68 q->verticalScrollBar()->setValue(yPositionForPage(currentPage));
69
70 if (m_pageMode == QPdfView::PageMode::SinglePage)
72}
73
74void QPdfViewPrivate::currentZoomChanged(qreal currentZoom)
75{
76 Q_Q(QPdfView);
77 q->setZoomFactor(currentZoom);
78}
79
81{
82 Q_Q(QPdfView);
83
84 const int x = q->horizontalScrollBar()->value();
85 const int y = q->verticalScrollBar()->value();
86 const int width = q->viewport()->width();
87 const int height = q->viewport()->height();
88
89 setViewport(QRect(x, y, width, height));
90}
91
92void QPdfViewPrivate::setViewport(QRect viewport)
93{
94 if (m_viewport == viewport)
95 return;
96
97 const QSize oldSize = m_viewport.size();
98
99 m_viewport = viewport;
100
101 if (oldSize != m_viewport.size()) {
103
104 if (m_zoomMode != QPdfView::ZoomMode::Custom) {
106 }
107 }
108
109 if (m_pageMode == QPdfView::PageMode::MultiPage) {
110 // An imaginary, 2px height line at the upper half of the viewport, which is used to
111 // determine which page is currently located there -> we propagate that as 'current' page
112 // to the QPdfPageNavigator object
113 const QRect currentPageLine(m_viewport.x(), m_viewport.y() + m_viewport.height() * 0.4, m_viewport.width(), 2);
114
115 int currentPage = 0;
116 for (auto it = m_documentLayout.pageGeometryAndScale.cbegin();
117 it != m_documentLayout.pageGeometryAndScale.cend(); ++it) {
118 const QRect pageGeometry = it.value().first;
119 if (pageGeometry.intersects(currentPageLine)) {
120 currentPage = it.key();
121 break;
122 }
123 }
124
125 if (currentPage != m_pageNavigator->currentPage()) {
127 // ΤODO give location on the page
128 m_pageNavigator->jump(currentPage, {}, m_zoomFactor);
129 m_blockPageScrolling = false;
130 }
131 }
132}
133
135{
136 Q_Q(QPdfView);
137
138 const QSize p = q->viewport()->size();
139 const QSize v = m_documentLayout.documentSize;
140
141 q->horizontalScrollBar()->setRange(0, v.width() - p.width());
142 q->horizontalScrollBar()->setPageStep(p.width());
143 q->verticalScrollBar()->setRange(0, v.height() - p.height());
144 q->verticalScrollBar()->setPageStep(p.height());
145}
146
147/*! \internal
148 Scroll lazily (the minimal distance) such that \a link's destination
149 becomes completely visible within the viewport.
150*/
151void QPdfViewPrivate::scrollTo(const QPdfLink &link)
152{
153 Q_Q(QPdfView);
154
155 // If the link represents a search result, we will scroll to show its beginning.
156 // If the link has a destination location, scroll there instead.
157 const QRect subRect = link.location().isNull() ? link.rectangles().constFirst().toRect()
158 : QRect(link.location().toPoint(), QSize(1, 1));
159 const auto &pageGeometryAndScale = m_documentLayout.pageGeometryAndScale.value(link.page());
160 const QRect scaledSubRect(subRect.topLeft() * pageGeometryAndScale.second,
161 subRect.size() * pageGeometryAndScale.second);
162 const QRect oldViewport = m_viewport;
163
164 // If the region we want to show is already fully visible in the m_viewport, there's nothing to do.
165 if (m_viewport.contains(scaledSubRect))
166 return;
167
168 // Calculate new scrollbar positions that move the minimum amount
169 // to get scaledSubRect to be fully visible, if possible.
170 int sx = -1;
171 int sy = -1;
172 if (scaledSubRect.left() < m_viewport.left())
173 sx = scaledSubRect.left();
174 else if (scaledSubRect.right() > m_viewport.right())
175 sx = scaledSubRect.right();
176 if (scaledSubRect.top() < m_viewport.top())
177 sy = scaledSubRect.top();
178 else if (scaledSubRect.bottom() > m_viewport.bottom())
179 sy = scaledSubRect.bottom();
180
181 // In multi-page mode, assume that the view is already aligned
182 // at the top-left corner of the page (because mouseReleaseEvent()
183 // called QPdfPageNavigator::jump() first), and scroll down from there.
184 if (m_pageMode == QPdfView::PageMode::MultiPage) {
185 sx = m_viewport.x() + (sx >= 0 ? sx : 0);
186 sy = m_viewport.y() + (sy >= 0 ? sy : 0);
187 }
188
189 if (sx >= 0)
190 q->horizontalScrollBar()->setValue(sx);
191 if (sy >= 0)
192 q->verticalScrollBar()->setValue(sy);
193
194 qCDebug(qLcWLink) << "scrolled to page" << link.page() << "@" << link.location()
195 << "scaled target region" << scaledSubRect << "scrollbars" << sx << sy
196 << "viewport" << oldViewport << "->" << m_viewport;
197}
198
199void QPdfViewPrivate::pageRendered(int pageNumber, QSize imageSize, const QImage &image, quint64 requestId)
200{
201 Q_Q(QPdfView);
202
203 Q_UNUSED(imageSize);
204 Q_UNUSED(requestId);
205
206 if (!m_cachedPagesLRU.contains(pageNumber)) {
207 if (m_cachedPagesLRU.size() > m_pageCacheLimit)
208 m_pageCache.remove(m_cachedPagesLRU.takeFirst());
209
210 m_cachedPagesLRU.append(pageNumber);
211 }
212
213 m_pageCache.insert(pageNumber, image);
214
215 q->viewport()->update();
216}
217
223
225{
226 Q_Q(QPdfView);
227
228 m_pageCache.clear();
229 q->viewport()->update();
230}
231
233{
234 // The DocumentLayout describes a virtual layout where all pages are positioned inside
235 // - For SinglePage mode, this is just an area as large as the current page surrounded
236 // by the m_documentMargins.
237 // - For MultiPage mode, this is the area that is covered by all pages which are placed
238 // below each other, with m_pageSpacing inbetween and surrounded by m_documentMargins
239
240 DocumentLayout documentLayout;
241
242 if (!m_document || m_document->status() != QPdfDocument::Status::Ready)
243 return documentLayout;
244
245 QHash<int, QPair<QRect, qreal>> pageGeometryAndScale;
246
247 const int pageCount = m_document->pageCount();
248
249 int totalWidth = 0;
250
251 const int startPage = (m_pageMode == QPdfView::PageMode::SinglePage ? m_pageNavigator->currentPage() : 0);
252 const int endPage = (m_pageMode == QPdfView::PageMode::SinglePage ? m_pageNavigator->currentPage() + 1 : pageCount);
253
254 // calculate page sizes
255 for (int page = startPage; page < endPage; ++page) {
256 QSize pageSize;
257 qreal pageScale = m_zoomFactor;
258 if (m_zoomMode == QPdfView::ZoomMode::Custom) {
259 pageSize = QSizeF(m_document->pagePointSize(page) * m_screenResolution * m_zoomFactor).toSize();
260 } else if (m_zoomMode == QPdfView::ZoomMode::FitToWidth) {
261 pageSize = QSizeF(m_document->pagePointSize(page) * m_screenResolution).toSize();
262 pageScale = (qreal(m_viewport.width() - m_documentMargins.left() - m_documentMargins.right()) /
263 qreal(pageSize.width()));
264 pageSize *= pageScale;
265 } else if (m_zoomMode == QPdfView::ZoomMode::FitInView) {
266 const QSize viewportSize(m_viewport.size() +
267 QSize(-m_documentMargins.left() - m_documentMargins.right(), -m_pageSpacing));
268
269 pageSize = QSizeF(m_document->pagePointSize(page) * m_screenResolution).toSize();
270 QSize scaledSize = pageSize.scaled(viewportSize, Qt::KeepAspectRatio);
271 // because of KeepAspectRatio, the ratio of widths should be the same as the ratio of heights
272 pageScale = qreal(scaledSize.width()) / qreal(pageSize.width());
273 pageSize = scaledSize;
274 }
275
276 totalWidth = qMax(totalWidth, pageSize.width());
277
278 pageGeometryAndScale[page] = {QRect(QPoint(0, 0), pageSize), pageScale};
279 }
280
281 totalWidth += m_documentMargins.left() + m_documentMargins.right();
282
283 int pageY = m_documentMargins.top();
284
285 // calculate page positions
286 for (int page = startPage; page < endPage; ++page) {
287 const QSize pageSize = pageGeometryAndScale[page].first.size();
288
289 // center horizontal inside the viewport
290 const int pageX = (qMax(totalWidth, m_viewport.width()) - pageSize.width()) / 2;
291
292 pageGeometryAndScale[page].first.moveTopLeft(QPoint(pageX, pageY));
293
294 pageY += pageSize.height() + m_pageSpacing;
295 }
296
297 pageY += m_documentMargins.bottom();
298
299 documentLayout.pageGeometryAndScale = pageGeometryAndScale;
300
301 // calculate overall document size
302 documentLayout.documentSize = QSize(totalWidth, pageY);
303
304 return documentLayout;
305}
306
308{
309 const auto it = m_documentLayout.pageGeometryAndScale.constFind(pageNumber);
310 if (it == m_documentLayout.pageGeometryAndScale.cend())
311 return 0.0;
312
313 return (*it).first.y();
314}
315
317{
318 qreal scale = m_screenResolution * m_zoomFactor;
319 switch (m_zoomMode) {
320 case QPdfView::ZoomMode::FitToWidth:
321 case QPdfView::ZoomMode::FitInView:
322 scale = m_screenResolution * m_documentLayout.pageGeometryAndScale[page].second;
323 break;
324 default:
325 break;
326 }
327
328 return QTransform::fromScale(scale, scale);
329}
330
332{
333 m_documentLayout = calculateDocumentLayout();
334
336}
337
338/*!
339 \class QPdfView
340 \inmodule QtPdf
341 \brief A PDF viewer widget.
342
343 QPdfView is a PDF viewer widget that offers a user experience similar to
344 many common PDF viewer applications, with two \l {pageMode}{modes}.
345 In the \c MultiPage mode, it supports flicking through the pages in the
346 entire document, with narrow gaps between the page images.
347 In the \c SinglePage mode, it shows one page at a time.
348*/
349
350/*!
351 Constructs a PDF viewer with parent widget \a parent.
352*/
353QPdfView::QPdfView(QWidget *parent)
354 : QAbstractScrollArea(parent)
355 , d_ptr(new QPdfViewPrivate(this))
356{
357 Q_D(QPdfView);
358
359 d->init();
360
361 connect(d->m_pageNavigator, &QPdfPageNavigator::currentPageChanged, this,
362 [d](int page){ d->currentPageChanged(page); });
363 connect(d->m_pageNavigator, &QPdfPageNavigator::currentZoomChanged,
364 this, &QPdfView::setZoomFactor);
365
366 connect(d->m_pageRenderer, &QPdfPageRenderer::pageRendered, this,
367 [d](int pageNumber, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions, quint64 requestId) {
368 d->pageRendered(pageNumber, imageSize, image, requestId); });
369
370 verticalScrollBar()->setSingleStep(20);
371 horizontalScrollBar()->setSingleStep(20);
372
373 setMouseTracking(true);
374 d->calculateViewport();
375}
376
377/*!
378 Destroys the PDF viewer.
379*/
380QPdfView::~QPdfView()
381{
382}
383
384/*!
385 \property QPdfView::document
386
387 This property holds the document to be viewed.
388*/
389void QPdfView::setDocument(QPdfDocument *document)
390{
391 Q_D(QPdfView);
392
393 if (d->m_document == document)
394 return;
395
396 if (d->m_document)
397 disconnect(d->m_documentStatusChangedConnection);
398
399 d->m_document = document;
400 emit documentChanged(d->m_document);
401
402 if (d->m_document)
403 d->m_documentStatusChangedConnection =
404 connect(d->m_document.data(), &QPdfDocument::statusChanged, this,
405 [d](QPdfDocument::Status s){ d->documentStatusChanged(s); });
406
407 d->m_pageRenderer->setDocument(d->m_document);
408 d->m_linkModel.setDocument(d->m_document);
409
410 d->documentStatusChanged(document->status());
411}
412
413QPdfDocument *QPdfView::document() const
414{
415 Q_D(const QPdfView);
416
417 return d->m_document;
418}
419
420/*!
421 \since 6.6
422 \property QPdfView::searchModel
423
424 If this property is set, QPdfView draws highlight rectangles over the
425 search results provided by \l QPdfSearchModel::resultsOnPage(). By default
426 it is \c nullptr.
427*/
428void QPdfView::setSearchModel(QPdfSearchModel *searchModel)
429{
430 Q_D(QPdfView);
431 if (d->m_searchModel == searchModel)
432 return;
433
434 if (d->m_searchModel)
435 d->m_searchModel->disconnect(this);
436
437 d->m_searchModel = searchModel;
438 emit searchModelChanged(searchModel);
439
440 if (searchModel) {
441 connect(searchModel, &QPdfSearchModel::dataChanged, this,
442 [this](const QModelIndex &, const QModelIndex &, const QList<int> &) { update(); });
443 }
444 setCurrentSearchResultIndex(-1);
445}
446
447QPdfSearchModel *QPdfView::searchModel() const
448{
449 Q_D(const QPdfView);
450 return d->m_searchModel;
451}
452
453/*!
454 \since 6.6
455 \property QPdfView::currentSearchResultIndex
456
457 If this property is set to a positive number, and \l searchModel is set,
458 QPdfView draws a frame around the search result provided by
459 \l QPdfSearchModel at the given index. For example, if QPdfSearchModel is
460 used as the model for a QListView, you can keep this property updated by
461 connecting QItemSelectionModel::currentChanged() from
462 QListView::selectionModel() to a function that will in turn call this function.
463
464 By default it is \c -1, so that no search results are framed.
465*/
466void QPdfView::setCurrentSearchResultIndex(int currentResult)
467{
468 Q_D(QPdfView);
469 if (d->m_currentSearchResultIndex == currentResult)
470 return;
471
472 d->m_currentSearchResultIndex = currentResult;
473 emit currentSearchResultIndexChanged(currentResult);
474 viewport()->update(); //update();
475}
476
477int QPdfView::currentSearchResultIndex() const
478{
479 Q_D(const QPdfView);
480 return d->m_currentSearchResultIndex;
481}
482
483/*!
484 This accessor returns the navigation stack that will handle back/forward navigation.
485*/
486QPdfPageNavigator *QPdfView::pageNavigator() const
487{
488 Q_D(const QPdfView);
489
490 return d->m_pageNavigator;
491}
492
493/*!
494 \enum QPdfView::PageMode
495
496 This enum describes the overall behavior of the PDF viewer:
497
498 \value SinglePage Show one page at a time.
499 \value MultiPage Allow scrolling through all pages in the document.
500*/
501
502/*!
503 \property QPdfView::pageMode
504
505 This property holds whether to show one page at a time, or all pages in the
506 document. The default is \c SinglePage.
507*/
508QPdfView::PageMode QPdfView::pageMode() const
509{
510 Q_D(const QPdfView);
511
512 return d->m_pageMode;
513}
514
515void QPdfView::setPageMode(PageMode mode)
516{
517 Q_D(QPdfView);
518
519 if (d->m_pageMode == mode)
520 return;
521
522 d->m_pageMode = mode;
523 d->invalidateDocumentLayout();
524
525 emit pageModeChanged(d->m_pageMode);
526}
527
528/*!
529 \enum QPdfView::ZoomMode
530
531 This enum describes the magnification behavior of the PDF viewer:
532
533 \value Custom Use \l zoomFactor only.
534 \value FitToWidth Automatically choose a zoom factor so that
535 the width of the page fits in the view.
536 \value FitInView Automatically choose a zoom factor so that
537 the entire page fits in the view.
538*/
539
540/*!
541 \property QPdfView::zoomMode
542
543 This property indicates whether to use a custom size for the page(s),
544 or zoom them to fit to the view. The default is \c CustomZoom.
545*/
546QPdfView::ZoomMode QPdfView::zoomMode() const
547{
548 Q_D(const QPdfView);
549
550 return d->m_zoomMode;
551}
552
553void QPdfView::setZoomMode(ZoomMode mode)
554{
555 Q_D(QPdfView);
556
557 if (d->m_zoomMode == mode)
558 return;
559
560 d->m_zoomMode = mode;
561 d->invalidateDocumentLayout();
562
563 emit zoomModeChanged(d->m_zoomMode);
564}
565
566/*!
567 \property QPdfView::zoomFactor
568
569 This property holds the ratio of pixels to points. The default is \c 1,
570 meaning one point (1/72 of an inch) equals 1 logical pixel.
571*/
572qreal QPdfView::zoomFactor() const
573{
574 Q_D(const QPdfView);
575
576 return d->m_zoomFactor;
577}
578
579void QPdfView::setZoomFactor(qreal factor)
580{
581 Q_D(QPdfView);
582
583 if (d->m_zoomFactor == factor)
584 return;
585
586 d->m_zoomFactor = factor;
587 d->invalidateDocumentLayout();
588
589 emit zoomFactorChanged(d->m_zoomFactor);
590}
591
592/*!
593 \property QPdfView::pageSpacing
594
595 This property holds the size of the padding between pages in the \l MultiPage
596 \l {pageMode}{mode}.
597*/
598int QPdfView::pageSpacing() const
599{
600 Q_D(const QPdfView);
601
602 return d->m_pageSpacing;
603}
604
605void QPdfView::setPageSpacing(int spacing)
606{
607 Q_D(QPdfView);
608
609 if (d->m_pageSpacing == spacing)
610 return;
611
612 d->m_pageSpacing = spacing;
613 d->invalidateDocumentLayout();
614
615 emit pageSpacingChanged(d->m_pageSpacing);
616}
617
618/*!
619 \property QPdfView::documentMargins
620
621 This property holds the margins around the page view.
622*/
623QMargins QPdfView::documentMargins() const
624{
625 Q_D(const QPdfView);
626
627 return d->m_documentMargins;
628}
629
630void QPdfView::setDocumentMargins(QMargins margins)
631{
632 Q_D(QPdfView);
633
634 if (d->m_documentMargins == margins)
635 return;
636
637 d->m_documentMargins = margins;
638 d->invalidateDocumentLayout();
639
640 emit documentMarginsChanged(d->m_documentMargins);
641}
642
643void QPdfView::paintEvent(QPaintEvent *event)
644{
645 Q_D(QPdfView);
646
647 QPainter painter(viewport());
648 painter.fillRect(event->rect(), palette().brush(QPalette::Dark));
649 painter.translate(-d->m_viewport.x(), -d->m_viewport.y());
650
651 for (auto it = d->m_documentLayout.pageGeometryAndScale.cbegin();
652 it != d->m_documentLayout.pageGeometryAndScale.cend(); ++it) {
653 const QRect pageGeometry = it.value().first;
654 if (pageGeometry.intersects(d->m_viewport)) { // page needs to be painted
655 painter.fillRect(pageGeometry, Qt::white);
656
657 const int page = it.key();
658 const auto pageIt = d->m_pageCache.constFind(page);
659 if (pageIt != d->m_pageCache.cend()) {
660 const QImage &img = pageIt.value();
661 painter.drawImage(pageGeometry, img);
662 } else {
663 d->m_pageRenderer->requestPage(page, pageGeometry.size() * devicePixelRatioF());
664 }
665
666 const QTransform scaleTransform = d->screenScaleTransform(page);
667#ifdef DEBUG_LINKS
668 const QString fmt = u"page %1 @ %2, %3"_s;
669 d->m_linkModel.setPage(page);
670 const int linkCount = d->m_linkModel.rowCount({});
671 for (int i = 0; i < linkCount; ++i) {
672 const QRectF linkBounds = scaleTransform.mapRect(
673 d->m_linkModel.data(d->m_linkModel.index(i),
674 int(QPdfLinkModel::Role::Rect)).toRectF())
675 .translated(pageGeometry.topLeft());
676 painter.setPen(Qt::blue);
677 painter.drawRect(linkBounds);
678 painter.setPen(Qt::red);
679 const QPoint loc = d->m_linkModel.data(d->m_linkModel.index(i),
680 int(QPdfLinkModel::Role::Location)).toPoint();
681 // TODO maybe draw destination URL if that's what it is
682 painter.drawText(linkBounds.bottomLeft() + QPoint(2, -2),
683 fmt.arg(d->m_linkModel.data(d->m_linkModel.index(i),
684 int(QPdfLinkModel::Role::Page)).toInt())
685 .arg(loc.x()).arg(loc.y()));
686 }
687#endif
688 if (d->m_searchModel) {
689 for (const QPdfLink &result : d->m_searchModel->resultsOnPage(page)) {
690 for (const QRectF &rect : result.rectangles())
691 painter.fillRect(scaleTransform.mapRect(rect).translated(pageGeometry.topLeft()), SearchResultHighlight);
692 }
693
694 if (d->m_currentSearchResultIndex >= 0 && d->m_currentSearchResultIndex < d->m_searchModel->rowCount({})) {
695 const QPdfLink &cur = d->m_searchModel->resultAtIndex(d->m_currentSearchResultIndex);
696 if (cur.page() == page) {
697 painter.setPen({CurrentSearchResultHighlight, CurrentSearchResultWidth});
698 for (const auto &rect : cur.rectangles())
699 painter.drawRect(scaleTransform.mapRect(rect).translated(pageGeometry.topLeft()));
700 }
701 }
702 }
703 }
704 }
705}
706
707void QPdfView::resizeEvent(QResizeEvent *event)
708{
709 Q_D(QPdfView);
710
711 QAbstractScrollArea::resizeEvent(event);
712
713 d->updateScrollBars();
714 d->calculateViewport();
715}
716
717void QPdfView::scrollContentsBy(int dx, int dy)
718{
719 Q_D(QPdfView);
720
721 QAbstractScrollArea::scrollContentsBy(dx, dy);
722
723 d->calculateViewport();
724}
725
726void QPdfView::mousePressEvent(QMouseEvent *event)
727{
728 Q_ASSERT(event->isAccepted());
729}
730
731void QPdfView::mouseMoveEvent(QMouseEvent *event)
732{
733 Q_D(QPdfView);
734 for (auto it = d->m_documentLayout.pageGeometryAndScale.cbegin();
735 it != d->m_documentLayout.pageGeometryAndScale.cend(); ++it) {
736 const int page = it.key();
737 const QTransform screenInvTransform = d->screenScaleTransform(page).inverted();
738 const QRect pageGeometry = it.value().first;
739 if (pageGeometry.contains(event->position().toPoint())) {
740 QPointF posInPoints = screenInvTransform.map(event->position() - pageGeometry.topLeft()
741 + d->m_viewport.topLeft());
742 d->m_linkModel.setPage(page);
743 auto dest = d->m_linkModel.linkAt(posInPoints);
744 setCursor(dest.isValid() ? Qt::PointingHandCursor : Qt::ArrowCursor);
745 if (dest.isValid())
746 qCDebug(qLcWLink) << event->position() << ":" << posInPoints << "pt ->" << dest;
747 }
748 }
749}
750
751void QPdfView::mouseReleaseEvent(QMouseEvent *event)
752{
753 Q_D(QPdfView);
754 for (auto it = d->m_documentLayout.pageGeometryAndScale.cbegin();
755 it != d->m_documentLayout.pageGeometryAndScale.cend(); ++it) {
756 const int page = it.key();
757 const QTransform screenInvTransform = d->screenScaleTransform(page).inverted();
758 const QRect pageGeometry = it.value().first;
759 if (pageGeometry.contains(event->position().toPoint())) {
760 QPointF posInPoints = screenInvTransform.map(event->position() - pageGeometry.topLeft()
761 + d->m_viewport.topLeft());
762 d->m_linkModel.setPage(page);
763 auto dest = d->m_linkModel.linkAt(posInPoints);
764 if (dest.isValid()) {
765 qCDebug(qLcWLink) << event << ": jumping to" << dest;
766 d->m_pageNavigator->jump(dest.page(), dest.location(), dest.zoom());
767 d->scrollTo(dest);
768 }
769 return;
770 }
771 }
772}
773
774QT_END_NAMESPACE
775
776#include "moc_qpdfview.cpp"
QTransform screenScaleTransform(int page) const
Definition qpdfview.cpp:316
void updateScrollBars()
Definition qpdfview.cpp:134
void setViewport(QRect viewport)
Definition qpdfview.cpp:92
QPdfPageRenderer * m_pageRenderer
Definition qpdfview_p.h:66
void updateDocumentLayout()
Definition qpdfview.cpp:331
QPdfPageNavigator * m_pageNavigator
Definition qpdfview_p.h:65
void documentStatusChanged(QPdfDocument::Status status)
Definition qpdfview.cpp:54
qreal yPositionForPage(int page) const
Definition qpdfview.cpp:307
void invalidatePageCache()
Definition qpdfview.cpp:224
void currentZoomChanged(qreal currentZoom)
Definition qpdfview.cpp:74
void invalidateDocumentLayout()
Definition qpdfview.cpp:218
void currentPageChanged(int currentPage)
Definition qpdfview.cpp:61
bool m_blockPageScrolling
Definition qpdfview_p.h:78
void calculateViewport()
Definition qpdfview.cpp:80
DocumentLayout calculateDocumentLayout() const
Definition qpdfview.cpp:232
void pageRendered(int pageNumber, QSize imageSize, const QImage &image, quint64 requestId)
Definition qpdfview.cpp:199
void scrollTo(const QPdfLink &link)
Definition qpdfview.cpp:151
Combined button and popup list for selecting options.
static const QColor CurrentSearchResultHighlight(Qt::cyan)
static const int CurrentSearchResultWidth(2)