294QPdfView::QPdfView(QWidget *parent)
295 : QAbstractScrollArea(parent)
296 , d_ptr(new QPdfViewPrivate(this))
302 connect(d->m_pageNavigator, &QPdfPageNavigator::currentPageChanged, this,
303 [d](int page){ d->currentPageChanged(page); });
305 connect(d->m_pageRenderer, &QPdfPageRenderer::pageRendered, this,
306 [d](int pageNumber, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions, quint64 requestId) {
307 d->pageRendered(pageNumber, imageSize, image, requestId); });
309 verticalScrollBar()->setSingleStep(20);
310 horizontalScrollBar()->setSingleStep(20);
312 setMouseTracking(true);
313 d->calculateViewport();
328void QPdfView::setDocument(QPdfDocument *document)
332 if (d->m_document == document)
336 disconnect(d->m_documentStatusChangedConnection);
338 d->m_document = document;
339 emit documentChanged(d->m_document);
342 d->m_documentStatusChangedConnection =
343 connect(d->m_document.data(), &QPdfDocument::statusChanged, this,
344 [d](){ d->documentStatusChanged(); });
346 d->m_pageRenderer->setDocument(d->m_document);
347 d->m_linkModel.setDocument(d->m_document);
349 d->documentStatusChanged();
367void QPdfView::setSearchModel(QPdfSearchModel *searchModel)
370 if (d->m_searchModel == searchModel)
373 if (d->m_searchModel)
374 d->m_searchModel->disconnect(this);
376 d->m_searchModel = searchModel;
377 emit searchModelChanged(searchModel);
380 connect(searchModel, &QPdfSearchModel::dataChanged, this,
381 [this](const QModelIndex &, const QModelIndex &, const QList<int> &) { update(); });
383 setCurrentSearchResultIndex(-1);
582void QPdfView::paintEvent(QPaintEvent *event)
586 QPainter painter(viewport());
587 painter.fillRect(event->rect(), palette().brush(QPalette::Dark));
588 painter.translate(-d->m_viewport.x(), -d->m_viewport.y());
590 for (auto it = d->m_documentLayout.pageGeometryAndScale.cbegin();
591 it != d->m_documentLayout.pageGeometryAndScale.cend(); ++it) {
592 const QRect pageGeometry = it.value().first;
593 if (pageGeometry.intersects(d->m_viewport)) { // page needs to be painted
594 painter.fillRect(pageGeometry, Qt::white);
596 const int page = it.key();
597 const auto pageIt = d->m_pageCache.constFind(page);
598 if (pageIt != d->m_pageCache.cend()) {
599 const QImage &img = pageIt.value();
600 painter.drawImage(pageGeometry, img);
602 d->m_pageRenderer->requestPage(page, pageGeometry.size() * devicePixelRatioF());
605 const QTransform scaleTransform = d->screenScaleTransform(page);
607 const QString fmt = u"page %1 @ %2, %3"_s;
608 d->m_linkModel.setPage(page);
609 const int linkCount = d->m_linkModel.rowCount({});
610 for (int i = 0; i < linkCount; ++i) {
611 const QRectF linkBounds = scaleTransform.mapRect(
612 d->m_linkModel.data(d->m_linkModel.index(i),
613 int(QPdfLinkModel::Role::Rect)).toRectF())
614 .translated(pageGeometry.topLeft());
615 painter.setPen(Qt::blue);
616 painter.drawRect(linkBounds);
617 painter.setPen(Qt::red);
618 const QPoint loc = d->m_linkModel.data(d->m_linkModel.index(i),
619 int(QPdfLinkModel::Role::Location)).toPoint();
620 // TODO maybe draw destination URL if that's what it is
621 painter.drawText(linkBounds.bottomLeft() + QPoint(2, -2),
622 fmt.arg(d->m_linkModel.data(d->m_linkModel.index(i),
623 int(QPdfLinkModel::Role::Page)).toInt())
624 .arg(loc.x()).arg(loc.y()));
627 if (d->m_searchModel) {
628 for (const QPdfLink &result : d->m_searchModel->resultsOnPage(page)) {
629 for (const QRectF &rect : result.rectangles())
630 painter.fillRect(scaleTransform.mapRect(rect).translated(pageGeometry.topLeft()), SearchResultHighlight);
633 if (d->m_currentSearchResultIndex >= 0 && d->m_currentSearchResultIndex < d->m_searchModel->rowCount({})) {
634 const QPdfLink &cur = d->m_searchModel->resultAtIndex(d->m_currentSearchResultIndex);
635 if (cur.page() == page) {
636 painter.setPen({CurrentSearchResultHighlight, CurrentSearchResultWidth});
637 for (const auto &rect : cur.rectangles())
638 painter.drawRect(scaleTransform.mapRect(rect).translated(pageGeometry.topLeft()));
670void QPdfView::mouseMoveEvent(QMouseEvent *event)
673 for (auto it = d->m_documentLayout.pageGeometryAndScale.cbegin();
674 it != d->m_documentLayout.pageGeometryAndScale.cend(); ++it) {
675 const int page = it.key();
676 const QTransform screenInvTransform = d->screenScaleTransform(page).inverted();
677 const QRect pageGeometry = it.value().first;
678 if (pageGeometry.contains(event->position().toPoint())) {
679 const QPointF posInPoints = screenInvTransform.map(event->position() - pageGeometry.topLeft());
680 d->m_linkModel.setPage(page);
681 auto dest = d->m_linkModel.linkAt(posInPoints);
682 setCursor(dest.isValid() ? Qt::PointingHandCursor : Qt::ArrowCursor);
684 qCDebug(qLcWLink) << event->position() << ":" << posInPoints << "pt ->" << dest;
689void QPdfView::mouseReleaseEvent(QMouseEvent *event)
692 for (auto it = d->m_documentLayout.pageGeometryAndScale.cbegin();
693 it != d->m_documentLayout.pageGeometryAndScale.cend(); ++it) {
694 const int page = it.key();
695 const QTransform screenInvTransform = d->screenScaleTransform(page).inverted();
696 const QRect pageGeometry = it.value().first;
697 if (pageGeometry.contains(event->position().toPoint())) {
698 const QPointF posInPoints = screenInvTransform.map(event->position() - pageGeometry.topLeft());
699 d->m_linkModel.setPage(page);
700 auto dest = d->m_linkModel.linkAt(posInPoints);
701 if (dest.isValid()) {
702 qCDebug(qLcWLink) << event << ": jumping to" << dest;
703 d->m_pageNavigator->jump(dest.page(), dest.location(), dest.zoom());
704 // TODO scroll and zoom to where the link tells us to