summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-01-20 15:12:01 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2020-02-03 23:44:16 +0100
commitbf3133033236afb34974fec63ac21e1749d503ad (patch)
treecfb48f3cfb430c4cb1f93a5af23d2ccd3ca1c84d
parent7cf69cb52d434f5e74619b0577104d05688b0c22 (diff)
Add PdfMultiPageView
So far it's a ListView with a page per delegate. Many features are working, but zooming and rotation are not working yet. Change-Id: I9ee7aa60ad4411bd8734fe2cd987a68906a5cf57 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r--examples/pdf/multipage/main.cpp69
-rw-r--r--examples/pdf/multipage/multipage.pro14
-rw-r--r--examples/pdf/multipage/resources/document-open.svg13
-rw-r--r--examples/pdf/multipage/resources/edit-clear.svg15
-rw-r--r--examples/pdf/multipage/resources/edit-copy.svg15
-rw-r--r--examples/pdf/multipage/resources/go-next-view-page.svg13
-rw-r--r--examples/pdf/multipage/resources/go-previous-view-page.svg13
-rw-r--r--examples/pdf/multipage/resources/rotate-left.svg6
-rw-r--r--examples/pdf/multipage/resources/rotate-right.svg6
-rw-r--r--examples/pdf/multipage/resources/zoom-fit-best.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-fit-width.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-in.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-original.svg13
-rw-r--r--examples/pdf/multipage/resources/zoom-out.svg13
-rw-r--r--examples/pdf/multipage/viewer.qml276
-rw-r--r--examples/pdf/multipage/viewer.qrc17
-rw-r--r--src/pdf/quick/plugin.cpp1
-rw-r--r--src/pdf/quick/qml/PdfMultiPageView.qml180
-rw-r--r--src/pdf/quick/quick.pro1
-rw-r--r--src/pdf/quick/resources.qrc1
20 files changed, 705 insertions, 0 deletions
diff --git a/examples/pdf/multipage/main.cpp b/examples/pdf/multipage/main.cpp
new file mode 100644
index 000000000..451521f80
--- /dev/null
+++ b/examples/pdf/multipage/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QApplication app(argc, argv);
+ QCoreApplication::setApplicationName("Qt Quick Multi-page PDF Viewer Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:///pdfviewer/viewer.qml")));
+ if (app.arguments().count() > 1) {
+ QUrl toLoad = QUrl::fromUserInput(app.arguments().at(1));
+ engine.rootObjects().first()->setProperty("source", toLoad);
+ }
+
+ return app.exec();
+}
diff --git a/examples/pdf/multipage/multipage.pro b/examples/pdf/multipage/multipage.pro
new file mode 100644
index 000000000..5fff58fe1
--- /dev/null
+++ b/examples/pdf/multipage/multipage.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+QT += qml quick pdf widgets
+
+SOURCES += main.cpp
+
+RESOURCES += \
+ viewer.qrc
+EXAMPLE_FILES = \
+ viewer.qml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/pdf/multipage
+INSTALLS += target
+
diff --git a/examples/pdf/multipage/resources/document-open.svg b/examples/pdf/multipage/resources/document-open.svg
new file mode 100644
index 000000000..bf23123a3
--- /dev/null
+++ b/examples/pdf/multipage/resources/document-open.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="m4 4v24h24l-1-1h-22v-13h5l3-3h14v16l1 1v-21h-10l-3-3z"
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/edit-clear.svg b/examples/pdf/multipage/resources/edit-clear.svg
new file mode 100644
index 000000000..1c35aaf04
--- /dev/null
+++ b/examples/pdf/multipage/resources/edit-clear.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 8 3 L 0.94335938 10.056641 L 0 11 L 0.94335938 11.943359 L 8 19 L 20.333984 19 L 22 19 L 22 3 L 20.333984 3 L 8 3 z M 11.320312 7 L 14 9.6796875 L 16.679688 7 L 18 8.3203125 L 15.320312 11 L 18 13.679688 L 16.679688 15 L 14 12.320312 L 11.320312 15 L 10 13.679688 L 12.679688 11 L 10 8.3203125 L 11.320312 7 z "
+ class="ColorScheme-Text"
+ transform="translate(1,1)"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/edit-copy.svg b/examples/pdf/multipage/resources/edit-copy.svg
new file mode 100644
index 000000000..9dd16877d
--- /dev/null
+++ b/examples/pdf/multipage/resources/edit-copy.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="m4 3v1 13h1 2 1v1 1h6l4-4v-1-7-1h-2v-3h-1-10-1m1 1h10v2h-7v1 9h-1-2v-12m4 3h8v7h-3-1v1 3h-4v-11"
+ class="ColorScheme-Text"
+ transform="translate(1,1)"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/go-next-view-page.svg b/examples/pdf/multipage/resources/go-next-view-page.svg
new file mode 100644
index 000000000..e453ddbec
--- /dev/null
+++ b/examples/pdf/multipage/resources/go-next-view-page.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 8.7070312 4 L 8 4.7070312 L 14.125 10.832031 L 15.292969 12 L 14.125 13.167969 L 8 19.292969 L 8.7070312 20 L 14.832031 13.875 L 16.707031 12 L 14.832031 10.125 L 8.7070312 4 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/go-previous-view-page.svg b/examples/pdf/multipage/resources/go-previous-view-page.svg
new file mode 100644
index 000000000..b032309e9
--- /dev/null
+++ b/examples/pdf/multipage/resources/go-previous-view-page.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 15.292969 4 L 9.1679688 10.125 L 7.2929688 12 L 9.1679688 13.875 L 15.292969 20 L 16 19.292969 L 9.875 13.167969 L 8.7070312 12 L 9.875 10.832031 L 16 4.7070312 L 15.292969 4 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/rotate-left.svg b/examples/pdf/multipage/resources/rotate-left.svg
new file mode 100644
index 000000000..90ce53c9d
--- /dev/null
+++ b/examples/pdf/multipage/resources/rotate-left.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g color="#000" font-weight="400" fill="#474747">
+ <path d="M2 9v1c0 .265.093.53.281.719l3.72 3.719 3.718-3.72c.188-.187.281-.453.281-.718V9H9c-.265 0-.53.093-.719.281l-2.28 2.281-2.282-2.28A1.015 1.015 0 0 0 3 9z"/>
+ <path d="M8.5 3A3.515 3.515 0 0 0 5 6.5V12h2V6.5C7 5.66 7.66 5 8.5 5H13V3z"/>
+ </g>
+</svg>
diff --git a/examples/pdf/multipage/resources/rotate-right.svg b/examples/pdf/multipage/resources/rotate-right.svg
new file mode 100644
index 000000000..7383d1c84
--- /dev/null
+++ b/examples/pdf/multipage/resources/rotate-right.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g font-weight="400" fill="#474747">
+ <path d="M3 3v2h4.5C8.34 5 9 5.66 9 6.5V12h2V6.5C11 4.579 9.421 3 7.5 3z"/>
+ <path d="M6 9h1c.257 0 .529.13.719.313L10 11.592l2.281-2.28C12.471 9.13 12.743 9 13 9h1v1c0 .31-.09.552-.281.75L10 14.406 6.281 10.75C6.091 10.552 6 10.31 6 10z"/>
+ </g>
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-fit-best.svg b/examples/pdf/multipage/resources/zoom-fit-best.svg
new file mode 100644
index 000000000..adf302621
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-fit-best.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#4d4d4d;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 5 L 4 8 L 5 8 L 5 5 L 8 5 L 8 4 L 5 4 L 4 4 z M 12 4 L 10 6 L 14 6 L 12 4 z M 16 4 L 16 5 L 19 5 L 19 8 L 20 8 L 20 5 L 20 4 L 19 4 L 16 4 z M 7 7 L 7 17 L 17 17 L 17 7 L 7 7 z M 8 8 L 16 8 L 16 16 L 8 16 L 8 8 z M 6 10 L 4 12 L 6 14 L 6 10 z M 18 10 L 18 14 L 20 12 L 18 10 z M 4 16 L 4 19 L 4 20 L 8 20 L 8 19 L 5 19 L 5 16 L 4 16 z M 19 16 L 19 19 L 16 19 L 16 20 L 20 20 L 20 19 L 20 16 L 19 16 z M 10 18 L 12 20 L 14 18 L 10 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-fit-width.svg b/examples/pdf/multipage/resources/zoom-fit-width.svg
new file mode 100644
index 000000000..985ee5205
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-fit-width.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#4d4d4d;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 7 7 L 7 17 L 17 17 L 17 7 L 7 7 z M 8 8 L 16 8 L 16 16 L 8 16 L 8 8 z M 6 10 L 4 12 L 6 14 L 6 10 z M 18 10 L 18 14 L 20 12 L 18 10 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-in.svg b/examples/pdf/multipage/resources/zoom-in.svg
new file mode 100644
index 000000000..efdc9f17d
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-in.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 6 L 5 6 L 5 5 L 6 5 L 6 4 L 4 4 z M 9 4 L 9 5 L 11 5 L 11 4 L 9 4 z M 13 4 L 13 5 L 15 5 L 15 4 L 13 4 z M 18 4 L 18 5 L 19 5 L 19 6 L 20 6 L 20 4 L 18 4 z M 12 8 L 12 9 L 14.292969 9 L 11 12.292969 L 11.707031 13 L 15 9.7070312 L 15 12 L 16 12 L 16 8 L 15 8 L 12 8 z M 4 9 L 4 11 L 5 11 L 5 9 L 4 9 z M 19 9 L 19 11 L 20 11 L 20 9 L 19 9 z M 19 13 L 19 15 L 20 15 L 20 13 L 19 13 z M 4 14 L 4 20 L 10 20 L 10 14 L 4 14 z M 5 15 L 9 15 L 9 19 L 5 19 L 5 15 z M 19 18 L 19 19 L 18 19 L 18 20 L 20 20 L 20 18 L 19 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-original.svg b/examples/pdf/multipage/resources/zoom-original.svg
new file mode 100644
index 000000000..1b4080a03
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-original.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 5 L 4 7 L 5 7 L 5 5 L 7 5 L 7 4 L 5 4 L 4 4 z M 17 4 L 17 5 L 19 5 L 19 7 L 20 7 L 20 5 L 20 4 L 19 4 L 17 4 z M 6 6 L 6 18 L 18 18 L 18 6 L 6 6 z M 7 7 L 17 7 L 17 17 L 7 17 L 7 7 z M 4 17 L 4 19 L 4 20 L 7 20 L 7 19 L 5 19 L 5 17 L 4 17 z M 19 17 L 19 19 L 17 19 L 17 20 L 20 20 L 20 17 L 19 17 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/resources/zoom-out.svg b/examples/pdf/multipage/resources/zoom-out.svg
new file mode 100644
index 000000000..fcde9e526
--- /dev/null
+++ b/examples/pdf/multipage/resources/zoom-out.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <path style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4 4 L 4 11 L 5 11 L 5 5 L 19 5 L 19 19 L 13 19 L 13 20 L 20 20 L 20 19 L 20 5 L 20 4 L 5 4 L 4 4 z M 15.292969 8 L 12 11.292969 L 12 9 L 11 9 L 11 13 L 12 13 L 15 13 L 15 12 L 12.707031 12 L 16 8.7070312 L 15.292969 8 z M 4 14 L 4 16 L 5 16 L 5 15 L 6 15 L 6 14 L 4 14 z M 8 14 L 8 15 L 9 15 L 9 16 L 10 16 L 10 14 L 8 14 z M 4 18 L 4 20 L 6 20 L 6 19 L 5 19 L 5 18 L 4 18 z M 9 18 L 9 19 L 8 19 L 8 20 L 10 20 L 10 18 L 9 18 z "
+ class="ColorScheme-Text"
+ />
+</svg>
diff --git a/examples/pdf/multipage/viewer.qml b/examples/pdf/multipage/viewer.qml
new file mode 100644
index 000000000..d20ad4a5b
--- /dev/null
+++ b/examples/pdf/multipage/viewer.qml
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $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
+import Qt.labs.platform 1.1 as Platform
+
+ApplicationWindow {
+ id: root
+ width: 800
+ height: 1024
+ color: "lightgrey"
+ title: document.title
+ visible: true
+ property alias source: document.source // for main.cpp
+
+ header: ToolBar {
+ RowLayout {
+ anchors.fill: parent
+ anchors.rightMargin: 6
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.Open
+ icon.source: "resources/document-open.svg"
+ onTriggered: fileDialog.open()
+ }
+ }
+ /* TODO zoom & rotation
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.ZoomIn
+ enabled: view.sourceSize.width < 10000
+ icon.source: "resources/zoom-in.svg"
+ onTriggered: view.renderScale *= Math.sqrt(2)
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.ZoomOut
+ enabled: view.sourceSize.width > 50
+ icon.source: "resources/zoom-out.svg"
+ onTriggered: view.renderScale /= Math.sqrt(2)
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/zoom-fit-width.svg"
+ onTriggered: view.scaleToWidth(root.contentItem.width, root.contentItem.height)
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/zoom-fit-best.svg"
+ onTriggered: view.scaleToPage(root.contentItem.width, root.contentItem.height)
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+0"
+ icon.source: "resources/zoom-original.svg"
+ onTriggered: view.resetScale()
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+L"
+ icon.source: "resources/rotate-left.svg"
+ onTriggered: view.rotation -= 90
+ }
+ }
+ ToolButton {
+ action: Action {
+ shortcut: "Ctrl+R"
+ icon.source: "resources/rotate-right.svg"
+ onTriggered: view.rotation += 90
+ }
+ }
+ */
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-previous-view-page.svg"
+ enabled: view.backEnbled
+ onTriggered: view.back()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "go back"
+ }
+ SpinBox {
+ id: currentPageSB
+ from: 1
+ to: document.pageCount
+ editable: true
+ onValueChanged: view.currentPage = value - 1
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ onActivated: currentPageSB.value--
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ onActivated: currentPageSB.value++
+ }
+ }
+ ToolButton {
+ action: Action {
+ icon.source: "resources/go-next-view-page.svg"
+ enabled: view.forwardEnabled
+ onTriggered: view.forward()
+ }
+ ToolTip.visible: enabled && hovered
+ ToolTip.delay: 2000
+ ToolTip.text: "go forward"
+ }
+ ToolButton {
+ action: Action {
+ shortcut: StandardKey.Copy
+ icon.source: "resources/edit-copy.svg"
+ enabled: view.selectedText !== ""
+ onTriggered: view.copySelectionToClipboard()
+ }
+ }
+ TextField {
+ id: searchField
+ placeholderText: "search"
+ Layout.minimumWidth: 200
+ Layout.fillWidth: true
+ Image {
+ visible: searchField.text !== ""
+ source: "resources/edit-clear.svg"
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ margins: 3
+ rightMargin: 5
+ }
+ TapHandler {
+ onTapped: searchField.clear()
+ }
+ }
+ }
+ Shortcut {
+ sequence: StandardKey.Find
+ onActivated: searchField.forceActiveFocus()
+ }
+ Shortcut {
+ sequence: StandardKey.Quit
+ onActivated: Qt.quit()
+ }
+ }
+ }
+
+ Platform.FileDialog {
+ id: fileDialog
+ title: "Open a PDF file"
+ nameFilters: [ "PDF files (*.pdf)" ]
+ onAccepted: document.source = file
+ }
+
+ Dialog {
+ id: passwordDialog
+ title: "Password"
+ standardButtons: Dialog.Ok | Dialog.Cancel
+ modal: true
+ closePolicy: Popup.CloseOnEscape
+ anchors.centerIn: parent
+ width: 300
+
+ TextField {
+ id: passwordField
+ placeholderText: qsTr("Please provide the password")
+ echoMode: TextInput.Password
+ width: parent.width
+ onAccepted: passwordDialog.accept()
+ }
+ onAccepted: document.password = passwordField.text
+ }
+
+ Dialog {
+ id: errorDialog
+ title: "Error loading " + document.source
+ standardButtons: Dialog.Ok
+ modal: true
+ closePolicy: Popup.CloseOnEscape
+ anchors.centerIn: parent
+ width: 300
+
+ Label {
+ id: errorField
+ text: document.error
+ }
+ }
+
+ PdfDocument {
+ id: document
+ onStatusChanged: {
+ if (status === PdfDocument.Error) errorDialog.open()
+ view.document = (status === PdfDocument.Ready ? document : undefined)
+ }
+ onPasswordRequired: {
+ passwordDialog.open()
+ passwordField.forceActiveFocus()
+ }
+ }
+
+ PdfMultiPageView {
+ id: view
+ anchors.fill: parent
+ document: root.document
+ searchString: searchField.text
+ onCurrentPageReallyChanged: currentPageSB.value = page + 1
+ }
+
+ footer: ToolBar {
+ height: statusLabel.implicitHeight * 1.5
+ Label {
+ id: statusLabel
+ anchors.verticalCenter: parent.verticalCenter
+ x: 6
+ property size implicitPointSize: document.pagePointSize(view.currentPage)
+ text: "page " + (currentPageSB.value) + " of " + document.pageCount +
+ " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1)
+ visible: document.pageCount > 0
+ }
+ }
+}
diff --git a/examples/pdf/multipage/viewer.qrc b/examples/pdf/multipage/viewer.qrc
new file mode 100644
index 000000000..fa3561caf
--- /dev/null
+++ b/examples/pdf/multipage/viewer.qrc
@@ -0,0 +1,17 @@
+<RCC>
+ <qresource prefix="/pdfviewer">
+ <file>viewer.qml</file>
+ <file>resources/edit-clear.svg</file>
+ <file>resources/edit-copy.svg</file>
+ <file>resources/go-next-view-page.svg</file>
+ <file>resources/go-previous-view-page.svg</file>
+ <file>resources/rotate-left.svg</file>
+ <file>resources/rotate-right.svg</file>
+ <file>resources/zoom-in.svg</file>
+ <file>resources/zoom-fit-best.svg</file>
+ <file>resources/zoom-fit-width.svg</file>
+ <file>resources/zoom-original.svg</file>
+ <file>resources/zoom-out.svg</file>
+ <file>resources/document-open.svg</file>
+ </qresource>
+</RCC>
diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp
index 3c8077ff2..a831a09b6 100644
--- a/src/pdf/quick/plugin.cpp
+++ b/src/pdf/quick/plugin.cpp
@@ -89,6 +89,7 @@ public:
qmlRegisterType<QQuickPdfSelection>(uri, 5, 15, "PdfSelection");
qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfPageView.qml"), uri, 5, 15, "PdfPageView");
+ qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml"), uri, 5, 15, "PdfMultiPageView");
}
};
diff --git a/src/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml
new file mode 100644
index 000000000..36b194812
--- /dev/null
+++ b/src/pdf/quick/qml/PdfMultiPageView.qml
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+import QtQuick.Pdf 5.15
+import QtQuick.Shapes 1.15
+import QtQuick.Window 2.15
+
+Item {
+ // public API
+ // TODO 5.15: required property
+ property var document: undefined
+ property real renderScale: 1
+ property string searchString
+ property string selectedText
+ property alias currentPage: listView.currentIndex
+ function copySelectionToClipboard() {
+ if (listView.currentItem !== null)
+ listView.currentItem.selection.copyToClipboard()
+ }
+ property alias backEnabled: navigationStack.backAvailable
+ property alias forwardEnabled: navigationStack.forwardAvailable
+ function back() { navigationStack.back() }
+ function forward() { navigationStack.forward() }
+ signal currentPageReallyChanged(page: int)
+
+ id: root
+ ListView {
+ id: listView
+ anchors.fill: parent
+ model: root.document === undefined ? 0 : root.document.pageCount
+ spacing: 6
+ highlightRangeMode: ListView.ApplyRange
+ highlightMoveVelocity: 2000 // TODO increase velocity when setting currentIndex somehow, too
+ onCurrentIndexChanged: {
+ navigationStack.currentPage = currentIndex
+ root.currentPageReallyChanged(currentIndex)
+ }
+ delegate: Rectangle {
+ id: paper
+ width: image.width
+ height: image.height
+ property alias selection: selection
+ property real __pageScale: image.paintedWidth / document.pagePointSize(index).width
+ Image {
+ id: image
+ source: document.source
+ currentFrame: index
+ asynchronous: true
+ fillMode: Image.PreserveAspectFit
+ width: document.pagePointSize(currentFrame).width
+ height: document.pagePointSize(currentFrame).height
+ }
+ Shape {
+ anchors.fill: parent
+ opacity: 0.25
+ visible: image.status === Image.Ready
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "blue"
+ fillColor: "cyan"
+ scale: Qt.size(paper.__pageScale, paper.__pageScale)
+ PathMultiline {
+ id: searchResultBoundaries
+ paths: searchModel.matchGeometry
+ }
+ }
+ ShapePath {
+ fillColor: "orange"
+ scale: Qt.size(paper.__pageScale, paper.__pageScale)
+ PathMultiline {
+ id: selectionBoundaries
+ paths: selection.geometry
+ }
+ }
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: root.document
+ page: image.currentFrame
+ searchString: root.searchString
+ }
+ PdfSelection {
+ id: selection
+ document: root.document
+ page: image.currentFrame
+ fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / paper.__pageScale, textSelectionDrag.centroid.pressPosition.y / paper.__pageScale)
+ toPoint: Qt.point(textSelectionDrag.centroid.position.x / paper.__pageScale, textSelectionDrag.centroid.position.y / paper.__pageScale)
+ hold: !textSelectionDrag.active && !tapHandler.pressed
+ onTextChanged: root.selectedText = text
+ }
+ DragHandler {
+ id: textSelectionDrag
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ target: null
+ }
+ TapHandler {
+ id: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ Repeater {
+ model: PdfLinkModel {
+ id: linkModel
+ document: root.document
+ page: image.currentFrame
+ }
+ delegate: Rectangle {
+ color: "transparent"
+ border.color: "lightgrey"
+ x: rect.x * paper.__pageScale
+ y: rect.y * paper.__pageScale
+ width: rect.width * paper.__pageScale
+ height: rect.height * paper.__pageScale
+ HoverHandler { cursorShape: Qt.PointingHandCursor } // 5.15 only (QTBUG-68073)
+ TapHandler {
+ onTapped: {
+ if (page >= 0)
+ listView.currentIndex = page
+ else
+ Qt.openUrlExternally(url)
+ }
+ }
+ }
+ }
+ }
+ }
+ PdfNavigationStack {
+ id: navigationStack
+ onCurrentPageJumped: listView.currentIndex = page
+ onCurrentPageChanged: root.currentPageReallyChanged(navigationStack.currentPage)
+ }
+}
diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro
index a3bdfb45e..a0a39d414 100644
--- a/src/pdf/quick/quick.pro
+++ b/src/pdf/quick/quick.pro
@@ -6,6 +6,7 @@ IMPORT_VERSION = 1.0
#QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf
PDF_QML_FILES = \
+ qml/PdfMultiPageView.qml \
qml/PdfPageView.qml \
QML_FILES += $$PDF_QML_FILES qmldir
diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc
index a3f34189c..282610d4c 100644
--- a/src/pdf/quick/resources.qrc
+++ b/src/pdf/quick/resources.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/qt-project.org/qtpdf">
+ <file>qml/PdfMultiPageView.qml</file>
<file>qml/PdfPageView.qml</file>
</qresource>
</RCC>