summaryrefslogtreecommitdiffstats
path: root/src/pdfquick
diff options
context:
space:
mode:
authorMichal Klocek <michal.klocek@qt.io>2021-07-09 19:24:15 +0200
committerMichal Klocek <michal.klocek@qt.io>2021-08-17 17:38:55 +0200
commit79d04aa9e3e9aa84d7378260519f9e9a6759dc41 (patch)
tree3f4b1ae9f514ffc2a3d730cb8d7db9fb72ee1e7d /src/pdfquick
parent58b787cbad581914367aea4604993cce30a65d06 (diff)
Add QtPdf to cmake build
Port QtPdf to Qt6: * QtPdf,QtPdfWidgets,QtPdfQuick libs * QtPdfQuickPlugin, QtPdfPlugin (imageformat) plugins * widget and quick examples * qtpdf tests To fit gn cmake integration and new repo layout code is a bit reshuffled. Compared to qmke build following features are not ported yet: * ios fat libs * qtbase 3rdparty static dependencies WebEngine build can be skipped with setting QT_FEATURE_qtwebengine_build=OFF Note this patch needs follow up for 6.2 branch to disable qtpdf builds by default, since this should not part of qt 6.2 release. Pick-to: 6.2 Task-number: QTBUG-95353 Change-Id: I4dd9f3934bdd478fb6d2fa686074a24d91f09953 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src/pdfquick')
-rw-r--r--src/pdfquick/CMakeLists.txt55
-rw-r--r--src/pdfquick/plugin.cpp99
-rw-r--r--src/pdfquick/plugins.qmltypes52
-rw-r--r--src/pdfquick/qml/+material/PdfStyle.qml54
-rw-r--r--src/pdfquick/qml/+universal/PdfStyle.qml55
-rw-r--r--src/pdfquick/qml/PdfMultiPageView.qml434
-rw-r--r--src/pdfquick/qml/PdfPageView.qml276
-rw-r--r--src/pdfquick/qml/PdfScrollablePageView.qml307
-rw-r--r--src/pdfquick/qml/PdfStyle.qml54
-rw-r--r--src/pdfquick/qquickpdfdocument.cpp305
-rw-r--r--src/pdfquick/qquickpdfdocument_p.h138
-rw-r--r--src/pdfquick/qquickpdflinkmodel.cpp131
-rw-r--r--src/pdfquick/qquickpdflinkmodel_p.h88
-rw-r--r--src/pdfquick/qquickpdfnavigationstack.cpp272
-rw-r--r--src/pdfquick/qquickpdfnavigationstack_p.h103
-rw-r--r--src/pdfquick/qquickpdfsearchmodel.cpp301
-rw-r--r--src/pdfquick/qquickpdfsearchmodel_p.h114
-rw-r--r--src/pdfquick/qquickpdfselection.cpp543
-rw-r--r--src/pdfquick/qquickpdfselection_p.h148
-rw-r--r--src/pdfquick/qquicktableviewextra.cpp78
-rw-r--r--src/pdfquick/qquicktableviewextra_p.h93
-rw-r--r--src/pdfquick/qtpdfquickglobal_p.h59
22 files changed, 3759 insertions, 0 deletions
diff --git a/src/pdfquick/CMakeLists.txt b/src/pdfquick/CMakeLists.txt
new file mode 100644
index 000000000..12cb393ff
--- /dev/null
+++ b/src/pdfquick/CMakeLists.txt
@@ -0,0 +1,55 @@
+find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS Core Gui Qml Quick)
+
+get_configure_mode(configureMode)
+
+set(qml_files
+# TODO:
+# "qml/+material/PdfStyle.qml"
+# "qml/+universal/PdfStyle.qml"
+ "qml/PdfMultiPageView.qml"
+ "qml/PdfPageView.qml"
+ "qml/PdfScrollablePageView.qml"
+ "qml/PdfStyle.qml"
+)
+
+qt_internal_add_qml_module(PdfQuick
+ URI "QtQuick.Pdf"
+ VERSION "${PROJECT_VERSION}"
+ CLASS_NAME QtQuick2PdfPlugin
+ NO_GENERATE_PLUGIN_SOURCE
+ NO_PLUGIN_OPTIONAL
+ NO_GENERATE_QMLTYPES
+ PLUGIN_TARGET qtpdfquickplugin
+ QML_FILES ${qml_files}
+ DEPENDENCIES QtQuickControls2
+ SOURCES
+ qquickpdfdocument.cpp qquickpdfdocument_p.h
+ qquickpdflinkmodel.cpp qquickpdflinkmodel_p.h
+ qquickpdfnavigationstack.cpp qquickpdfnavigationstack_p.h
+ qquickpdfsearchmodel.cpp qquickpdfsearchmodel_p.h
+ qquickpdfselection.cpp qquickpdfselection_p.h
+ qquicktableviewextra.cpp qquicktableviewextra_p.h
+ qtpdfquickglobal_p.h
+ INCLUDE_DIRECTORIES
+ ../3rdparty/chromium
+ PUBLIC_LIBRARIES
+ Qt::QuickPrivate
+ Qt::PdfPrivate
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+)
+
+qt_internal_extend_target(qtpdfquickplugin
+ SOURCES
+ plugin.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::PdfQuickPrivate
+)
+
+make_install_only(PdfQuick)
+make_install_only(qtpdfquickplugin)
+
+
+
diff --git a/src/pdfquick/plugin.cpp b/src/pdfquick/plugin.cpp
new file mode 100644
index 000000000..23f32bc39
--- /dev/null
+++ b/src/pdfquick/plugin.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlextensionplugin.h>
+#include "qquickpdfdocument_p.h"
+#include "qquickpdflinkmodel_p.h"
+#include "qquickpdfnavigationstack_p.h"
+#include "qquickpdfsearchmodel_p.h"
+#include "qquickpdfselection_p.h"
+#include "qquicktableviewextra_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtQuick.Pdf
+ \title Qt Quick PDF QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for handling PDF documents.
+
+ This QML module contains types for handling PDF documents.
+
+ To use the types in this module, import the module with the following line:
+
+ \code
+ import QtQuick.Pdf
+ \endcode
+*/
+
+class QtQuick2PdfPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ QtQuick2PdfPlugin() : QQmlExtensionPlugin() { }
+
+ void initializeEngine(QQmlEngine *engine, const char *uri) override {
+ Q_UNUSED(uri);
+#ifdef QT_STATIC
+ Q_UNUSED(engine);
+#else
+ engine->addImportPath(QStringLiteral("qrc:/"));
+#endif
+ }
+
+ void registerTypes(const char *uri) override {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Pdf"));
+
+ // Register the latest version, even if there are no new types or new revisions for existing types yet.
+ qmlRegisterModule(uri, 2, QT_VERSION_MINOR);
+
+ qmlRegisterType<QQuickPdfDocument>(uri, 5, 15, "PdfDocument");
+ qmlRegisterType<QQuickPdfLinkModel>(uri, 5, 15, "PdfLinkModel");
+ qmlRegisterType<QQuickPdfNavigationStack>(uri, 5, 15, "PdfNavigationStack");
+ qmlRegisterType<QQuickPdfSearchModel>(uri, 5, 15, "PdfSearchModel");
+ qmlRegisterType<QQuickPdfSelection>(uri, 5, 15, "PdfSelection");
+ qmlRegisterType<QQuickTableViewExtra>(uri, 5, 15, "TableViewExtra");
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/pdfquick/plugins.qmltypes b/src/pdfquick/plugins.qmltypes
new file mode 100644
index 000000000..a30361d33
--- /dev/null
+++ b/src/pdfquick/plugins.qmltypes
@@ -0,0 +1,52 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable QtQuick.Pdf 5.14'
+
+Module {
+ dependencies: [
+ "QtGraphicalEffects 1.12",
+ "QtQuick 2.14",
+ "QtQuick.Controls 2.14",
+ "QtQuick.Controls.Fusion 2.14",
+ "QtQuick.Controls.Fusion.impl 2.14",
+ "QtQuick.Controls.Imagine 2.14",
+ "QtQuick.Controls.Imagine.impl 2.14",
+ "QtQuick.Controls.Material 2.14",
+ "QtQuick.Controls.Material.impl 2.14",
+ "QtQuick.Controls.Universal 2.14",
+ "QtQuick.Controls.Universal.impl 2.12",
+ "QtQuick.Controls.impl 2.14",
+ "QtQuick.Shapes 1.14",
+ "QtQuick.Templates 2.14",
+ "QtQuick.Window 2.2"
+ ]
+ Component {
+ name: "QQuickPdfDocument"
+ prototype: "QObject"
+ exports: ["QtQuick.Pdf/PdfDocument 5.14"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "source"; type: "QUrl" }
+ Property { name: "pageCount"; type: "int"; isReadonly: true }
+ Property { name: "password"; type: "string" }
+ Property { name: "status"; type: "QPdfDocument::Status"; isReadonly: true }
+ Property { name: "title"; type: "string"; isReadonly: true }
+ Property { name: "subject"; type: "string"; isReadonly: true }
+ Property { name: "author"; type: "string"; isReadonly: true }
+ Property { name: "keywords"; type: "string"; isReadonly: true }
+ Property { name: "producer"; type: "string"; isReadonly: true }
+ Property { name: "creator"; type: "string"; isReadonly: true }
+ Property { name: "creationDate"; type: "QDateTime"; isReadonly: true }
+ Property { name: "modificationDate"; type: "QDateTime"; isReadonly: true }
+ Signal { name: "passwordRequired" }
+ Signal { name: "metaDataLoaded" }
+ Method {
+ name: "pagePointSize"
+ type: "QSizeF"
+ Parameter { name: "page"; type: "int" }
+ }
+ }
+}
diff --git a/src/pdfquick/qml/+material/PdfStyle.qml b/src/pdfquick/qml/+material/PdfStyle.qml
new file mode 100644
index 000000000..12df30466
--- /dev/null
+++ b/src/pdfquick/qml/+material/PdfStyle.qml
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQml 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Controls.Material 2.14
+import QtQuick.Shapes 1.14
+
+QtObject {
+ property Control prototypeControl: Control { }
+ function withAlpha(color, alpha) {
+ return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
+ }
+ property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5)
+ property color pageSearchResultsColor: withAlpha(Qt.lighter(Material.accentColor, 1.5), 0.5)
+ property color currentSearchResultStrokeColor: Material.accentColor
+ property real currentSearchResultStrokeWidth: 2
+ property color linkUnderscoreColor: prototypeControl.palette.link
+ property real linkUnderscoreStrokeWidth: 1
+ property var linkUnderscoreStrokeStyle: ShapePath.DashLine
+ property var linkUnderscoreDashPattern: [ 1, 4 ]
+}
diff --git a/src/pdfquick/qml/+universal/PdfStyle.qml b/src/pdfquick/qml/+universal/PdfStyle.qml
new file mode 100644
index 000000000..e92f2a080
--- /dev/null
+++ b/src/pdfquick/qml/+universal/PdfStyle.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQml 2.14
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Controls.Universal 2.14
+import QtQuick.Shapes 1.14
+
+QtObject {
+ property Control prototypeControl: Control { }
+ function withAlpha(color, alpha) {
+ return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
+ }
+ property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5)
+ property color pageSearchResultsColor: withAlpha(Qt.lighter(Universal.accent, 1.5), 0.5)
+ property color currentSearchResultStrokeColor: Universal.accent
+ property real currentSearchResultStrokeWidth: 2
+ property color linkUnderscoreColor: prototypeControl.palette.link
+ property real linkUnderscoreStrokeWidth: 1
+ property var linkUnderscoreStrokeStyle: ShapePath.DashLine
+ property var linkUnderscoreDashPattern: [ 1, 4 ]
+}
diff --git a/src/pdfquick/qml/PdfMultiPageView.qml b/src/pdfquick/qml/PdfMultiPageView.qml
new file mode 100644
index 000000000..71485c214
--- /dev/null
+++ b/src/pdfquick/qml/PdfMultiPageView.qml
@@ -0,0 +1,434 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import QtQuick.Window 2.14
+
+Item {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property bool debug: false
+
+ property string selectedText
+ function selectAll() {
+ var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
+ if (currentItem)
+ currentItem.selection.selectAll()
+ }
+ function copySelectionToClipboard() {
+ var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
+ if (debug)
+ console.log("currentItem", currentItem, "sel", currentItem.selection.text)
+ if (currentItem)
+ currentItem.selection.copyToClipboard()
+ }
+
+ // page navigation
+ property alias currentPage: navigationStack.currentPage
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ function goToPage(page) {
+ if (page === navigationStack.currentPage)
+ return
+ goToLocation(page, Qt.point(-1, -1), 0)
+ }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0) {
+ navigationStack.jumping = true // don't call navigationStack.update() because we will push() instead
+ root.renderScale = zoom
+ tableView.forceLayout() // but do ensure that the table layout is correct before we try to jump
+ navigationStack.jumping = false
+ }
+ navigationStack.push(page, location, zoom) // actually jump
+ }
+ property vector2d jumpLocationMargin: Qt.vector2d(10, 10) // px from top-left corner
+ property int currentPageRenderingStatus: Image.Null
+
+ // page scaling
+ property real renderScale: 1
+ property real pageRotation: 0
+ function resetScale() { root.renderScale = 1 }
+ function scaleToWidth(width, height) {
+ root.renderScale = width / (tableView.rot90 ? tableView.firstPagePointSize.height : tableView.firstPagePointSize.width)
+ }
+ function scaleToPage(width, height) {
+ var windowAspect = width / height
+ var pageAspect = tableView.firstPagePointSize.width / tableView.firstPagePointSize.height
+ if (tableView.rot90) {
+ if (windowAspect > pageAspect) {
+ root.renderScale = height / tableView.firstPagePointSize.width
+ } else {
+ root.renderScale = width / tableView.firstPagePointSize.height
+ }
+ } else {
+ if (windowAspect > pageAspect) {
+ root.renderScale = height / tableView.firstPagePointSize.height
+ } else {
+ root.renderScale = width / tableView.firstPagePointSize.width
+ }
+ }
+ }
+
+ // text search
+ property alias searchModel: searchModel
+ property alias searchString: searchModel.searchString
+ function searchBack() { --searchModel.currentResult }
+ function searchForward() { ++searchModel.currentResult }
+
+ id: root
+ PdfStyle { id: style }
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ anchors.leftMargin: 2
+ model: modelInUse && root.document !== undefined ? root.document.pageCount : 0
+ // workaround to make TableView do scheduleRebuildTable(RebuildOption::All) in cases when forceLayout() doesn't
+ property bool modelInUse: true
+ function rebuild() {
+ modelInUse = false
+ modelInUse = true
+ }
+ // end workaround
+ rowSpacing: 6
+ property real rotationNorm: Math.round((360 + (root.pageRotation % 360)) % 360)
+ property bool rot90: rotationNorm == 90 || rotationNorm == 270
+ onRot90Changed: forceLayout()
+ property size firstPagePointSize: document === undefined ? Qt.size(0, 0) : document.pagePointSize(0)
+ property real pageHolderWidth: Math.max(root.width, document === undefined ? 0 :
+ (rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
+ contentWidth: document === undefined ? 0 : pageHolderWidth + vscroll.width + 2
+ rowHeightProvider: function(row) { return (rot90 ? document.pagePointSize(row).width : document.pagePointSize(row).height) * root.renderScale }
+ TableViewExtra {
+ id: tableHelper
+ tableView: tableView
+ }
+ delegate: Rectangle {
+ id: pageHolder
+ color: root.debug ? "beige" : "transparent"
+ Text {
+ visible: root.debug
+ anchors { right: parent.right; verticalCenter: parent.verticalCenter }
+ rotation: -90; text: pageHolder.width.toFixed(1) + "x" + pageHolder.height.toFixed(1) + "\n" +
+ image.width.toFixed(1) + "x" + image.height.toFixed(1)
+ }
+ implicitWidth: tableView.pageHolderWidth
+ implicitHeight: tableView.rot90 ? image.width : image.height
+ property alias selection: selection
+ Rectangle {
+ id: paper
+ width: image.width
+ height: image.height
+ rotation: root.pageRotation
+ anchors.centerIn: pinch.active ? undefined : parent
+ property size pagePointSize: document.pagePointSize(index)
+ property real pageScale: image.paintedWidth / pagePointSize.width
+ Image {
+ id: image
+ source: document.source
+ currentFrame: index
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ width: paper.pagePointSize.width * root.renderScale
+ height: paper.pagePointSize.height * root.renderScale
+ property real renderScale: root.renderScale
+ property real oldRenderScale: 1
+ onRenderScaleChanged: {
+ image.sourceSize.width = paper.pagePointSize.width * renderScale
+ image.sourceSize.height = 0
+ paper.scale = 1
+ searchHighlights.update()
+ }
+ onStatusChanged: {
+ if (index === navigationStack.currentPage)
+ root.currentPageRenderingStatus = status
+ }
+ }
+ Shape {
+ anchors.fill: parent
+ visible: image.status === Image.Ready
+ onVisibleChanged: searchHighlights.update()
+ ShapePath {
+ strokeWidth: -1
+ fillColor: style.pageSearchResultsColor
+ scale: Qt.size(paper.pageScale, paper.pageScale)
+ PathMultiline {
+ id: searchHighlights
+ function update() {
+ // paths could be a binding, but we need to be able to "kick" it sometimes
+ paths = searchModel.boundingPolygonsOnPage(index)
+ }
+ }
+ }
+ Connections {
+ target: searchModel
+ // whenever the highlights on the _current_ page change, they actually need to change on _all_ pages
+ // (usually because the search string has changed)
+ function onCurrentPageBoundingPolygonsChanged() { searchHighlights.update() }
+ }
+ ShapePath {
+ strokeWidth: -1
+ fillColor: style.selectionColor
+ scale: Qt.size(paper.pageScale, paper.pageScale)
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+ Shape {
+ anchors.fill: parent
+ visible: image.status === Image.Ready && searchModel.currentPage === index
+ ShapePath {
+ strokeWidth: style.currentSearchResultStrokeWidth
+ strokeColor: style.currentSearchResultStrokeColor
+ fillColor: "transparent"
+ scale: Qt.size(paper.pageScale, paper.pageScale)
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ }
+ PinchHandler {
+ id: pinch
+ minimumScale: 0.1
+ maximumScale: root.renderScale < 4 ? 2 : 1
+ minimumRotation: root.pageRotation
+ maximumRotation: root.pageRotation
+ enabled: image.sourceSize.width < 5000
+ onActiveChanged:
+ if (active) {
+ paper.z = 10
+ } else {
+ paper.z = 0
+ var centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale,
+ pinch.centroid.position.y / root.renderScale)
+ var centroidInFlickable = tableView.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y)
+ var newSourceWidth = image.sourceSize.width * paper.scale
+ var ratio = newSourceWidth / image.sourceSize.width
+ if (root.debug)
+ console.log("pinch ended on page", index, "with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable,
+ "page at", pageHolder.x.toFixed(2), pageHolder.y.toFixed(2),
+ "contentX/Y were", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2))
+ if (ratio > 1.1 || ratio < 0.9) {
+ var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio)
+ paper.scale = 1
+ paper.x = 0
+ paper.y = 0
+ root.renderScale *= ratio
+ tableView.forceLayout()
+ if (tableView.rotationNorm == 0) {
+ tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.x - centroidInFlickable.x
+ tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.y - centroidInFlickable.y
+ } else if (tableView.rotationNorm == 90) {
+ tableView.contentX = pageHolder.x + tableView.originX + image.height - centroidOnPage.y - centroidInFlickable.x
+ tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.x - centroidInFlickable.y
+ } else if (tableView.rotationNorm == 180) {
+ tableView.contentX = pageHolder.x + tableView.originX + image.width - centroidOnPage.x - centroidInFlickable.x
+ tableView.contentY = pageHolder.y + tableView.originY + image.height - centroidOnPage.y - centroidInFlickable.y
+ } else if (tableView.rotationNorm == 270) {
+ tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.y - centroidInFlickable.x
+ tableView.contentY = pageHolder.y + tableView.originY + image.width - centroidOnPage.x - centroidInFlickable.y
+ }
+ if (root.debug)
+ console.log("contentX/Y adjusted to", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2), "y @top", pageHolder.y)
+ tableView.returnToBounds()
+ }
+ }
+ grabPermissions: PointerHandler.CanTakeOverFromAnything
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: mouseClickHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ TapHandler {
+ id: touchTapHandler
+ acceptedDevices: PointerDevice.TouchScreen
+ onTapped: {
+ selection.clear()
+ selection.forceActiveFocus()
+ }
+ }
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: image.currentFrame
+ }
+ delegate: Shape {
+ x: rect.x * paper.pageScale
+ y: rect.y * paper.pageScale
+ width: rect.width * paper.pageScale
+ height: rect.height * paper.pageScale
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: style.linkUnderscoreStrokeWidth
+ strokeColor: style.linkUnderscoreColor
+ strokeStyle: style.linkUnderscoreStrokeStyle
+ dashPattern: style.linkUnderscoreDashPattern
+ startX: 0; startY: height
+ PathLine { x: width; y: height }
+ }
+ MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
+ id: linkMA
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ hoverEnabled: true
+ onClicked: {
+ if (page >= 0)
+ root.goToLocation(page, location, zoom)
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ ToolTip {
+ visible: linkMA.containsMouse
+ delay: 1000
+ text: page >= 0 ?
+ ("page " + (page + 1) +
+ " location " + location.x.toFixed(1) + ", " + location.y.toFixed(1) +
+ " zoom " + zoom) : url
+ }
+ }
+ }
+ PdfSelection {
+ id: selection
+ anchors.fill: parent
+ document: root.document
+ page: image.currentFrame
+ renderScale: image.renderScale
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active && !mouseClickHandler.pressed
+ onTextChanged: root.selectedText = text
+ focus: true
+ }
+ }
+ }
+ ScrollBar.vertical: ScrollBar {
+ id: vscroll
+ property bool moved: false
+ onPositionChanged: moved = true
+ onActiveChanged: {
+ var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
+ var currentItem = tableHelper.itemAtCell(cell)
+ var currentLocation = Qt.point(0, 0)
+ if (currentItem) { // maybe the delegate wasn't loaded yet
+ currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
+ (tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
+ }
+ if (active) {
+ moved = false
+ // emitJumped false to avoid interrupting a pinch if TableView thinks it should scroll at the same time
+ navigationStack.push(cell.y, currentLocation, root.renderScale, false)
+ } else if (moved) {
+ navigationStack.update(cell.y, currentLocation, root.renderScale)
+ }
+ }
+ }
+ ScrollBar.horizontal: ScrollBar { }
+ }
+ onRenderScaleChanged: {
+ // if navigationStack.jumped changes the scale, don't turn around and update the stack again;
+ // and don't force layout either, because positionViewAtCell() will do that
+ if (navigationStack.jumping)
+ return
+ // make TableView rebuild from scratch, because otherwise it doesn't know the delegates are changing size
+ tableView.rebuild()
+ var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
+ var currentItem = tableHelper.itemAtCell(cell)
+ if (currentItem) {
+ var currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
+ (tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
+ navigationStack.update(cell.y, currentLocation, renderScale)
+ }
+ }
+ PdfNavigationStack {
+ id: navigationStack
+ property bool jumping: false
+ property int previousPage: 0
+ onJumped: {
+ jumping = true
+ root.renderScale = zoom
+ if (location.y < 0) {
+ // invalid to indicate that a specific location was not needed,
+ // so attempt to position the new page just as the current page is
+ var currentYOffset = 0
+ var previousPageDelegate = tableHelper.itemAtCell(0, previousPage)
+ if (previousPageDelegate)
+ currentYOffset = tableView.contentY - previousPageDelegate.y
+ tableHelper.positionViewAtRow(page, Qt.AlignTop, currentYOffset)
+ if (root.debug) {
+ console.log("going from page", previousPage, "to", page, "offset", currentYOffset,
+ "ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
+ }
+ } else {
+ // jump to a page and position the given location relative to the top-left corner of the viewport
+ var pageSize = root.document.pagePointSize(page)
+ pageSize.width *= root.renderScale
+ pageSize.height *= root.renderScale
+ var xOffsetLimit = Math.max(0, pageSize.width - root.width) / 2
+ var offset = Qt.point(Math.max(-xOffsetLimit, Math.min(xOffsetLimit,
+ location.x * root.renderScale - jumpLocationMargin.x)),
+ Math.max(0, location.y * root.renderScale - jumpLocationMargin.y))
+ tableHelper.positionViewAtCell(0, page, Qt.AlignLeft | Qt.AlignTop, offset)
+ if (root.debug) {
+ console.log("going to zoom", zoom, "loc", location, "on page", page,
+ "ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
+ }
+ }
+ jumping = false
+ previousPage = page
+ }
+ onCurrentPageChanged: searchModel.currentPage = currentPage
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ // TODO maybe avoid jumping if the result is already fully visible in the viewport
+ onCurrentResultBoundingRectChanged: root.goToLocation(currentPage,
+ Qt.point(currentResultBoundingRect.x, currentResultBoundingRect.y), 0)
+ }
+}
diff --git a/src/pdfquick/qml/PdfPageView.qml b/src/pdfquick/qml/PdfPageView.qml
new file mode 100644
index 000000000..b90ad2d7f
--- /dev/null
+++ b/src/pdfquick/qml/PdfPageView.qml
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import Qt.labs.animation 1.0
+
+Rectangle {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property alias status: image.status
+
+ property alias selectedText: selection.text
+ function selectAll() {
+ selection.selectAll()
+ }
+ function copySelectionToClipboard() {
+ selection.copyToClipboard()
+ }
+
+ // page navigation
+ property alias currentPage: navigationStack.currentPage
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ function goToPage(page) { goToLocation(page, Qt.point(0, 0), 0) }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0)
+ root.renderScale = zoom
+ navigationStack.push(page, location, zoom)
+ }
+
+ // page scaling
+ property real renderScale: 1
+ property alias sourceSize: image.sourceSize
+ function resetScale() {
+ image.sourceSize.width = 0
+ image.sourceSize.height = 0
+ root.x = 0
+ root.y = 0
+ root.scale = 1
+ }
+ function scaleToWidth(width, height) {
+ var halfRotation = Math.abs(root.rotation % 180)
+ image.sourceSize = Qt.size((halfRotation > 45 && halfRotation < 135) ? height : width, 0)
+ root.x = 0
+ root.y = 0
+ image.centerInSize = Qt.size(width, height)
+ image.centerOnLoad = true
+ image.vCenterOnLoad = (halfRotation > 45 && halfRotation < 135)
+ root.scale = 1
+ }
+ function scaleToPage(width, height) {
+ var windowAspect = width / height
+ var halfRotation = Math.abs(root.rotation % 180)
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ if (halfRotation > 45 && halfRotation < 135) {
+ // rotated 90 or 270º
+ var pageAspect = pagePointSize.height / pagePointSize.width
+ if (windowAspect > pageAspect) {
+ image.sourceSize = Qt.size(height, 0)
+ } else {
+ image.sourceSize = Qt.size(0, width)
+ }
+ } else {
+ var pageAspect = pagePointSize.width / pagePointSize.height
+ if (windowAspect > pageAspect) {
+ image.sourceSize = Qt.size(0, height)
+ } else {
+ image.sourceSize = Qt.size(width, 0)
+ }
+ }
+ image.centerInSize = Qt.size(width, height)
+ image.centerOnLoad = true
+ image.vCenterOnLoad = true
+ root.scale = 1
+ }
+
+ // text search
+ property alias searchModel: searchModel
+ property alias searchString: searchModel.searchString
+ function searchBack() { --searchModel.currentResult }
+ function searchForward() { ++searchModel.currentResult }
+
+ // implementation
+ id: root
+ width: image.width
+ height: image.height
+
+ PdfSelection {
+ id: selection
+ document: root.document
+ page: navigationStack.currentPage
+ fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / image.pageScale, textSelectionDrag.centroid.pressPosition.y / image.pageScale)
+ toPoint: Qt.point(textSelectionDrag.centroid.position.x / image.pageScale, textSelectionDrag.centroid.position.y / image.pageScale)
+ hold: !textSelectionDrag.active && !tapHandler.pressed
+ }
+
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ onCurrentPageChanged: root.goToPage(currentPage)
+ }
+
+ PdfNavigationStack {
+ id: navigationStack
+ onCurrentPageChanged: searchModel.currentPage = currentPage
+ // TODO onCurrentLocationChanged: position currentLocation.x and .y in middle // currentPageChanged() MUST occur first!
+ onCurrentZoomChanged: root.renderScale = currentZoom
+ // TODO deal with horizontal location (need WheelHandler or Flickable probably)
+ }
+
+ Image {
+ id: image
+ currentFrame: navigationStack.currentPage
+ source: document.status === PdfDocument.Ready ? document.source : ""
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ property bool centerOnLoad: false
+ property bool vCenterOnLoad: false
+ property size centerInSize
+ property real pageScale: image.paintedWidth / document.pagePointSize(navigationStack.currentPage).width
+ function reRenderIfNecessary() {
+ var newSourceWidth = image.sourceSize.width * root.scale
+ var ratio = newSourceWidth / image.sourceSize.width
+ if (ratio > 1.1 || ratio < 0.9) {
+ image.sourceSize.width = newSourceWidth
+ image.sourceSize.height = 0
+ root.scale = 1
+ }
+ }
+ onStatusChanged:
+ if (status == Image.Ready && centerOnLoad) {
+ root.x = (centerInSize.width - image.implicitWidth) / 2
+ root.y = vCenterOnLoad ? (centerInSize.height - image.implicitHeight) / 2 : 0
+ centerOnLoad = false
+ vCenterOnLoad = false
+ }
+ }
+ onRenderScaleChanged: {
+ image.sourceSize.width = document.pagePointSize(navigationStack.currentPage).width * renderScale
+ image.sourceSize.height = 0
+ root.scale = 1
+ }
+
+ Shape {
+ anchors.fill: parent
+ opacity: 0.25
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "cyan"
+ fillColor: "steelblue"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentPageBoundingPolygons
+ }
+ }
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "orange"
+ fillColor: "cyan"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ ShapePath {
+ fillColor: "orange"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: navigationStack.currentPage
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x * image.pageScale
+ y: rect.y * image.pageScale
+ width: rect.width * image.pageScale
+ height: rect.height * image.pageScale
+ MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ if (page >= 0)
+ navigationStack.push(page, Qt.point(0, 0), root.renderScale)
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+
+ PinchHandler {
+ id: pinch
+ minimumScale: 0.1
+ maximumScale: 10
+ minimumRotation: 0
+ maximumRotation: 0
+ onActiveChanged: if (!active) image.reRenderIfNecessary()
+ grabPermissions: PinchHandler.TakeOverForbidden // don't allow takeover if pinch has started
+ }
+ DragHandler {
+ id: pageMovingTouchDrag
+ acceptedDevices: PointerDevice.TouchScreen
+ }
+ DragHandler {
+ id: pageMovingMiddleMouseDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ acceptedButtons: Qt.MiddleButton
+ snapMode: DragHandler.NoSnap
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ // prevent it from being scrolled out of view
+ BoundaryRule on x {
+ minimum: 100 - root.width
+ maximum: root.parent.width - 100
+ }
+ BoundaryRule on y {
+ minimum: 100 - root.height
+ maximum: root.parent.height - 100
+ }
+}
diff --git a/src/pdfquick/qml/PdfScrollablePageView.qml b/src/pdfquick/qml/PdfScrollablePageView.qml
new file mode 100644
index 000000000..51d9e530d
--- /dev/null
+++ b/src/pdfquick/qml/PdfScrollablePageView.qml
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.14
+import Qt.labs.animation 1.0
+
+Flickable {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property bool debug: false
+ property alias status: image.status
+
+ property alias selectedText: selection.text
+ function selectAll() {
+ selection.selectAll()
+ }
+ function copySelectionToClipboard() {
+ selection.copyToClipboard()
+ }
+
+ // page navigation
+ property alias currentPage: navigationStack.currentPage
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ function goToPage(page) {
+ if (page === navigationStack.currentPage)
+ return
+ goToLocation(page, Qt.point(0, 0), 0)
+ }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0)
+ root.renderScale = zoom
+ navigationStack.push(page, location, zoom)
+ }
+
+ // page scaling
+ property real renderScale: 1
+ property real pageRotation: 0
+ property alias sourceSize: image.sourceSize
+ function resetScale() {
+ paper.scale = 1
+ root.renderScale = 1
+ }
+ function scaleToWidth(width, height) {
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ root.renderScale = root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width)
+ if (debug)
+ console.log("scaling", pagePointSize, "to fit", root.width, "rotated?", paper.rot90, "scale", root.renderScale)
+ root.contentX = 0
+ root.contentY = 0
+ }
+ function scaleToPage(width, height) {
+ var pagePointSize = document.pagePointSize(navigationStack.currentPage)
+ root.renderScale = Math.min(
+ root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width),
+ root.height / (paper.rot90 ? pagePointSize.width : pagePointSize.height) )
+ root.contentX = 0
+ root.contentY = 0
+ }
+
+ // text search
+ property alias searchModel: searchModel
+ property alias searchString: searchModel.searchString
+ function searchBack() { --searchModel.currentResult }
+ function searchForward() { ++searchModel.currentResult }
+
+ // implementation
+ id: root
+ PdfStyle { id: style }
+ contentWidth: paper.width
+ contentHeight: paper.height
+ ScrollBar.vertical: ScrollBar {
+ onActiveChanged:
+ if (!active ) {
+ var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
+ (root.contentY + root.height / 2) / root.renderScale)
+ navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
+ }
+ }
+ ScrollBar.horizontal: ScrollBar {
+ onActiveChanged:
+ if (!active ) {
+ var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
+ (root.contentY + root.height / 2) / root.renderScale)
+ navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
+ }
+ }
+
+ onRenderScaleChanged: {
+ image.sourceSize.width = document.pagePointSize(navigationStack.currentPage).width * renderScale
+ image.sourceSize.height = 0
+ paper.scale = 1
+ var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
+ (root.contentY + root.height / 2) / root.renderScale)
+ navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
+ }
+
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ // TODO maybe avoid jumping if the result is already fully visible in the viewport
+ onCurrentResultBoundingRectChanged: root.goToLocation(currentPage,
+ Qt.point(currentResultBoundingRect.x, currentResultBoundingRect.y), 0)
+ }
+
+ PdfNavigationStack {
+ id: navigationStack
+ onJumped: {
+ root.renderScale = zoom
+ var dx = Math.max(0, location.x * root.renderScale - root.width / 2) - root.contentX
+ var dy = Math.max(0, location.y * root.renderScale - root.height / 2) - root.contentY
+ // don't jump if location is in the viewport already, i.e. if the "error" between desired and actual contentX/Y is small
+ if (Math.abs(dx) > root.width / 3)
+ root.contentX += dx
+ if (Math.abs(dy) > root.height / 3)
+ root.contentY += dy
+ if (root.debug) {
+ console.log("going to zoom", zoom, "loc", location,
+ "on page", page, "ended up @", root.contentX + ", " + root.contentY)
+ }
+ }
+ onCurrentPageChanged: searchModel.currentPage = currentPage
+ }
+
+ Rectangle {
+ id: paper
+ width: rot90 ? image.height : image.width
+ height: rot90 ? image.width : image.height
+ property real rotationModulus: Math.abs(root.pageRotation % 180)
+ property bool rot90: rotationModulus > 45 && rotationModulus < 135
+
+ Image {
+ id: image
+ currentFrame: navigationStack.currentPage
+ source: document.status === PdfDocument.Ready ? document.source : ""
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ rotation: root.pageRotation
+ anchors.centerIn: parent
+ property real pageScale: image.paintedWidth / document.pagePointSize(navigationStack.currentPage).width
+
+ Shape {
+ anchors.fill: parent
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: -1
+ fillColor: style.pageSearchResultsColor
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentPageBoundingPolygons
+ }
+ }
+ ShapePath {
+ strokeWidth: style.currentSearchResultStrokeWidth
+ strokeColor: style.currentSearchResultStrokeColor
+ fillColor: "transparent"
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ ShapePath {
+ fillColor: style.selectionColor
+ scale: Qt.size(image.pageScale, image.pageScale)
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: navigationStack.currentPage
+ }
+ delegate: Shape {
+ x: rect.x * image.pageScale
+ y: rect.y * image.pageScale
+ width: rect.width * image.pageScale
+ height: rect.height * image.pageScale
+ ShapePath {
+ strokeWidth: style.linkUnderscoreStrokeWidth
+ strokeColor: style.linkUnderscoreColor
+ strokeStyle: style.linkUnderscoreStrokeStyle
+ dashPattern: style.linkUnderscoreDashPattern
+ startX: 0; startY: height
+ PathLine { x: width; y: height }
+ }
+ MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: {
+ if (page >= 0)
+ navigationStack.push(page, Qt.point(0, 0), root.renderScale)
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: mouseClickHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ TapHandler {
+ id: touchTapHandler
+ acceptedDevices: PointerDevice.TouchScreen
+ onTapped: {
+ selection.clear()
+ selection.focus = true
+ }
+ }
+ }
+
+ PdfSelection {
+ id: selection
+ anchors.fill: parent
+ document: root.document
+ page: navigationStack.currentPage
+ renderScale: image.pageScale
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active && !mouseClickHandler.pressed
+ focus: true
+ }
+
+ PinchHandler {
+ id: pinch
+ minimumScale: 0.1
+ maximumScale: root.renderScale < 4 ? 2 : 1
+ minimumRotation: 0
+ maximumRotation: 0
+ enabled: image.sourceSize.width < 5000
+ onActiveChanged:
+ if (!active) {
+ var centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale,
+ pinch.centroid.position.y / root.renderScale)
+ var centroidInFlickable = root.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y)
+ var newSourceWidth = image.sourceSize.width * paper.scale
+ var ratio = newSourceWidth / image.sourceSize.width
+ if (root.debug)
+ console.log("pinch ended with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable,
+ "page at", paper.x.toFixed(2), paper.y.toFixed(2),
+ "contentX/Y were", root.contentX.toFixed(2), root.contentY.toFixed(2))
+ if (ratio > 1.1 || ratio < 0.9) {
+ var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio)
+ paper.scale = 1
+ paper.x = 0
+ paper.y = 0
+ root.contentX = centroidOnPage.x - centroidInFlickable.x
+ root.contentY = centroidOnPage.y - centroidInFlickable.y
+ root.renderScale *= ratio // onRenderScaleChanged calls navigationStack.update() so we don't need to here
+ if (root.debug)
+ console.log("contentX/Y adjusted to", root.contentX.toFixed(2), root.contentY.toFixed(2))
+ } else {
+ paper.x = 0
+ paper.y = 0
+ }
+ }
+ grabPermissions: PointerHandler.CanTakeOverFromAnything
+ }
+ }
+}
diff --git a/src/pdfquick/qml/PdfStyle.qml b/src/pdfquick/qml/PdfStyle.qml
new file mode 100644
index 000000000..090465ce6
--- /dev/null
+++ b/src/pdfquick/qml/PdfStyle.qml
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQml 2.14
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Shapes 1.14
+
+QtObject {
+ property Control prototypeControl: Control { }
+ function withAlpha(color, alpha) {
+ return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
+ }
+ property color selectionColor: withAlpha(prototypeControl.palette.highlight, 0.5)
+ property color pageSearchResultsColor: "#80B0C4DE"
+ property color currentSearchResultStrokeColor: "cyan"
+ property real currentSearchResultStrokeWidth: 2
+ property color linkUnderscoreColor: prototypeControl.palette.link
+ property real linkUnderscoreStrokeWidth: 1
+ property var linkUnderscoreStrokeStyle: ShapePath.DashLine
+ property var linkUnderscoreDashPattern: [ 1, 4 ]
+}
diff --git a/src/pdfquick/qquickpdfdocument.cpp b/src/pdfquick/qquickpdfdocument.cpp
new file mode 100644
index 000000000..79b23ca84
--- /dev/null
+++ b/src/pdfquick/qquickpdfdocument.cpp
@@ -0,0 +1,305 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfdocument_p.h"
+#include <QQuickItem>
+#include <QQmlEngine>
+#include <QStandardPaths>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype PdfDocument
+ \instantiates QQuickPdfDocument
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of a PDF document.
+ \since 5.15
+
+ PdfDocument provides access to PDF document meta-information.
+ It is not necessary for rendering, as it is enough to use an
+ \l Image with source set to the URL of the PDF.
+*/
+
+/*!
+ Constructs a PDF document.
+*/
+QQuickPdfDocument::QQuickPdfDocument(QObject *parent)
+ : QObject(parent)
+{
+ connect(&m_doc, &QPdfDocument::passwordChanged, this, &QQuickPdfDocument::passwordChanged);
+ connect(&m_doc, &QPdfDocument::passwordRequired, this, &QQuickPdfDocument::passwordRequired);
+ connect(&m_doc, &QPdfDocument::statusChanged, [=] (QPdfDocument::Status status) {
+ emit statusChanged();
+ if (status == QPdfDocument::Ready)
+ emit metaDataChanged();
+ });
+ connect(&m_doc, &QPdfDocument::pageCountChanged, this, &QQuickPdfDocument::pageCountChanged);
+}
+
+void QQuickPdfDocument::componentComplete()
+{
+ if (m_doc.error() == QPdfDocument::IncorrectPasswordError)
+ emit passwordRequired();
+}
+
+/*!
+ \qmlproperty url PdfDocument::source
+
+ This property holds a URL pointing to the PDF file to be loaded.
+
+ \note At this time, only local filesystem URLs are supported.
+*/
+void QQuickPdfDocument::setSource(QUrl source)
+{
+ if (m_source == source)
+ return;
+
+ m_source = source;
+ m_maxPageWidthHeight = QSizeF();
+ emit sourceChanged();
+ if (source.scheme() == QLatin1String("qrc"))
+ m_doc.load(QLatin1Char(':') + source.path());
+ else
+ m_doc.load(source.toLocalFile());
+}
+
+/*!
+ \qmlproperty string PdfDocument::error
+
+ This property holds a translated string representation of the current
+ error, if any.
+
+ \sa status
+*/
+QString QQuickPdfDocument::error() const
+{
+ switch (m_doc.error()) {
+ case QPdfDocument::NoError:
+ return tr("no error");
+ break;
+ case QPdfDocument::UnknownError:
+ break;
+ case QPdfDocument::DataNotYetAvailableError:
+ return tr("data not yet available");
+ break;
+ case QPdfDocument::FileNotFoundError:
+ return tr("file not found");
+ break;
+ case QPdfDocument::InvalidFileFormatError:
+ return tr("invalid file format");
+ break;
+ case QPdfDocument::IncorrectPasswordError:
+ return tr("incorrect password");
+ break;
+ case QPdfDocument::UnsupportedSecuritySchemeError:
+ return tr("unsupported security scheme");
+ break;
+ }
+ return tr("unknown error");
+}
+
+/*!
+ \qmlproperty bool PdfDocument::password
+
+ This property holds the document password. If the passwordRequired()
+ signal is emitted, the UI should prompt the user and then set this
+ property so that document opening can continue.
+*/
+void QQuickPdfDocument::setPassword(const QString &password)
+{
+ if (m_doc.password() == password)
+ return;
+ m_doc.setPassword(password);
+ if (source().isValid() && source().isLocalFile())
+ m_doc.load(source().path());
+}
+
+/*!
+ \qmlproperty int PdfDocument::pageCount
+
+ This property holds the number of pages the PDF contains.
+*/
+
+/*!
+ \qmlsignal PdfDocument::passwordRequired()
+
+ This signal is emitted when the PDF requires a password in order to open.
+ The UI in a typical PDF viewer should prompt the user for the password
+ and then set the password property when the user has provided it.
+*/
+
+/*!
+ \qmlmethod size PdfDocument::pagePointSize(int page)
+
+ Returns the size of the given \a page in points.
+*/
+QSizeF QQuickPdfDocument::pagePointSize(int page) const
+{
+ return m_doc.pageSize(page);
+}
+
+qreal QQuickPdfDocument::maxPageWidth() const
+{
+ const_cast<QQuickPdfDocument *>(this)->updateMaxPageSize();
+ return m_maxPageWidthHeight.width();
+}
+
+qreal QQuickPdfDocument::maxPageHeight() const
+{
+ const_cast<QQuickPdfDocument *>(this)->updateMaxPageSize();
+ return m_maxPageWidthHeight.height();
+}
+
+/*!
+ \internal
+ \qmlmethod size PdfDocument::heightSumBeforePage(int page)
+
+ Returns the sum of the heights, in points, of all sets of \a facingPages
+ pages from 0 to the given \a page, exclusive.
+
+ That is, if the pages were laid out end-to-end in adjacent sets of
+ \a facingPages, what would be the distance in points from the top of the
+ first page to the top of the given page.
+*/
+// Workaround for lack of something analogous to ListView.positionViewAtIndex() in TableView
+qreal QQuickPdfDocument::heightSumBeforePage(int page, qreal spacing, int facingPages) const
+{
+ qreal ret = 0;
+ for (int i = 0; i < page; i+= facingPages) {
+ if (i + facingPages > page)
+ break;
+ qreal facingPagesHeight = 0;
+ for (int j = i; j < i + facingPages; ++j)
+ facingPagesHeight = qMax(facingPagesHeight, pagePointSize(j).height());
+ ret += facingPagesHeight + spacing;
+ }
+ return ret;
+}
+
+void QQuickPdfDocument::updateMaxPageSize()
+{
+ if (m_maxPageWidthHeight.isValid())
+ return;
+ qreal w = 0;
+ qreal h = 0;
+ const int count = pageCount();
+ for (int i = 0; i < count; ++i) {
+ auto size = pagePointSize(i);
+ w = qMax(w, size.width());
+ h = qMax(w, size.height());
+ }
+ m_maxPageWidthHeight = QSizeF(w, h);
+}
+
+/*!
+ \qmlproperty real PdfDocument::maxPageWidth
+
+ This property holds the width of the widest page in the document, in points.
+*/
+
+/*!
+ \qmlproperty real PdfDocument::maxPageHeight
+
+ This property holds the height of the tallest page in the document, in points.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::title
+
+ This property holds the document's title. A typical viewer UI can bind this
+ to the \c Window.title property.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::author
+
+ This property holds the name of the person who created the document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::subject
+
+ This property holds the subject of the document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::keywords
+
+ This property holds the keywords associated with the document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::creator
+
+ If the document was converted to PDF from another format, this property
+ holds the name of the software that created the original document.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::producer
+
+ If the document was converted to PDF from another format, this property
+ holds the name of the software that converted it to PDF.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::creationDate
+
+ This property holds the date and time the document was created.
+*/
+
+/*!
+ \qmlproperty string PdfDocument::modificationDate
+
+ This property holds the date and time the document was most recently
+ modified.
+*/
+
+/*!
+ \qmlproperty enum PdfDocument::status
+
+ This property tells the current status of the document. The possible values are:
+
+ \value PdfDocument.Null The initial status after the document has been created or after it has been closed.
+ \value PdfDocument.Loading The status after load() has been called and before the document is fully loaded.
+ \value PdfDocument.Ready The status when the document is fully loaded and its data can be accessed.
+ \value PdfDocument.Unloading The status after close() has been called on an open document.
+ At this point the document is still valid and all its data can be accessed.
+ \value PdfDocument.Error The status after Loading, if loading has failed.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdfquick/qquickpdfdocument_p.h b/src/pdfquick/qquickpdfdocument_p.h
new file mode 100644
index 000000000..cfeeb7b98
--- /dev/null
+++ b/src/pdfquick/qquickpdfdocument_p.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFDOCUMENT_P_H
+#define QQUICKPDFDOCUMENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtPdfQuick/private/qtpdfquickglobal_p.h>
+#include <QtPdf/QPdfDocument>
+#include <QDateTime>
+#include <QJSValue>
+#include <QQmlParserStatus>
+#include <QUrl>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+
+class Q_PDFQUICK_EXPORT QQuickPdfDocument : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL)
+ Q_PROPERTY(qreal maxPageWidth READ maxPageWidth NOTIFY metaDataChanged)
+ Q_PROPERTY(qreal maxPageHeight READ maxPageHeight NOTIFY metaDataChanged)
+ Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL)
+ Q_PROPERTY(QPdfDocument::Status status READ status NOTIFY statusChanged FINAL)
+ Q_PROPERTY(QString error READ error NOTIFY statusChanged FINAL)
+
+ Q_PROPERTY(QString title READ title NOTIFY metaDataChanged)
+ Q_PROPERTY(QString subject READ subject NOTIFY metaDataChanged)
+ Q_PROPERTY(QString author READ author NOTIFY metaDataChanged)
+ Q_PROPERTY(QString keywords READ keywords NOTIFY metaDataChanged)
+ Q_PROPERTY(QString producer READ producer NOTIFY metaDataChanged)
+ Q_PROPERTY(QString creator READ creator NOTIFY metaDataChanged)
+ Q_PROPERTY(QDateTime creationDate READ creationDate NOTIFY metaDataChanged)
+ Q_PROPERTY(QDateTime modificationDate READ modificationDate NOTIFY metaDataChanged)
+
+public:
+ explicit QQuickPdfDocument(QObject *parent = nullptr);
+
+ void classBegin() override {}
+ void componentComplete() override;
+
+ QUrl source() const { return m_source; }
+ void setSource(QUrl source);
+
+ int pageCount() const { return m_doc.pageCount(); }
+ QPdfDocument::Status status() const { return m_doc.status(); }
+
+ QString error() const;
+
+ QString password() const { return m_doc.password(); }
+ void setPassword(const QString &password);
+
+ QString title() { return m_doc.metaData(QPdfDocument::Title).toString(); }
+ QString author() { return m_doc.metaData(QPdfDocument::Author).toString(); }
+ QString subject() { return m_doc.metaData(QPdfDocument::Subject).toString(); }
+ QString keywords() { return m_doc.metaData(QPdfDocument::Keywords).toString(); }
+ QString producer() { return m_doc.metaData(QPdfDocument::Producer).toString(); }
+ QString creator() { return m_doc.metaData(QPdfDocument::Creator).toString(); }
+ QDateTime creationDate() { return m_doc.metaData(QPdfDocument::CreationDate).toDateTime(); }
+ QDateTime modificationDate() { return m_doc.metaData(QPdfDocument::ModificationDate).toDateTime(); }
+
+ Q_INVOKABLE QSizeF pagePointSize(int page) const;
+ qreal maxPageWidth() const;
+ qreal maxPageHeight() const;
+ Q_INVOKABLE qreal heightSumBeforePage(int page, qreal spacing = 0, int facingPages = 1) const;
+
+Q_SIGNALS:
+ void sourceChanged();
+ void passwordChanged();
+ void passwordRequired();
+ void statusChanged();
+ void pageCountChanged();
+ void metaDataChanged();
+
+private:
+ QPdfDocument &document() { return m_doc; }
+ void updateMaxPageSize();
+
+private:
+ QUrl m_source;
+ QPdfDocument m_doc;
+ QSizeF m_maxPageWidthHeight;
+
+ friend class QQuickPdfLinkModel;
+ friend class QQuickPdfSearchModel;
+ friend class QQuickPdfSelection;
+
+ Q_DISABLE_COPY(QQuickPdfDocument)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPDFDOCUMENT_P_H
diff --git a/src/pdfquick/qquickpdflinkmodel.cpp b/src/pdfquick/qquickpdflinkmodel.cpp
new file mode 100644
index 000000000..4f3958337
--- /dev/null
+++ b/src/pdfquick/qquickpdflinkmodel.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdflinkmodel_p.h"
+#include <QQuickItem>
+#include <QQmlEngine>
+#include <QStandardPaths>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype PdfLinkModel
+ \instantiates QQuickPdfLinkModel
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of links within a PDF document.
+ \since 5.15
+
+ PdfLinkModel provides the geometry and the destination for each link
+ that the specified \l page contains.
+
+ The available model roles are:
+
+ \value rect
+ Bounding rectangle around the link.
+ \value url
+ If the link is a web link, the URL for that; otherwise an empty URL.
+ \value page
+ If the link is an internal link, the page number to which the link should jump; otherwise \c {-1}.
+ \value location
+ If the link is an internal link, the location on the page to which the link should jump.
+ \value zoom
+ If the link is an internal link, the intended zoom level on the destination page.
+
+ Normally it will be used with \l {QtQuick::Repeater}{Repeater} to visualize
+ the links and provide the ability to click them:
+
+ \qml
+ Repeater {
+ model: PdfLinkModel {
+ document: root.document
+ page: image.currentFrame
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x
+ y: rect.y
+ width: rect.width
+ height: rect.height
+ HoverHandler { cursorShape: Qt.PointingHandCursor }
+ TapHandler {
+ onTapped: {
+ if (page >= 0)
+ image.currentFrame = page
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+ \endqml
+
+ \note General-purpose PDF viewing capabilities are provided by
+ \l PdfScrollablePageView and \l PdfMultiPageView. PdfLinkModel is only needed
+ when building PDF view components from scratch.
+*/
+
+QQuickPdfLinkModel::QQuickPdfLinkModel(QObject *parent)
+ : QPdfLinkModel(parent)
+{
+}
+
+/*!
+ \qmlproperty PdfDocument PdfLinkModel::document
+
+ This property holds the PDF document in which links are to be found.
+*/
+QQuickPdfDocument *QQuickPdfLinkModel::document() const
+{
+ return m_quickDocument;
+}
+
+void QQuickPdfLinkModel::setDocument(QQuickPdfDocument *document)
+{
+ if (document == m_quickDocument)
+ return;
+ m_quickDocument = document;
+ QPdfLinkModel::setDocument(&document->m_doc);
+}
+
+/*!
+ \qmlproperty int PdfLinkModel::page
+
+ This property holds the page number on which links are to be found.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdfquick/qquickpdflinkmodel_p.h b/src/pdfquick/qquickpdflinkmodel_p.h
new file mode 100644
index 000000000..d777643ef
--- /dev/null
+++ b/src/pdfquick/qquickpdflinkmodel_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFLINKMODEL_P_H
+#define QQUICKPDFLINKMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtPdfQuick/private/qtpdfquickglobal_p.h>
+#include <QtPdfQuick/private/qquickpdfdocument_p.h>
+#include <QtPdf/private/qpdflinkmodel_p.h>
+
+#include <QVariant>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_PDFQUICK_EXPORT QQuickPdfLinkModel : public QPdfLinkModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+
+public:
+ explicit QQuickPdfLinkModel(QObject *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument *document);
+
+signals:
+ void documentChanged();
+
+private:
+ void updateResults();
+
+private:
+ QQuickPdfDocument *m_quickDocument;
+ QList<QPolygonF> m_linksGeometry;
+
+ Q_DISABLE_COPY(QQuickPdfLinkModel)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfLinkModel)
+
+#endif // QQUICKPDFLINKMODEL_P_H
diff --git a/src/pdfquick/qquickpdfnavigationstack.cpp b/src/pdfquick/qquickpdfnavigationstack.cpp
new file mode 100644
index 000000000..e41e43ed5
--- /dev/null
+++ b/src/pdfquick/qquickpdfnavigationstack.cpp
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfnavigationstack_p.h"
+#include <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcNav, "qt.pdf.navigationstack")
+
+/*!
+ \qmltype PdfNavigationStack
+ \instantiates QQuickPdfNavigationStack
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief History of the destinations visited within a PDF Document.
+ \since 5.15
+
+ PdfNavigationStack remembers which destinations the user has visited in a PDF
+ document, and provides the ability to traverse backward and forward.
+*/
+
+QQuickPdfNavigationStack::QQuickPdfNavigationStack(QObject *parent)
+ : QObject(parent)
+{
+ push(0, QPointF(), 1);
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::forward()
+
+ Goes back to the page, location and zoom level that was being viewed before
+ back() was called, and then emits the \l jumped() signal.
+
+ If a new destination was pushed since the last time \l back() was called,
+ the forward() function does nothing, because there is a branch in the
+ timeline which causes the "future" to be lost.
+*/
+void QQuickPdfNavigationStack::forward()
+{
+ if (m_currentHistoryIndex >= m_pageHistory.count() - 1)
+ return;
+ bool backAvailableWas = backAvailable();
+ bool forwardAvailableWas = forwardAvailable();
+ QPointF currentLocationWas = currentLocation();
+ qreal currentZoomWas = currentZoom();
+ ++m_currentHistoryIndex;
+ m_changing = true;
+ emit jumped(currentPage(), currentLocation(), currentZoom());
+ if (currentZoomWas != currentZoom())
+ emit currentZoomChanged();
+ emit currentPageChanged();
+ if (currentLocationWas != currentLocation())
+ emit currentLocationChanged();
+ if (!backAvailableWas)
+ emit backAvailableChanged();
+ if (forwardAvailableWas != forwardAvailable())
+ emit forwardAvailableChanged();
+ m_changing = false;
+ qCDebug(qLcNav) << "forward: index" << m_currentHistoryIndex << "page" << currentPage()
+ << "@" << currentLocation() << "zoom" << currentZoom();
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::back()
+
+ Pops the stack, updates the \l currentPage, \l currentLocation and
+ \l currentZoom properties to the most-recently-viewed destination, and then
+ emits the \l jumped() signal.
+*/
+void QQuickPdfNavigationStack::back()
+{
+ if (m_currentHistoryIndex <= 0)
+ return;
+ bool backAvailableWas = backAvailable();
+ bool forwardAvailableWas = forwardAvailable();
+ QPointF currentLocationWas = currentLocation();
+ qreal currentZoomWas = currentZoom();
+ --m_currentHistoryIndex;
+ m_changing = true;
+ emit jumped(currentPage(), currentLocation(), currentZoom());
+ if (currentZoomWas != currentZoom())
+ emit currentZoomChanged();
+ emit currentPageChanged();
+ if (currentLocationWas != currentLocation())
+ emit currentLocationChanged();
+ if (backAvailableWas != backAvailable())
+ emit backAvailableChanged();
+ if (!forwardAvailableWas)
+ emit forwardAvailableChanged();
+ m_changing = false;
+ qCDebug(qLcNav) << "back: index" << m_currentHistoryIndex << "page" << currentPage()
+ << "@" << currentLocation() << "zoom" << currentZoom();
+}
+
+/*!
+ \qmlproperty int PdfNavigationStack::currentPage
+
+ This property holds the current page that is being viewed.
+ If there is no current page, it holds \c -1.
+*/
+int QQuickPdfNavigationStack::currentPage() const
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return -1;
+ return m_pageHistory.at(m_currentHistoryIndex)->page;
+}
+
+/*!
+ \qmlproperty point PdfNavigationStack::currentLocation
+
+ This property holds the current location on the page that is being viewed.
+*/
+QPointF QQuickPdfNavigationStack::currentLocation() const
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return QPointF();
+ return m_pageHistory.at(m_currentHistoryIndex)->location;
+}
+
+/*!
+ \qmlproperty real PdfNavigationStack::currentZoom
+
+ This property holds the magnification scale on the page that is being viewed.
+*/
+qreal QQuickPdfNavigationStack::currentZoom() const
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return 1;
+ return m_pageHistory.at(m_currentHistoryIndex)->zoom;
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::push(int page, point location, qreal zoom)
+
+ Adds the given destination, consisting of \a page, \a location and \a zoom,
+ to the history of visited locations. If \a emitJumped is \c false, the
+ \l jumped() signal will not be emitted.
+
+ If forwardAvailable is \c true, calling this function represents a branch
+ in the timeline which causes the "future" to be lost, and therefore
+ forwardAvailable will change to \c false.
+*/
+void QQuickPdfNavigationStack::push(int page, QPointF location, qreal zoom, bool emitJumped)
+{
+ if (page == currentPage() && location == currentLocation() && zoom == currentZoom())
+ return;
+ if (qFuzzyIsNull(zoom))
+ zoom = currentZoom();
+ bool backAvailableWas = backAvailable();
+ bool forwardAvailableWas = forwardAvailable();
+ if (!m_changing) {
+ if (m_currentHistoryIndex >= 0 && forwardAvailableWas)
+ m_pageHistory.remove(m_currentHistoryIndex + 1, m_pageHistory.count() - m_currentHistoryIndex - 1);
+ m_pageHistory.append(QExplicitlySharedDataPointer<QPdfDestinationPrivate>(new QPdfDestinationPrivate(page, location, zoom)));
+ m_currentHistoryIndex = m_pageHistory.count() - 1;
+ }
+ emit currentZoomChanged();
+ emit currentPageChanged();
+ emit currentLocationChanged();
+ if (m_changing)
+ return;
+ if (!backAvailableWas)
+ emit backAvailableChanged();
+ if (forwardAvailableWas)
+ emit forwardAvailableChanged();
+ if (emitJumped)
+ emit jumped(page, location, zoom);
+ qCDebug(qLcNav) << "push: index" << m_currentHistoryIndex << "page" << page
+ << "@" << location << "zoom" << zoom << "-> history" <<
+ [this]() {
+ QStringList ret;
+ for (auto d : m_pageHistory)
+ ret << QString::number(d->page);
+ return ret.join(QLatin1Char(','));
+ }();
+}
+
+/*!
+ \qmlmethod void PdfNavigationStack::update(int page, point location, qreal zoom)
+
+ Modifies the current destination, consisting of \a page, \a location and \a zoom.
+
+ This can be called periodically while the user is manually moving around
+ the document, so that after back() is called, forward() will jump back to
+ the most-recently-viewed destination rather than the destination that was
+ last specified by push().
+
+ The \c currentZoomChanged, \c currentPageChanged and \c currentLocationChanged
+ signals will be emitted if the respective properties are actually changed.
+ The \l jumped signal is not emitted, because this operation
+ represents smooth movement rather than a navigational jump.
+*/
+void QQuickPdfNavigationStack::update(int page, QPointF location, qreal zoom)
+{
+ if (m_currentHistoryIndex < 0 || m_currentHistoryIndex >= m_pageHistory.count())
+ return;
+ int currentPageWas = currentPage();
+ QPointF currentLocationWas = currentLocation();
+ qreal currentZoomWas = currentZoom();
+ if (page == currentPageWas && location == currentLocationWas && zoom == currentZoomWas)
+ return;
+ m_pageHistory[m_currentHistoryIndex]->page = page;
+ m_pageHistory[m_currentHistoryIndex]->location = location;
+ m_pageHistory[m_currentHistoryIndex]->zoom = zoom;
+ if (currentZoomWas != zoom)
+ emit currentZoomChanged();
+ if (currentPageWas != page)
+ emit currentPageChanged();
+ if (currentLocationWas != location)
+ emit currentLocationChanged();
+ qCDebug(qLcNav) << "update: index" << m_currentHistoryIndex << "page" << page
+ << "@" << location << "zoom" << zoom << "-> history" <<
+ [this]() {
+ QStringList ret;
+ for (auto d : m_pageHistory)
+ ret << QString::number(d->page);
+ return ret.join(QLatin1Char(','));
+ }();
+}
+
+bool QQuickPdfNavigationStack::backAvailable() const
+{
+ return m_currentHistoryIndex > 0;
+}
+
+bool QQuickPdfNavigationStack::forwardAvailable() const
+{
+ return m_currentHistoryIndex < m_pageHistory.count() - 1;
+}
+
+/*!
+ \qmlsignal PdfNavigationStack::jumped(int page, point location, qreal zoom)
+
+ This signal is emitted when forward(), back() or push() is called, but not
+ when update() is called.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdfquick/qquickpdfnavigationstack_p.h b/src/pdfquick/qquickpdfnavigationstack_p.h
new file mode 100644
index 000000000..1d37a4a85
--- /dev/null
+++ b/src/pdfquick/qquickpdfnavigationstack_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFNAVIGATIONSTACK_P_H
+#define QQUICKPDFNAVIGATIONSTACK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtPdfQuick/private/qtpdfquickglobal_p.h>
+#include <QtPdfQuick/private/qquickpdfdocument_p.h>
+#include <QtPdf/private/qpdfdestination_p.h>
+
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_PDFQUICK_EXPORT QQuickPdfNavigationStack : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int currentPage READ currentPage NOTIFY currentPageChanged)
+ Q_PROPERTY(QPointF currentLocation READ currentLocation NOTIFY currentLocationChanged)
+ Q_PROPERTY(qreal currentZoom READ currentZoom NOTIFY currentZoomChanged)
+ Q_PROPERTY(bool backAvailable READ backAvailable NOTIFY backAvailableChanged)
+ Q_PROPERTY(bool forwardAvailable READ forwardAvailable NOTIFY forwardAvailableChanged)
+
+public:
+ explicit QQuickPdfNavigationStack(QObject *parent = nullptr);
+
+ Q_INVOKABLE void push(int page, QPointF location, qreal zoom, bool emitJumped = true);
+ Q_INVOKABLE void update(int page, QPointF location, qreal zoom);
+ Q_INVOKABLE void forward();
+ Q_INVOKABLE void back();
+
+ int currentPage() const;
+ QPointF currentLocation() const;
+ qreal currentZoom() const;
+
+ bool backAvailable() const;
+ bool forwardAvailable() const;
+
+Q_SIGNALS:
+ void currentPageChanged();
+ void currentLocationChanged();
+ void currentZoomChanged();
+ void backAvailableChanged();
+ void forwardAvailableChanged();
+ void jumped(int page, QPointF location, qreal zoom);
+
+private:
+ QList<QExplicitlySharedDataPointer<QPdfDestinationPrivate>> m_pageHistory;
+ int m_currentHistoryIndex = 0;
+ bool m_changing = false;
+
+ Q_DISABLE_COPY(QQuickPdfNavigationStack)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfNavigationStack)
+
+#endif // QQUICKPDFNAVIGATIONSTACK_P_H
diff --git a/src/pdfquick/qquickpdfsearchmodel.cpp b/src/pdfquick/qquickpdfsearchmodel.cpp
new file mode 100644
index 000000000..d91ca69f8
--- /dev/null
+++ b/src/pdfquick/qquickpdfsearchmodel.cpp
@@ -0,0 +1,301 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfsearchmodel_p.h"
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcSearch, "qt.pdf.search")
+
+/*!
+ \qmltype PdfSearchModel
+ \instantiates QQuickPdfSearchModel
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of text search results within a PDF Document.
+ \since 5.15
+
+ PdfSearchModel provides the ability to search for text strings within a
+ document and get the geometric locations of matches on each page.
+*/
+
+QQuickPdfSearchModel::QQuickPdfSearchModel(QObject *parent)
+ : QPdfSearchModel(parent)
+{
+ connect(this, &QPdfSearchModel::searchStringChanged,
+ this, &QQuickPdfSearchModel::onResultsChanged);
+}
+
+QQuickPdfDocument *QQuickPdfSearchModel::document() const
+{
+ return m_quickDocument;
+}
+
+void QQuickPdfSearchModel::setDocument(QQuickPdfDocument *document)
+{
+ if (document == m_quickDocument || !document)
+ return;
+
+ m_quickDocument = document;
+ QPdfSearchModel::setDocument(&document->m_doc);
+}
+
+/*!
+ \qmlproperty list<list<point>> PdfSearchModel::currentResultBoundingPolygons
+
+ A set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around the regions comprising the search result \l currentResult
+ on \l currentPage. This is normally used to highlight one search result
+ at a time, in a UI that allows stepping through the results:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: doc
+ currentPage: view.currentPage
+ currentResult: ...
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: searchModel.currentResultBoundingPolygons
+ }
+ }
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QList<QPolygonF> QQuickPdfSearchModel::currentResultBoundingPolygons() const
+{
+ QList<QPolygonF> ret;
+ const auto &results = const_cast<QQuickPdfSearchModel *>(this)->resultsOnPage(m_currentPage);
+ if (m_currentResult < 0 || m_currentResult >= results.count())
+ return ret;
+ const auto result = results[m_currentResult];
+ for (auto rect : result.rectangles())
+ ret << QPolygonF(rect);
+ return ret;
+}
+
+/*!
+ \qmlproperty point PdfSearchModel::currentResultBoundingRect
+
+ The bounding box containing all \l currentResultBoundingPolygons.
+
+ When this property changes, a scrollable view should automatically scroll
+ itself in such a way as to ensure that this region is visible; for example,
+ it could try to position the upper-left corner near the upper-left of its
+ own viewport, subject to the constraints of the scrollable area.
+*/
+QRectF QQuickPdfSearchModel::currentResultBoundingRect() const
+{
+ QRectF ret;
+ const auto &results = const_cast<QQuickPdfSearchModel *>(this)->resultsOnPage(m_currentPage);
+ if (m_currentResult < 0 || m_currentResult >= results.count())
+ return ret;
+ auto rects = results[m_currentResult].rectangles();
+ ret = rects.takeFirst();
+ for (auto rect : rects)
+ ret = ret.united(rect);
+ return ret;
+}
+
+void QQuickPdfSearchModel::onResultsChanged()
+{
+ emit currentPageBoundingPolygonsChanged();
+ emit currentResultBoundingPolygonsChanged();
+}
+
+/*!
+ \qmlproperty list<list<point>> PdfSearchModel::currentPageBoundingPolygons
+
+ A set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around all the regions where search results are found on
+ \l currentPage:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: doc
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: searchModel.matchGeometry(view.currentPage)
+ }
+ }
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QList<QPolygonF> QQuickPdfSearchModel::currentPageBoundingPolygons() const
+{
+ return const_cast<QQuickPdfSearchModel *>(this)->boundingPolygonsOnPage(m_currentPage);
+}
+
+/*!
+ \qmlfunction list<list<point>> PdfSearchModel::boundingPolygonsOnPage(int page)
+
+ Returns a set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around all the locations where search results are found:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: doc
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: searchModel.matchGeometry(view.currentPage)
+ }
+ }
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QList<QPolygonF> QQuickPdfSearchModel::boundingPolygonsOnPage(int page)
+{
+ if (!document() || searchString().isEmpty() || page < 0 || page > document()->pageCount())
+ return {};
+
+ updatePage(page);
+
+ QList<QPolygonF> ret;
+ auto m = QPdfSearchModel::resultsOnPage(page);
+ for (auto result : m) {
+ for (auto rect : result.rectangles())
+ ret << QPolygonF(rect);
+ }
+
+ return ret;
+}
+
+/*!
+ \qmlproperty int PdfSearchModel::currentPage
+
+ The page on which \l currentMatchGeometry should provide filtered search results.
+*/
+void QQuickPdfSearchModel::setCurrentPage(int currentPage)
+{
+ if (m_currentPage == currentPage)
+ return;
+
+ if (currentPage < 0)
+ currentPage = document()->pageCount() - 1;
+ else if (currentPage >= document()->pageCount())
+ currentPage = 0;
+
+ m_currentPage = currentPage;
+ if (!m_suspendSignals) {
+ emit currentPageChanged();
+ onResultsChanged();
+ }
+}
+
+/*!
+ \qmlproperty int PdfSearchModel::currentResult
+
+ The result index on \l currentPage for which \l currentResultBoundingPolygons
+ should provide the regions to highlight.
+*/
+void QQuickPdfSearchModel::setCurrentResult(int currentResult)
+{
+ if (m_currentResult == currentResult)
+ return;
+
+ int currentResultWas = currentResult;
+ int currentPageWas = m_currentPage;
+ if (currentResult < 0) {
+ setCurrentPage(m_currentPage - 1);
+ while (resultsOnPage(m_currentPage).count() == 0 && m_currentPage != currentPageWas) {
+ m_suspendSignals = true;
+ setCurrentPage(m_currentPage - 1);
+ }
+ if (m_suspendSignals) {
+ emit currentPageChanged();
+ m_suspendSignals = false;
+ }
+ const auto results = resultsOnPage(m_currentPage);
+ currentResult = results.count() - 1;
+ } else {
+ const auto results = resultsOnPage(m_currentPage);
+ if (currentResult >= results.count()) {
+ setCurrentPage(m_currentPage + 1);
+ while (resultsOnPage(m_currentPage).count() == 0 && m_currentPage != currentPageWas) {
+ m_suspendSignals = true;
+ setCurrentPage(m_currentPage + 1);
+ }
+ if (m_suspendSignals) {
+ emit currentPageChanged();
+ m_suspendSignals = false;
+ }
+ currentResult = 0;
+ }
+ }
+ qCDebug(qLcSearch) << "currentResult was" << m_currentResult
+ << "requested" << currentResultWas << "on page" << currentPageWas
+ << "->" << currentResult << "on page" << m_currentPage;
+
+ m_currentResult = currentResult;
+ emit currentResultChanged();
+ emit currentResultBoundingPolygonsChanged();
+ emit currentResultBoundingRectChanged();
+}
+
+/*!
+ \qmlproperty string PdfSearchModel::searchString
+
+ The string to search for.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdfquick/qquickpdfsearchmodel_p.h b/src/pdfquick/qquickpdfsearchmodel_p.h
new file mode 100644
index 000000000..ad92d4222
--- /dev/null
+++ b/src/pdfquick/qquickpdfsearchmodel_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFSEARCHMODEL_P_H
+#define QQUICKPDFSEARCHMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtPdfQuick/private/qtpdfquickglobal_p.h>
+#include <QtPdfQuick/private/qquickpdfdocument_p.h>
+#include <QtPdf/qpdfsearchmodel.h>
+
+#include <QtCore/qvariant.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_PDFQUICK_EXPORT QQuickPdfSearchModel : public QPdfSearchModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged)
+ Q_PROPERTY(int currentResult READ currentResult WRITE setCurrentResult NOTIFY currentResultChanged)
+ Q_PROPERTY(QList<QPolygonF> currentPageBoundingPolygons READ currentPageBoundingPolygons NOTIFY currentPageBoundingPolygonsChanged)
+ Q_PROPERTY(QList<QPolygonF> currentResultBoundingPolygons READ currentResultBoundingPolygons NOTIFY currentResultBoundingPolygonsChanged)
+ Q_PROPERTY(QRectF currentResultBoundingRect READ currentResultBoundingRect NOTIFY currentResultBoundingRectChanged)
+
+public:
+ explicit QQuickPdfSearchModel(QObject *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument * document);
+
+ Q_INVOKABLE QList<QPolygonF> boundingPolygonsOnPage(int page);
+
+ int currentPage() const { return m_currentPage; }
+ void setCurrentPage(int currentPage);
+
+ int currentResult() const { return m_currentResult; }
+ void setCurrentResult(int currentResult);
+
+ QList<QPolygonF> currentPageBoundingPolygons() const;
+ QList<QPolygonF> currentResultBoundingPolygons() const;
+ QRectF currentResultBoundingRect() const;
+
+signals:
+ void documentChanged();
+ void currentPageChanged();
+ void currentResultChanged();
+ void currentPageBoundingPolygonsChanged();
+ void currentResultBoundingPolygonsChanged();
+ void currentResultBoundingRectChanged();
+
+private:
+ void updateResults();
+ void onResultsChanged();
+
+private:
+ QQuickPdfDocument *m_quickDocument = nullptr;
+ int m_currentPage = 0;
+ int m_currentResult = 0;
+ bool m_suspendSignals = false;
+
+ Q_DISABLE_COPY(QQuickPdfSearchModel)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfSearchModel)
+QML_DECLARE_TYPE(QPdfSelection)
+
+#endif // QQUICKPDFSEARCHMODEL_P_H
diff --git a/src/pdfquick/qquickpdfselection.cpp b/src/pdfquick/qquickpdfselection.cpp
new file mode 100644
index 000000000..54a13828a
--- /dev/null
+++ b/src/pdfquick/qquickpdfselection.cpp
@@ -0,0 +1,543 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpdfselection_p.h"
+#include "qquickpdfdocument_p.h"
+#include <QClipboard>
+#include <QGuiApplication>
+#include <QLoggingCategory>
+#include <QQuickItem>
+#include <QQmlEngine>
+#include <QRegularExpression>
+#include <QStandardPaths>
+#include <QtPdf/private/qpdfdocument_p.h>
+
+Q_LOGGING_CATEGORY(qLcIm, "qt.pdf.im")
+
+QT_BEGIN_NAMESPACE
+
+static const QRegularExpression WordDelimiter(QStringLiteral("\\s"));
+
+/*!
+ \qmltype PdfSelection
+ \instantiates QQuickPdfSelection
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A representation of a text selection within a PDF Document.
+ \since 5.15
+
+ PdfSelection provides the text string and its geometry within a bounding box
+ from one point to another.
+
+ To modify the selection using the mouse, bind \l fromPoint and \l toPoint
+ to the suitable properties of an input handler so that they will be set to
+ the positions where the drag gesture begins and ends, respectively; and
+ bind the \l hold property so that it will be set to \c true during the drag
+ gesture and \c false when the gesture ends.
+
+ PdfSelection also directly handles Input Method queries so that text
+ selection handles can be used on platforms such as iOS. For this purpose,
+ it must have keyboard focus.
+*/
+
+/*!
+ Constructs a SearchModel.
+*/
+QQuickPdfSelection::QQuickPdfSelection(QQuickItem *parent)
+ : QQuickItem(parent)
+{
+#if QT_CONFIG(im)
+ setFlags(ItemIsFocusScope | ItemAcceptsInputMethod);
+ // workaround to get Copy instead of Paste on the popover menu (QTBUG-83811)
+ setProperty("qt_im_readonly", QVariant(true));
+#endif
+}
+
+QQuickPdfDocument *QQuickPdfSelection::document() const
+{
+ return m_document;
+}
+
+void QQuickPdfSelection::setDocument(QQuickPdfDocument *document)
+{
+ if (m_document == document)
+ return;
+
+ if (m_document) {
+ disconnect(m_document, &QQuickPdfDocument::sourceChanged,
+ this, &QQuickPdfSelection::resetPoints);
+ }
+ m_document = document;
+ emit documentChanged();
+ resetPoints();
+ connect(m_document, &QQuickPdfDocument::sourceChanged,
+ this, &QQuickPdfSelection::resetPoints);
+}
+
+/*!
+ \qmlproperty list<list<point>> PdfSelection::geometry
+
+ A set of paths in a form that can be bound to the \c paths property of a
+ \l {QtQuick::PathMultiline}{PathMultiline} instance to render a batch of
+ rectangles around the text regions that are included in the selection:
+
+ \qml
+ PdfDocument {
+ id: doc
+ }
+ PdfSelection {
+ id: selection
+ document: doc
+ fromPoint: textSelectionDrag.centroid.pressPosition
+ toPoint: textSelectionDrag.centroid.position
+ hold: !textSelectionDrag.active
+ }
+ Shape {
+ ShapePath {
+ PathMultiline {
+ paths: selection.geometry
+ }
+ }
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ \endqml
+
+ \sa PathMultiline
+*/
+QList<QPolygonF> QQuickPdfSelection::geometry() const
+{
+ return m_geometry;
+}
+
+void QQuickPdfSelection::clear()
+{
+ m_hitPoint = QPointF();
+ m_fromPoint = QPointF();
+ m_toPoint = QPointF();
+ m_heightAtAnchor = 0;
+ m_heightAtCursor = 0;
+ m_fromCharIndex = -1;
+ m_toCharIndex = -1;
+ m_text.clear();
+ m_geometry.clear();
+ emit fromPointChanged();
+ emit toPointChanged();
+ emit textChanged();
+ emit selectedAreaChanged();
+ QGuiApplication::inputMethod()->update(Qt::ImQueryInput);
+}
+
+void QQuickPdfSelection::selectAll()
+{
+ QPdfSelection sel = m_document->m_doc.getAllText(m_page);
+ if (sel.text() != m_text) {
+ m_text = sel.text();
+ if (QGuiApplication::clipboard()->supportsSelection())
+ sel.copyToClipboard(QClipboard::Selection);
+ emit textChanged();
+ }
+
+ if (sel.bounds() != m_geometry) {
+ m_geometry = sel.bounds();
+ emit selectedAreaChanged();
+ }
+#if QT_CONFIG(im)
+ m_fromCharIndex = sel.startIndex();
+ m_toCharIndex = sel.endIndex();
+ if (sel.bounds().isEmpty()) {
+ m_fromPoint = QPointF();
+ m_toPoint = QPointF();
+ } else {
+ m_fromPoint = sel.bounds().first().boundingRect().topLeft() * m_renderScale;
+ m_toPoint = sel.bounds().last().boundingRect().bottomRight() * m_renderScale - QPointF(0, m_heightAtCursor);
+ }
+
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
+#endif
+}
+
+#if QT_CONFIG(im)
+void QQuickPdfSelection::keyReleaseEvent(QKeyEvent *ev)
+{
+ qCDebug(qLcIm) << "release" << ev;
+ const auto &allText = pageText();
+ if (ev == QKeySequence::MoveToPreviousWord) {
+ // iOS sends MoveToPreviousWord first to get to the beginning of the word,
+ // and then SelectNextWord to select the whole word.
+ int i = allText.lastIndexOf(WordDelimiter, m_fromCharIndex - allText.length());
+ if (i < 0)
+ i = 0;
+ else
+ i += 1; // don't select the space before the word
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, i, m_text.length() + m_fromCharIndex - i);
+ update(sel);
+ QGuiApplication::inputMethod()->update(Qt::ImAnchorRectangle);
+ } else if (ev == QKeySequence::SelectNextWord) {
+ int i = allText.indexOf(WordDelimiter, m_toCharIndex);
+ if (i < 0)
+ i = allText.length(); // go to the end of m_textAfter
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, m_fromCharIndex, m_text.length() + i - m_toCharIndex);
+ update(sel);
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle);
+ } else if (ev == QKeySequence::Copy) {
+ copyToClipboard();
+ }
+}
+
+void QQuickPdfSelection::inputMethodEvent(QInputMethodEvent *event)
+{
+ for (auto attr : event->attributes()) {
+ switch (attr.type) {
+ case QInputMethodEvent::Cursor:
+ qCDebug(qLcIm) << "QInputMethodEvent::Cursor: moved to" << attr.start << "len" << attr.length;
+ break;
+ case QInputMethodEvent::Selection: {
+ auto sel = m_document->m_doc.getSelectionAtIndex(m_page, attr.start, attr.length);
+ update(sel);
+ qCDebug(qLcIm) << "QInputMethodEvent::Selection: from" << attr.start << "len" << attr.length
+ << "result:" << m_fromCharIndex << "->" << m_toCharIndex << sel.boundingRectangle();
+ // the iOS plugin decided that it wanted to change the selection, but still has to be told to move the handles (!?)
+ QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
+ break;
+ }
+ case QInputMethodEvent::Language:
+ case QInputMethodEvent::Ruby:
+ case QInputMethodEvent::TextFormat:
+ break;
+ }
+ }
+}
+
+QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const
+{
+ if (!argument.isNull()) {
+ qCDebug(qLcIm) << "IM query" << query << "with arg" << argument;
+ if (query == Qt::ImCursorPosition) {
+ // If it didn't move since last time, return the same result.
+ if (m_hitPoint == argument.toPointF())
+ return inputMethodQuery(query);
+ m_hitPoint = argument.toPointF();
+ auto tp = m_document->m_doc.d->hitTest(m_page, m_hitPoint / m_renderScale);
+ qCDebug(qLcIm) << "ImCursorPosition hit testing in px" << m_hitPoint << "pt" << (m_hitPoint / m_renderScale)
+ << "got char index" << tp.charIndex << "@" << tp.position << "pt," << tp.position * m_renderScale << "px";
+ if (tp.charIndex >= 0) {
+ m_toCharIndex = tp.charIndex;
+ m_toPoint = tp.position * m_renderScale - QPointF(0, m_heightAtCursor);
+ m_heightAtCursor = tp.height * m_renderScale;
+ if (qFuzzyIsNull(m_heightAtAnchor))
+ m_heightAtAnchor = m_heightAtCursor;
+ }
+ }
+ }
+ return inputMethodQuery(query);
+}
+
+QVariant QQuickPdfSelection::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ QVariant ret;
+ switch (query) {
+ case Qt::ImEnabled:
+ ret = true;
+ break;
+ case Qt::ImHints:
+ ret = QVariant(Qt::ImhMultiLine | Qt::ImhNoPredictiveText);
+ break;
+ case Qt::ImInputItemClipRectangle:
+ ret = boundingRect();
+ break;
+ case Qt::ImAnchorPosition:
+ ret = m_fromCharIndex;
+ break;
+ case Qt::ImAbsolutePosition:
+ ret = m_toCharIndex;
+ break;
+ case Qt::ImCursorPosition:
+ ret = m_toCharIndex;
+ break;
+ case Qt::ImAnchorRectangle:
+ ret = QRectF(m_fromPoint, QSizeF(1, m_heightAtAnchor));
+ break;
+ case Qt::ImCursorRectangle:
+ ret = QRectF(m_toPoint, QSizeF(1, m_heightAtCursor));
+ break;
+ case Qt::ImSurroundingText:
+ ret = QVariant(pageText());
+ break;
+ case Qt::ImTextBeforeCursor:
+ ret = QVariant(pageText().mid(0, m_toCharIndex));
+ break;
+ case Qt::ImTextAfterCursor:
+ ret = QVariant(pageText().mid(m_toCharIndex));
+ break;
+ case Qt::ImCurrentSelection:
+ ret = QVariant(m_text);
+ break;
+ case Qt::ImEnterKeyType:
+ break;
+ case Qt::ImFont: {
+ QFont font = QGuiApplication::font();
+ font.setPointSizeF(m_heightAtCursor);
+ ret = font;
+ break;
+ }
+ case Qt::ImMaximumTextLength:
+ break;
+ case Qt::ImPreferredLanguage:
+ break;
+ case Qt::ImPlatformData:
+ break;
+ case Qt::ImReadOnly:
+ break;
+ case Qt::ImQueryInput:
+ case Qt::ImQueryAll:
+ qWarning() << "unexpected composite query";
+ break;
+ }
+ qCDebug(qLcIm) << "IM query" << query << "returns" << ret;
+ return ret;
+}
+#endif // QT_CONFIG(im)
+
+const QString &QQuickPdfSelection::pageText() const
+{
+ if (m_pageTextDirty) {
+ m_pageText = m_document->m_doc.getAllText(m_page).text();
+ m_pageTextDirty = false;
+ }
+ return m_pageText;
+}
+
+void QQuickPdfSelection::resetPoints()
+{
+ bool wasHolding = m_hold;
+ m_hold = false;
+ setFromPoint(QPointF());
+ setToPoint(QPointF());
+ m_hold = wasHolding;
+}
+
+/*!
+ \qmlproperty int PdfSelection::page
+
+ The page number on which to search.
+
+ \sa QtQuick::Image::currentFrame
+*/
+int QQuickPdfSelection::page() const
+{
+ return m_page;
+}
+
+void QQuickPdfSelection::setPage(int page)
+{
+ if (m_page == page)
+ return;
+
+ m_page = page;
+ m_pageTextDirty = true;
+ emit pageChanged();
+ resetPoints();
+}
+
+/*!
+ \qmlproperty real PdfSelection::renderScale
+ \brief The ratio from points to pixels at which the page is rendered.
+
+ This is used to scale \l fromPoint and \l toPoint to find ranges of
+ selected characters in the document, because positions within the document
+ are always given in points.
+*/
+qreal QQuickPdfSelection::renderScale() const
+{
+ return m_renderScale;
+}
+
+void QQuickPdfSelection::setRenderScale(qreal scale)
+{
+ if (qFuzzyCompare(scale, m_renderScale))
+ return;
+
+ m_renderScale = scale;
+ emit renderScaleChanged();
+ updateResults();
+}
+
+/*!
+ \qmlproperty point PdfSelection::fromPoint
+
+ The beginning location, in pixels from the upper-left corner of the page,
+ from which to find selected text. This can be bound to the
+ \c centroid.pressPosition of a \l DragHandler to begin selecting text from
+ the position where the user presses the mouse button and begins dragging,
+ for example.
+*/
+QPointF QQuickPdfSelection::fromPoint() const
+{
+ return m_fromPoint;
+}
+
+void QQuickPdfSelection::setFromPoint(QPointF fromPoint)
+{
+ if (m_hold || m_fromPoint == fromPoint)
+ return;
+
+ m_fromPoint = fromPoint;
+ emit fromPointChanged();
+ updateResults();
+}
+
+/*!
+ \qmlproperty point PdfSelection::toPoint
+
+ The ending location, in pixels from the upper-left corner of the page,
+ from which to find selected text. This can be bound to the
+ \c centroid.position of a \l DragHandler to end selection of text at the
+ position where the user is currently dragging the mouse, for example.
+*/
+QPointF QQuickPdfSelection::toPoint() const
+{
+ return m_toPoint;
+}
+
+void QQuickPdfSelection::setToPoint(QPointF toPoint)
+{
+ if (m_hold || m_toPoint == toPoint)
+ return;
+
+ m_toPoint = toPoint;
+ emit toPointChanged();
+ updateResults();
+}
+
+/*!
+ \qmlproperty bool PdfSelection::hold
+
+ Controls whether to hold the existing selection regardless of changes to
+ \l fromPoint and \l toPoint. This property can be set to \c true when the mouse
+ or touchpoint is released, so that the selection is not lost due to the
+ point bindings changing.
+*/
+bool QQuickPdfSelection::hold() const
+{
+ return m_hold;
+}
+
+void QQuickPdfSelection::setHold(bool hold)
+{
+ if (m_hold == hold)
+ return;
+
+ m_hold = hold;
+ emit holdChanged();
+}
+
+/*!
+ \qmlproperty string PdfSelection::string
+
+ The string found.
+*/
+QString QQuickPdfSelection::text() const
+{
+ return m_text;
+}
+
+#if QT_CONFIG(clipboard)
+/*!
+ \qmlmethod void PdfSelection::copyToClipboard()
+
+ Copies plain text from the \l string property to the system clipboard.
+*/
+void QQuickPdfSelection::copyToClipboard() const
+{
+ QGuiApplication::clipboard()->setText(m_text);
+}
+#endif
+
+void QQuickPdfSelection::updateResults()
+{
+ if (!m_document)
+ return;
+ QPdfSelection sel = m_document->document().getSelection(m_page,
+ m_fromPoint / m_renderScale, m_toPoint / m_renderScale);
+ update(sel, true);
+}
+
+void QQuickPdfSelection::update(const QPdfSelection &sel, bool textAndGeometryOnly)
+{
+ if (sel.text() != m_text) {
+ m_text = sel.text();
+ if (QGuiApplication::clipboard()->supportsSelection())
+ sel.copyToClipboard(QClipboard::Selection);
+ emit textChanged();
+ }
+
+ if (sel.bounds() != m_geometry) {
+ m_geometry = sel.bounds();
+ emit selectedAreaChanged();
+ }
+
+ if (textAndGeometryOnly)
+ return;
+
+ m_fromCharIndex = sel.startIndex();
+ m_toCharIndex = sel.endIndex();
+ if (sel.bounds().isEmpty()) {
+ m_fromPoint = sel.boundingRectangle().topLeft() * m_renderScale;
+ m_toPoint = m_fromPoint;
+ } else {
+ Qt::InputMethodQueries toUpdate = {};
+ QRectF firstLineBounds = sel.bounds().first().boundingRect();
+ m_fromPoint = firstLineBounds.topLeft() * m_renderScale;
+ if (!qFuzzyCompare(m_heightAtAnchor, firstLineBounds.height())) {
+ m_heightAtAnchor = firstLineBounds.height() * m_renderScale;
+ toUpdate.setFlag(Qt::ImAnchorRectangle);
+ }
+ QRectF lastLineBounds = sel.bounds().last().boundingRect();
+ if (!qFuzzyCompare(m_heightAtCursor, lastLineBounds.height())) {
+ m_heightAtCursor = lastLineBounds.height() * m_renderScale;
+ toUpdate.setFlag(Qt::ImCursorRectangle);
+ }
+ m_toPoint = lastLineBounds.topRight() * m_renderScale;
+ if (toUpdate)
+ QGuiApplication::inputMethod()->update(toUpdate);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/pdfquick/qquickpdfselection_p.h b/src/pdfquick/qquickpdfselection_p.h
new file mode 100644
index 000000000..b364a6c03
--- /dev/null
+++ b/src/pdfquick/qquickpdfselection_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPDFSELECTION_P_H
+#define QQUICKPDFSELECTION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtPdfQuick/private/qtpdfquickglobal_p.h>
+#include <QtPdfQuick/private/qquickpdfdocument_p.h>
+#include <QPointF>
+#include <QPolygonF>
+#include <QVariant>
+#include <QtQml/qqml.h>
+#include <QtQuick/qquickitem.h>
+
+QT_BEGIN_NAMESPACE
+class QPdfSelection;
+
+class Q_PDFQUICK_EXPORT QQuickPdfSelection : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+ Q_PROPERTY(qreal renderScale READ renderScale WRITE setRenderScale NOTIFY renderScaleChanged)
+ Q_PROPERTY(QPointF fromPoint READ fromPoint WRITE setFromPoint NOTIFY fromPointChanged)
+ Q_PROPERTY(QPointF toPoint READ toPoint WRITE setToPoint NOTIFY toPointChanged)
+ Q_PROPERTY(bool hold READ hold WRITE setHold NOTIFY holdChanged)
+
+ Q_PROPERTY(QString text READ text NOTIFY textChanged)
+ Q_PROPERTY(QList<QPolygonF> geometry READ geometry NOTIFY selectedAreaChanged)
+
+public:
+ explicit QQuickPdfSelection(QQuickItem *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument * document);
+ int page() const;
+ void setPage(int page);
+ qreal renderScale() const;
+ void setRenderScale(qreal scale);
+ QPointF fromPoint() const;
+ void setFromPoint(QPointF fromPoint);
+ QPointF toPoint() const;
+ void setToPoint(QPointF toPoint);
+ bool hold() const;
+ void setHold(bool hold);
+
+ QString text() const;
+ QList<QPolygonF> geometry() const;
+
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void selectAll();
+#if QT_CONFIG(clipboard)
+ Q_INVOKABLE void copyToClipboard() const;
+#endif
+
+signals:
+ void documentChanged();
+ void pageChanged();
+ void renderScaleChanged();
+ void fromPointChanged();
+ void toPointChanged();
+ void holdChanged();
+ void textChanged();
+ void selectedAreaChanged();
+
+protected:
+#if QT_CONFIG(im)
+ void keyReleaseEvent(QKeyEvent *ev) override;
+ void inputMethodEvent(QInputMethodEvent *event) override;
+ Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const;
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const override;
+#endif
+
+private:
+ void resetPoints();
+ void updateResults();
+ void update(const QPdfSelection &sel, bool textAndGeometryOnly = false);
+ const QString &pageText() const;
+
+private:
+ QQuickPdfDocument *m_document = nullptr;
+ mutable QPointF m_hitPoint;
+ QPointF m_fromPoint;
+ mutable QPointF m_toPoint;
+ qreal m_renderScale = 1;
+ mutable qreal m_heightAtAnchor = 0;
+ mutable qreal m_heightAtCursor = 0;
+ QString m_text; // selected text
+ mutable QString m_pageText; // all text on the page
+ QList<QPolygonF> m_geometry;
+ int m_page = 0;
+ int m_fromCharIndex = -1; // same as anchor position
+ mutable int m_toCharIndex = -1; // same as cursor position
+ bool m_hold = false;
+ mutable bool m_pageTextDirty = true;
+
+ Q_DISABLE_COPY(QQuickPdfSelection)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfSelection)
+
+#endif // QQUICKPDFSELECTION_P_H
diff --git a/src/pdfquick/qquicktableviewextra.cpp b/src/pdfquick/qquicktableviewextra.cpp
new file mode 100644
index 000000000..017764a95
--- /dev/null
+++ b/src/pdfquick/qquicktableviewextra.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktableviewextra_p.h"
+#include <QtQml>
+#include <QQmlContext>
+
+Q_LOGGING_CATEGORY(qLcTVE, "qt.pdf.tableextra")
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \qmltype TableViewExtra
+ \instantiates QQuickTableViewExtra
+ \inqmlmodule QtQuick.Pdf
+ \ingroup pdf
+ \brief A helper class with missing TableView functions
+ \since 5.15
+
+ TableViewExtra provides equivalents for some functions that will be added
+ to TableView in Qt 6.
+*/
+
+QQuickTableViewExtra::QQuickTableViewExtra(QObject *parent) : QObject(parent)
+{
+}
+
+QPoint QQuickTableViewExtra::cellAtPos(qreal x, qreal y) const
+{
+ QPointF position(x, y);
+ return m_tableView->cellAtPos(position);
+}
+
+QQuickItem *QQuickTableViewExtra::itemAtCell(const QPoint &cell) const
+{
+ return m_tableView->itemAtCell(cell);
+}
+
+void QQuickTableViewExtra::positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset)
+{
+ m_tableView->positionViewAtCell(cell, alignment, offset);
+}
+
+QT_END_NAMESPACE
diff --git a/src/pdfquick/qquicktableviewextra_p.h b/src/pdfquick/qquicktableviewextra_p.h
new file mode 100644
index 000000000..30eed696d
--- /dev/null
+++ b/src/pdfquick/qquicktableviewextra_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtPDF module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEWEXTRA_P_H
+#define QQUICKTABLEVIEWEXTRA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtPdfQuick/private/qtpdfquickglobal_p.h>
+#include <QtQuick/private/qquicktableview_p.h>
+#include <QPointF>
+#include <QPolygonF>
+#include <QVariant>
+#include <QtQml/qqml.h>
+#include <QtQuick/qquickitem.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_PDFQUICK_EXPORT QQuickTableViewExtra : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickTableView *tableView READ tableView WRITE setTableView)
+
+public:
+ QQuickTableViewExtra(QObject *parent = nullptr);
+
+ QQuickTableView * tableView() const { return m_tableView; }
+ void setTableView(QQuickTableView * tableView) { m_tableView = tableView; }
+
+ Q_INVOKABLE QPoint cellAtPos(qreal x, qreal y) const;
+ Q_INVOKABLE QQuickItem *itemAtCell(int column, int row) const {
+ return itemAtCell(QPoint(column, row));
+ }
+ Q_INVOKABLE QQuickItem *itemAtCell(const QPoint &cell) const;
+ Q_INVOKABLE void positionViewAtCell(int column, int row, Qt::Alignment alignment, const QPointF &offset = QPointF()) {
+ positionViewAtCell(QPoint(column, row), alignment, offset);
+ }
+ Q_INVOKABLE void positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset);
+ Q_INVOKABLE void positionViewAtRow(int row, Qt::Alignment alignment, qreal offset = 0) {
+ positionViewAtCell(QPoint(0, row), alignment & Qt::AlignVertical_Mask, QPointF(0, offset));
+ }
+
+private:
+ QQuickTableView *m_tableView = nullptr;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTableViewExtra)
+
+#endif // QQUICKTABLEVIEWEXTRA_P_H
diff --git a/src/pdfquick/qtpdfquickglobal_p.h b/src/pdfquick/qtpdfquickglobal_p.h
new file mode 100644
index 000000000..226a82c28
--- /dev/null
+++ b/src/pdfquick/qtpdfquickglobal_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTPDFQUICKGLOBAL_H
+#define QTPDFQUICKGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_STATIC
+# if defined(QT_BUILD_PDFQUICK_LIB)
+# define Q_PDFQUICK_EXPORT Q_DECL_EXPORT
+# else
+# define Q_PDFQUICK_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_PDFQUICK_EXPORT
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QTPDFQUICKGLOBAL_H