summaryrefslogtreecommitdiffstats
path: root/src/pdf
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdf')
-rw-r--r--src/pdf/api/qpdfbookmarkmodel.h98
-rw-r--r--src/pdf/api/qpdfdestination.h85
-rw-r--r--src/pdf/api/qpdfdestination_p.h71
-rw-r--r--src/pdf/api/qpdfdocument.h138
-rw-r--r--src/pdf/api/qpdfdocument_p.h113
-rw-r--r--src/pdf/api/qpdfdocumentrenderoptions.h94
-rw-r--r--src/pdf/api/qpdflinkmodel_p.h106
-rw-r--r--src/pdf/api/qpdflinkmodel_p_p.h91
-rw-r--r--src/pdf/api/qpdfnamespace.h73
-rw-r--r--src/pdf/api/qpdfpagenavigation.h92
-rw-r--r--src/pdf/api/qpdfpagerenderer.h92
-rw-r--r--src/pdf/api/qpdfsearchmodel.h98
-rw-r--r--src/pdf/api/qpdfsearchmodel_p.h86
-rw-r--r--src/pdf/api/qpdfsearchresult.h76
-rw-r--r--src/pdf/api/qpdfsearchresult_p.h72
-rw-r--r--src/pdf/api/qpdfselection.h85
-rw-r--r--src/pdf/api/qpdfselection_p.h59
-rw-r--r--src/pdf/api/qtpdfglobal.h59
-rw-r--r--src/pdf/configure.json64
-rw-r--r--src/pdf/doc/qtpdf.qdocconf58
-rw-r--r--src/pdf/doc/snippets/qtpdf-build.cmake2
-rw-r--r--src/pdf/doc/snippets/qtpdf_build_snippet.qdoc35
-rw-r--r--src/pdf/doc/src/qtpdf-examples.qdoc37
-rw-r--r--src/pdf/doc/src/qtpdf-index.qdoc76
-rw-r--r--src/pdf/doc/src/qtpdf-module.qdoc50
-rw-r--r--src/pdf/gn_run.pro70
-rw-r--r--src/pdf/jsbridge.cpp38
-rw-r--r--src/pdf/pdf.pro16
-rw-r--r--src/pdf/pdfcore.pro93
-rw-r--r--src/pdf/pdfcore_generator.pro15
-rw-r--r--src/pdf/qpdfbookmarkmodel.cpp363
-rw-r--r--src/pdf/qpdfdestination.cpp145
-rw-r--r--src/pdf/qpdfdocument.cpp803
-rw-r--r--src/pdf/qpdfdocumentrenderoptions.qdoc139
-rw-r--r--src/pdf/qpdflinkmodel.cpp251
-rw-r--r--src/pdf/qpdfnamespace.qdoc74
-rw-r--r--src/pdf/qpdfpagenavigation.cpp314
-rw-r--r--src/pdf/qpdfpagerenderer.cpp361
-rw-r--r--src/pdf/qpdfsearchmodel.cpp324
-rw-r--r--src/pdf/qpdfsearchresult.cpp80
-rw-r--r--src/pdf/qpdfselection.cpp149
-rw-r--r--src/pdf/qtpdf.gni7
-rw-r--r--src/pdf/quick/plugin.cpp101
-rw-r--r--src/pdf/quick/plugins.qmltypes52
-rw-r--r--src/pdf/quick/qml/+material/PdfStyle.qml54
-rw-r--r--src/pdf/quick/qml/+universal/PdfStyle.qml55
-rw-r--r--src/pdf/quick/qml/PdfMultiPageView.qml379
-rw-r--r--src/pdf/quick/qml/PdfPageView.qml276
-rw-r--r--src/pdf/quick/qml/PdfScrollablePageView.qml290
-rw-r--r--src/pdf/quick/qml/PdfStyle.qml54
-rw-r--r--src/pdf/quick/qmldir4
-rw-r--r--src/pdf/quick/qquickpdfdocument.cpp306
-rw-r--r--src/pdf/quick/qquickpdfdocument_p.h137
-rw-r--r--src/pdf/quick/qquickpdflinkmodel.cpp132
-rw-r--r--src/pdf/quick/qquickpdflinkmodel_p.h87
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack.cpp266
-rw-r--r--src/pdf/quick/qquickpdfnavigationstack_p.h102
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel.cpp277
-rw-r--r--src/pdf/quick/qquickpdfsearchmodel_p.h110
-rw-r--r--src/pdf/quick/qquickpdfselection.cpp284
-rw-r--r--src/pdf/quick/qquickpdfselection_p.h123
-rw-r--r--src/pdf/quick/quick.pro34
-rw-r--r--src/pdf/quick/resources.qrc10
63 files changed, 8285 insertions, 0 deletions
diff --git a/src/pdf/api/qpdfbookmarkmodel.h b/src/pdf/api/qpdfbookmarkmodel.h
new file mode 100644
index 000000000..8e06a1547
--- /dev/null
+++ b/src/pdf/api/qpdfbookmarkmodel.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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 QPDFBOOKMARKMODEL_H
+#define QPDFBOOKMARKMODEL_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QtCore/qabstractitemmodel.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocument;
+class QPdfBookmarkModelPrivate;
+
+class Q_PDF_EXPORT QPdfBookmarkModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(StructureMode structureMode READ structureMode WRITE setStructureMode NOTIFY structureModeChanged)
+
+public:
+ enum StructureMode
+ {
+ TreeMode,
+ ListMode
+ };
+ Q_ENUM(StructureMode)
+
+ enum Role
+ {
+ TitleRole = Qt::DisplayRole,
+ LevelRole = Qt::UserRole,
+ PageNumberRole
+ };
+ Q_ENUM(Role)
+
+ explicit QPdfBookmarkModel(QObject *parent = nullptr);
+
+ QPdfDocument* document() const;
+ void setDocument(QPdfDocument *document);
+
+ StructureMode structureMode() const;
+ void setStructureMode(StructureMode mode);
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &index) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+Q_SIGNALS:
+ void documentChanged(QPdfDocument *document);
+ void structureModeChanged(QPdfBookmarkModel::StructureMode structureMode);
+
+private:
+ Q_DECLARE_PRIVATE(QPdfBookmarkModel)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_documentStatusChanged())
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/pdf/api/qpdfdestination.h b/src/pdf/api/qpdfdestination.h
new file mode 100644
index 000000000..f9c186ff6
--- /dev/null
+++ b/src/pdf/api/qpdfdestination.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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 QPDFDESTINATION_H
+#define QPDFDESTINATION_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDestinationPrivate;
+
+class Q_PDF_EXPORT QPdfDestination
+{
+ Q_GADGET
+ Q_PROPERTY(bool valid READ isValid)
+ Q_PROPERTY(int page READ page)
+ Q_PROPERTY(QPointF location READ location)
+ Q_PROPERTY(qreal zoom READ zoom)
+
+public:
+ ~QPdfDestination();
+ QPdfDestination(const QPdfDestination &other);
+ QPdfDestination &operator=(const QPdfDestination &other);
+ QPdfDestination(QPdfDestination &&other) noexcept;
+ QPdfDestination &operator=(QPdfDestination &&other) noexcept { swap(other); return *this; }
+ void swap(QPdfDestination &other) noexcept { d.swap(other.d); }
+ bool isValid() const;
+ int page() const;
+ QPointF location() const;
+ qreal zoom() const;
+
+protected:
+ QPdfDestination();
+ QPdfDestination(int page, QPointF location, qreal zoom);
+ QPdfDestination(QPdfDestinationPrivate *d);
+ friend class QPdfDocument;
+ friend class QQuickPdfNavigationStack;
+
+protected:
+ QExplicitlySharedDataPointer<QPdfDestinationPrivate> d;
+};
+
+Q_PDF_EXPORT QDebug operator<<(QDebug, const QPdfDestination &);
+
+QT_END_NAMESPACE
+
+#endif // QPDFDESTINATION_H
diff --git a/src/pdf/api/qpdfdestination_p.h b/src/pdf/api/qpdfdestination_p.h
new file mode 100644
index 000000000..3520fb795
--- /dev/null
+++ b/src/pdf/api/qpdfdestination_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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 QPDFDESTINATION_P_H
+#define QPDFDESTINATION_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 <QPointF>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDestinationPrivate : public QSharedData
+{
+public:
+ QPdfDestinationPrivate() = default;
+ QPdfDestinationPrivate(int page, QPointF location, qreal zoom)
+ : page(page),
+ location(location),
+ zoom(zoom) { }
+
+ int page = -1;
+ QPointF location;
+ qreal zoom = 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFDESTINATION_P_H
diff --git a/src/pdf/api/qpdfdocument.h b/src/pdf/api/qpdfdocument.h
new file mode 100644
index 000000000..f80a7832b
--- /dev/null
+++ b/src/pdf/api/qpdfdocument.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 QPDFDOCUMENT_H
+#define QPDFDOCUMENT_H
+
+#include <QtPdf/qtpdfglobal.h>
+
+#include <QtCore/qobject.h>
+#include <QtGui/qimage.h>
+#include <QtPdf/qpdfdocumentrenderoptions.h>
+#include <QtPdf/qpdfselection.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocumentPrivate;
+class QNetworkReply;
+
+class Q_PDF_EXPORT QPdfDocument : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL)
+ Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged FINAL)
+
+public:
+ enum Status {
+ Null,
+ Loading,
+ Ready,
+ Unloading,
+ Error
+ };
+ Q_ENUM(Status)
+
+ enum DocumentError {
+ NoError,
+ UnknownError,
+ DataNotYetAvailableError,
+ FileNotFoundError,
+ InvalidFileFormatError,
+ IncorrectPasswordError,
+ UnsupportedSecuritySchemeError
+ };
+ Q_ENUM(DocumentError)
+
+ enum MetaDataField {
+ Title,
+ Subject,
+ Author,
+ Keywords,
+ Producer,
+ Creator,
+ CreationDate,
+ ModificationDate
+ };
+ Q_ENUM(MetaDataField)
+
+ explicit QPdfDocument(QObject *parent = nullptr);
+ ~QPdfDocument();
+
+ DocumentError load(const QString &fileName);
+
+ Status status() const;
+
+ void load(QIODevice *device);
+ void setPassword(const QString &password);
+ QString password() const;
+
+ QVariant metaData(MetaDataField field) const;
+
+ DocumentError error() const;
+
+ void close();
+
+ int pageCount() const;
+
+ QSizeF pageSize(int page) const;
+
+ QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions());
+
+ Q_INVOKABLE QPdfSelection getSelection(int page, QPointF start, QPointF end);
+ Q_INVOKABLE QPdfSelection getAllText(int page);
+
+Q_SIGNALS:
+ void passwordChanged();
+ void passwordRequired();
+ void statusChanged(QPdfDocument::Status status);
+ void pageCountChanged(int pageCount);
+
+private:
+ friend class QPdfBookmarkModelPrivate;
+ friend class QPdfLinkModelPrivate;
+ friend class QPdfSearchModel;
+ friend class QPdfSearchModelPrivate;
+
+ Q_PRIVATE_SLOT(d, void _q_tryLoadingWithSizeFromContentHeader())
+ Q_PRIVATE_SLOT(d, void _q_copyFromSequentialSourceDevice())
+ QScopedPointer<QPdfDocumentPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFDOCUMENT_H
diff --git a/src/pdf/api/qpdfdocument_p.h b/src/pdf/api/qpdfdocument_p.h
new file mode 100644
index 000000000..b69b6f19e
--- /dev/null
+++ b/src/pdf/api/qpdfdocument_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** 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 QPDFDOCUMENT_P_H
+#define QPDFDOCUMENT_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 "qpdfdocument.h"
+
+#include "third_party/pdfium/public/fpdfview.h"
+#include "third_party/pdfium/public/fpdf_dataavail.h"
+
+#include <QtCore/qbuffer.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qpointer.h>
+#include <QtNetwork/qnetworkreply.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfMutexLocker : public QMutexLocker
+{
+public:
+ QPdfMutexLocker();
+};
+
+class QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS
+{
+public:
+ QPdfDocumentPrivate();
+ ~QPdfDocumentPrivate();
+
+ QPdfDocument *q;
+
+ FPDF_AVAIL avail;
+ FPDF_DOCUMENT doc;
+ bool loadComplete;
+
+ QPointer<QIODevice> device;
+ QScopedPointer<QIODevice> ownDevice;
+ QBuffer asyncBuffer;
+ QPointer<QIODevice> sequentialSourceDevice;
+ QByteArray password;
+
+ QPdfDocument::Status status;
+ QPdfDocument::DocumentError lastError;
+ int pageCount;
+
+ void clear();
+
+ void load(QIODevice *device, bool ownDevice);
+ void loadAsync(QIODevice *device);
+
+ void _q_tryLoadingWithSizeFromContentHeader();
+ void initiateAsyncLoadWithTotalSizeKnown(quint64 totalSize);
+ void _q_copyFromSequentialSourceDevice();
+ void tryLoadDocument();
+ void checkComplete();
+ bool checkPageComplete(int page);
+ void setStatus(QPdfDocument::Status status);
+
+ static FPDF_BOOL fpdf_IsDataAvail(struct _FX_FILEAVAIL* pThis, size_t offset, size_t size);
+ static int fpdf_GetBlock(void* param, unsigned long position, unsigned char* pBuf, unsigned long size);
+ static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS* pThis, size_t offset, size_t size);
+ void updateLastError();
+ QString getText(FPDF_TEXTPAGE textPage, int startIndex, int count);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFDOCUMENT_P_H
diff --git a/src/pdf/api/qpdfdocumentrenderoptions.h b/src/pdf/api/qpdfdocumentrenderoptions.h
new file mode 100644
index 000000000..cafb4716f
--- /dev/null
+++ b/src/pdf/api/qpdfdocumentrenderoptions.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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 QPDFDOCUMENTRENDEROPTIONS_H
+#define QPDFDOCUMENTRENDEROPTIONS_H
+
+#include <QtPdf/qpdfnamespace.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocumentRenderOptions
+{
+public:
+ Q_DECL_CONSTEXPR QPdfDocumentRenderOptions() noexcept : m_renderFlags(0), m_rotation(0), m_reserved(0) {}
+
+ Q_DECL_CONSTEXPR QPdf::Rotation rotation() const noexcept { return static_cast<QPdf::Rotation>(m_rotation); }
+ Q_DECL_RELAXED_CONSTEXPR void setRotation(QPdf::Rotation r) noexcept { m_rotation = r; }
+
+ Q_DECL_CONSTEXPR QPdf::RenderFlags renderFlags() const noexcept { return static_cast<QPdf::RenderFlags>(m_renderFlags); }
+ Q_DECL_RELAXED_CONSTEXPR void setRenderFlags(QPdf::RenderFlags r) noexcept { m_renderFlags = r; }
+
+ Q_DECL_CONSTEXPR QRect scaledClipRect() const noexcept { return m_clipRect; }
+ Q_DECL_RELAXED_CONSTEXPR void setScaledClipRect(const QRect &r) noexcept { m_clipRect = r; }
+
+ Q_DECL_CONSTEXPR QSize scaledSize() const noexcept { return m_scaledSize; }
+ Q_DECL_RELAXED_CONSTEXPR void setScaledSize(const QSize &s) noexcept { m_scaledSize = s; }
+
+private:
+ friend Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept;
+
+ QRect m_clipRect;
+ QSize m_scaledSize;
+
+ quint32 m_renderFlags : 8;
+ quint32 m_rotation : 3;
+ quint32 m_reserved : 21;
+ quint32 m_reserved2 = 0;
+};
+
+Q_DECLARE_TYPEINFO(QPdfDocumentRenderOptions, Q_PRIMITIVE_TYPE);
+
+Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept
+{
+ return lhs.m_clipRect == rhs.m_clipRect && lhs.m_scaledSize == rhs.m_scaledSize &&
+ lhs.m_renderFlags == rhs.m_renderFlags && lhs.m_rotation == rhs.m_rotation &&
+ lhs.m_reserved == rhs.m_reserved && lhs.m_reserved2 == rhs.m_reserved2; // fix -Wunused-private-field
+}
+
+Q_DECL_CONSTEXPR inline bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept
+{
+ return !operator==(lhs, rhs);
+}
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QPdfDocumentRenderOptions)
+
+#endif // QPDFDOCUMENTRENDEROPTIONS_H
diff --git a/src/pdf/api/qpdflinkmodel_p.h b/src/pdf/api/qpdflinkmodel_p.h
new file mode 100644
index 000000000..cf9c0aad4
--- /dev/null
+++ b/src/pdf/api/qpdflinkmodel_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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 QPDFLINKMODEL_P_H
+#define QPDFLINKMODEL_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 "qtpdfglobal.h"
+#include "qpdfdocument.h"
+
+#include <QObject>
+#include <QAbstractListModel>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfLinkModelPrivate;
+
+class Q_PDF_EXPORT QPdfLinkModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+
+public:
+ enum class Role : int {
+ Rect = Qt::UserRole,
+ Url,
+ Page,
+ Location,
+ Zoom,
+ _Count
+ };
+ Q_ENUM(Role)
+ explicit QPdfLinkModel(QObject *parent = nullptr);
+ ~QPdfLinkModel();
+
+ QPdfDocument *document() const;
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ int page() const;
+
+public Q_SLOTS:
+ void setDocument(QPdfDocument *document);
+ void setPage(int page);
+
+Q_SIGNALS:
+ void documentChanged();
+ void pageChanged(int page);
+
+private Q_SLOTS:
+ void onStatusChanged(QPdfDocument::Status status);
+
+private:
+ QHash<int, QByteArray> m_roleNames;
+ Q_DECLARE_PRIVATE(QPdfLinkModel)
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFLINKMODEL_P_H
diff --git a/src/pdf/api/qpdflinkmodel_p_p.h b/src/pdf/api/qpdflinkmodel_p_p.h
new file mode 100644
index 000000000..3e44f1651
--- /dev/null
+++ b/src/pdf/api/qpdflinkmodel_p_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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 QPDFLINKMODEL_P_P_H
+#define QPDFLINKMODEL_P_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 "qpdflinkmodel_p.h"
+#include <private/qabstractitemmodel_p.h>
+
+#include "third_party/pdfium/public/fpdfview.h"
+
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfLinkModelPrivate: public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QPdfLinkModel)
+
+public:
+ QPdfLinkModelPrivate();
+
+ void update();
+
+ struct Link {
+ // where it is on the current page
+ QRectF rect;
+ int textStart = -1;
+ int textCharCount = 0;
+ // destination inside PDF
+ int page = -1; // -1 means look at the url instead
+ QPointF location;
+ qreal zoom = 1;
+ // web destination
+ QUrl url;
+
+ QString toString() const;
+ };
+
+ QPdfDocument *document = nullptr;
+ QVector<Link> links;
+ int page = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFLINKMODEL_P_P_H
diff --git a/src/pdf/api/qpdfnamespace.h b/src/pdf/api/qpdfnamespace.h
new file mode 100644
index 000000000..e76d0abd9
--- /dev/null
+++ b/src/pdf/api/qpdfnamespace.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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 QPDFNAMESPACE_H
+#define QPDFNAMESPACE_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QPdf {
+ Q_NAMESPACE
+
+ enum Rotation {
+ Rotate0,
+ Rotate90,
+ Rotate180,
+ Rotate270
+ };
+ Q_ENUM_NS(Rotation)
+
+ enum RenderFlag {
+ NoRenderFlags = 0x000,
+ RenderAnnotations = 0x001,
+ RenderOptimizedForLcd = 0x002,
+ RenderGrayscale = 0x004,
+ RenderForceHalftone = 0x008,
+ RenderTextAliased = 0x010,
+ RenderImageAliased = 0x020,
+ RenderPathAliased = 0x040
+ };
+ Q_FLAG_NS(RenderFlag)
+ Q_DECLARE_FLAGS(RenderFlags, RenderFlag)
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPdf::RenderFlags)
+
+QT_END_NAMESPACE
+#endif
diff --git a/src/pdf/api/qpdfpagenavigation.h b/src/pdf/api/qpdfpagenavigation.h
new file mode 100644
index 000000000..0f416bf77
--- /dev/null
+++ b/src/pdf/api/qpdfpagenavigation.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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 QPDFPAGENAVIGATION_H
+#define QPDFPAGENAVIGATION_H
+
+#include <QtPdf/qtpdfglobal.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocument;
+class QPdfPageNavigationPrivate;
+
+class Q_PDF_EXPORT QPdfPageNavigation : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged)
+
+ Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged)
+ Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged)
+ Q_PROPERTY(bool canGoToPreviousPage READ canGoToPreviousPage NOTIFY canGoToPreviousPageChanged)
+ Q_PROPERTY(bool canGoToNextPage READ canGoToNextPage NOTIFY canGoToNextPageChanged)
+
+public:
+ explicit QPdfPageNavigation(QObject *parent = nullptr);
+ ~QPdfPageNavigation();
+
+ QPdfDocument* document() const;
+ void setDocument(QPdfDocument *document);
+
+ int currentPage() const;
+ void setCurrentPage(int currentPage);
+
+ int pageCount() const;
+
+ bool canGoToPreviousPage() const;
+ bool canGoToNextPage() const;
+
+public Q_SLOTS:
+ void goToPreviousPage();
+ void goToNextPage();
+
+Q_SIGNALS:
+ void documentChanged(QPdfDocument *document);
+ void currentPageChanged(int currentPage);
+ void pageCountChanged(int pageCount);
+ void canGoToPreviousPageChanged(bool canGo);
+ void canGoToNextPageChanged(bool canGo);
+
+private:
+ Q_DECLARE_PRIVATE(QPdfPageNavigation)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/pdf/api/qpdfpagerenderer.h b/src/pdf/api/qpdfpagerenderer.h
new file mode 100644
index 000000000..c7b8de0df
--- /dev/null
+++ b/src/pdf/api/qpdfpagerenderer.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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 QPDFPAGERENDERER_H
+#define QPDFPAGERENDERER_H
+
+#include <QtPdf/qtpdfglobal.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qsize.h>
+#include <QtPdf/qpdfdocumentrenderoptions.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfDocument;
+class QPdfPageRendererPrivate;
+
+class Q_PDF_EXPORT QPdfPageRenderer : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(RenderMode renderMode READ renderMode WRITE setRenderMode NOTIFY renderModeChanged)
+
+public:
+ enum class RenderMode
+ {
+ MultiThreaded,
+ SingleThreaded
+ };
+ Q_ENUM(RenderMode)
+
+ explicit QPdfPageRenderer(QObject *parent = nullptr);
+ ~QPdfPageRenderer() override;
+
+ RenderMode renderMode() const;
+ void setRenderMode(RenderMode mode);
+
+ QPdfDocument* document() const;
+ void setDocument(QPdfDocument *document);
+
+ quint64 requestPage(int pageNumber, QSize imageSize,
+ QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions());
+
+Q_SIGNALS:
+ void documentChanged(QPdfDocument *document);
+ void renderModeChanged(RenderMode renderMode);
+
+ void pageRendered(int pageNumber, QSize imageSize, const QImage &image,
+ QPdfDocumentRenderOptions options, quint64 requestId);
+
+private:
+ Q_DECLARE_PRIVATE(QPdfPageRenderer)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/pdf/api/qpdfsearchmodel.h b/src/pdf/api/qpdfsearchmodel.h
new file mode 100644
index 000000000..eb0fb831f
--- /dev/null
+++ b/src/pdf/api/qpdfsearchmodel.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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 QPDFSEARCHMODEL_H
+#define QPDFSEARCHMODEL_H
+
+#include <QtPdf/qtpdfglobal.h>
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtPdf/qpdfdocument.h>
+#include <QtPdf/qpdfsearchresult.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchModelPrivate;
+
+class Q_PDF_EXPORT QPdfSearchModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY searchStringChanged)
+
+public:
+ enum class Role : int {
+ Page = Qt::UserRole,
+ IndexOnPage,
+ Location,
+ ContextBefore,
+ ContextAfter,
+ _Count
+ };
+ Q_ENUM(Role)
+ explicit QPdfSearchModel(QObject *parent = nullptr);
+ ~QPdfSearchModel();
+
+ QVector<QPdfSearchResult> resultsOnPage(int page) const;
+ QPdfSearchResult resultAtIndex(int index) const;
+
+ QPdfDocument *document() const;
+ QString searchString() const;
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+public Q_SLOTS:
+ void setSearchString(QString searchString);
+ void setDocument(QPdfDocument *document);
+
+Q_SIGNALS:
+ void documentChanged();
+ void searchStringChanged();
+
+protected:
+ void updatePage(int page);
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ QHash<int, QByteArray> m_roleNames;
+ Q_DECLARE_PRIVATE(QPdfSearchModel)
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHMODEL_H
diff --git a/src/pdf/api/qpdfsearchmodel_p.h b/src/pdf/api/qpdfsearchmodel_p.h
new file mode 100644
index 000000000..2a23706b2
--- /dev/null
+++ b/src/pdf/api/qpdfsearchmodel_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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 QPDFSEARCHMODEL_P_H
+#define QPDFSEARCHMODEL_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 "qpdfsearchmodel.h"
+#include "qpdfsearchresult_p.h"
+#include <private/qabstractitemmodel_p.h>
+
+#include "third_party/pdfium/public/fpdfview.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QPdfSearchModel)
+
+public:
+ QPdfSearchModelPrivate();
+ void clearResults();
+ bool doSearch(int page);
+
+ struct PageAndIndex {
+ int page;
+ int index;
+ };
+ PageAndIndex pageAndIndexForResult(int resultIndex);
+ int rowsBeforePage(int page);
+
+ QPdfDocument *document = nullptr;
+ QString searchString;
+ QVector<bool> pagesSearched;
+ QVector<QVector<QPdfSearchResult>> searchResults;
+ int rowCountSoFar = 0;
+ int updateTimerId = -1;
+ int nextPageToUpdate = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHMODEL_P_H
diff --git a/src/pdf/api/qpdfsearchresult.h b/src/pdf/api/qpdfsearchresult.h
new file mode 100644
index 000000000..2dfca2dc4
--- /dev/null
+++ b/src/pdf/api/qpdfsearchresult.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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 QPDFSEARCHRESULT_H
+#define QPDFSEARCHRESULT_H
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qvector.h>
+#include <QtPdf/qpdfdestination.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchResultPrivate;
+
+class Q_PDF_EXPORT QPdfSearchResult : public QPdfDestination
+{
+ Q_GADGET
+ Q_PROPERTY(QString contextBefore READ contextBefore)
+ Q_PROPERTY(QString contextAfter READ contextAfter)
+ Q_PROPERTY(QVector<QRectF> rectangles READ rectangles)
+
+public:
+ QPdfSearchResult();
+ ~QPdfSearchResult() {}
+
+ QString contextBefore() const;
+ QString contextAfter() const;
+ QVector<QRectF> rectangles() const;
+
+private:
+ QPdfSearchResult(int page, QVector<QRectF> rects, QString contextBefore, QString contextAfter);
+ QPdfSearchResult(QPdfSearchResultPrivate *d);
+ friend class QPdfDocument;
+ friend class QPdfSearchModelPrivate;
+ friend class QQuickPdfNavigationStack;
+};
+
+Q_PDF_EXPORT QDebug operator<<(QDebug, const QPdfSearchResult &);
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHRESULT_H
diff --git a/src/pdf/api/qpdfsearchresult_p.h b/src/pdf/api/qpdfsearchresult_p.h
new file mode 100644
index 000000000..615dce4e0
--- /dev/null
+++ b/src/pdf/api/qpdfsearchresult_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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 QPDFSEARCHRESULT_P_H
+#define QPDFSEARCHRESULT_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 "qpdfdestination_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSearchResultPrivate : public QPdfDestinationPrivate
+{
+public:
+ QPdfSearchResultPrivate() = default;
+ QPdfSearchResultPrivate(int page, QVector<QRectF> rects, QString contextBefore, QString contextAfter) :
+ QPdfDestinationPrivate(page, rects.first().topLeft(), 0),
+ contextBefore(contextBefore),
+ contextAfter(contextAfter),
+ rects(rects) {}
+
+ QString contextBefore;
+ QString contextAfter;
+ QVector<QRectF> rects;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSEARCHRESULT_P_H
diff --git a/src/pdf/api/qpdfselection.h b/src/pdf/api/qpdfselection.h
new file mode 100644
index 000000000..5a6a1cddc
--- /dev/null
+++ b/src/pdf/api/qpdfselection.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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 QPDFSELECTION_H
+#define QPDFSELECTION_H
+
+#include <QtPdf/qtpdfglobal.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qshareddata.h>
+#include <QtGui/qclipboard.h>
+#include <QtGui/qpolygon.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSelectionPrivate;
+
+class Q_PDF_EXPORT QPdfSelection
+{
+ Q_GADGET
+ Q_PROPERTY(bool valid READ isValid)
+ Q_PROPERTY(QVector<QPolygonF> bounds READ bounds)
+ Q_PROPERTY(QString text READ text)
+
+public:
+ ~QPdfSelection();
+ QPdfSelection(const QPdfSelection &other);
+ QPdfSelection &operator=(const QPdfSelection &other);
+ QPdfSelection(QPdfSelection &&other) noexcept;
+ QPdfSelection &operator=(QPdfSelection &&other) noexcept { swap(other); return *this; }
+ void swap(QPdfSelection &other) noexcept { d.swap(other.d); }
+ bool isValid() const;
+ QVector<QPolygonF> bounds() const;
+ QString text() const;
+#if QT_CONFIG(clipboard)
+ void copyToClipboard(QClipboard::Mode mode = QClipboard::Clipboard) const;
+#endif
+
+private:
+ QPdfSelection();
+ QPdfSelection(const QString &text, QVector<QPolygonF> bounds);
+ QPdfSelection(QPdfSelectionPrivate *d);
+ friend class QPdfDocument;
+ friend class QQuickPdfSelection;
+
+private:
+ QExplicitlySharedDataPointer<QPdfSelectionPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSELECTION_H
diff --git a/src/pdf/api/qpdfselection_p.h b/src/pdf/api/qpdfselection_p.h
new file mode 100644
index 000000000..37145f7f9
--- /dev/null
+++ b/src/pdf/api/qpdfselection_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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 QPDFSELECTION_P_H
+#define QPDFSELECTION_P_H
+
+#include <QPolygonF>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfSelectionPrivate : public QSharedData
+{
+public:
+ QPdfSelectionPrivate() = default;
+ QPdfSelectionPrivate(const QString &text, QVector<QPolygonF> bounds)
+ : text(text),
+ bounds(bounds) { }
+
+ QString text;
+ QVector<QPolygonF> bounds;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPDFSELECTION_P_H
diff --git a/src/pdf/api/qtpdfglobal.h b/src/pdf/api/qtpdfglobal.h
new file mode 100644
index 000000000..223ec4bcb
--- /dev/null
+++ b/src/pdf/api/qtpdfglobal.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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 QTPDFGLOBAL_H
+#define QTPDFGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef Q_PDF_EXPORT
+# ifndef QT_STATIC
+# if defined(QT_BUILD_PDF_LIB)
+# define Q_PDF_EXPORT Q_DECL_EXPORT
+# else
+# define Q_PDF_EXPORT Q_DECL_IMPORT
+# endif
+# else
+# define Q_PDF_EXPORT
+# endif
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QTPDFGLOBAL_H
+
diff --git a/src/pdf/configure.json b/src/pdf/configure.json
new file mode 100644
index 000000000..ddc0e99dc
--- /dev/null
+++ b/src/pdf/configure.json
@@ -0,0 +1,64 @@
+{
+ "module": "pdf",
+ "depends" : [ "buildtools-private" ],
+ "testDir": "../../config.tests",
+ "condition": "features.build-qtpdf && features.webengine-qtpdf-support",
+ "libraries": {
+ },
+ "tests": {
+ },
+ "features": {
+ "pdf-v8": {
+ "label": "Support V8",
+ "purpose": "Enables javascript support.",
+ "autoDetect": "false",
+ "condition": "!config.ios",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa": {
+ "label": "Support XFA",
+ "purpose": "Enables XFA support.",
+ "condition": "features.pdf-v8",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-bmp": {
+ "label": "Support XFA-BMP",
+ "purpose": "Enables XFA-BMP support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-gif": {
+ "label": "Support XFA-GIF",
+ "purpose": "Enables XFA-GIF support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-png": {
+ "label": "Support XFA-PNG",
+ "purpose": "Enables XFA-PNG support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ },
+ "pdf-xfa-tiff": {
+ "label": "Support XFA-TIFF",
+ "purpose": "Enables XFA-TIFF support.",
+ "condition": "features.pdf-xfa",
+ "output": ["privateFeature" ]
+ }
+ },
+ "report": [
+ ],
+ "summary": [
+ {
+ "section": "Qt PDF",
+ "entries": [
+ "pdf-v8",
+ "pdf-xfa",
+ "pdf-xfa-bmp",
+ "pdf-xfa-gif",
+ "pdf-xfa-png",
+ "pdf-xfa-tiff"
+ ]
+ }
+ ]
+}
diff --git a/src/pdf/doc/qtpdf.qdocconf b/src/pdf/doc/qtpdf.qdocconf
new file mode 100644
index 000000000..7a77105c9
--- /dev/null
+++ b/src/pdf/doc/qtpdf.qdocconf
@@ -0,0 +1,58 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QtPdf
+description = Qt Pdf Reference Documentation
+version = $QT_VERSION
+
+qhp.projects = QtPdf
+
+qhp.QtPdf.file = qtpdf.qhp
+qhp.QtPdf.namespace = org.qt-project.qtpdf.$QT_VERSION_TAG
+qhp.QtPdf.virtualFolder = qtpdf
+qhp.QtPdf.indexTitle = Qt PDF
+qhp.QtPdf.indexRoot =
+
+qhp.QtPdf.filterAttributes = qtpdf $QT_VERSION qtrefdoc
+qhp.QtPdf.customFilters.Qt.name = QtPdf $QT_VERSION
+qhp.QtPdf.customFilters.Qt.filterAttributes = qtpdf $QT_VERSION
+
+qhp.QtPdf.subprojects = classes qmltypes examples
+
+qhp.QtPdf.subprojects.classes.title = C++ Classes
+qhp.QtPdf.subprojects.classes.indexTitle = Qt PDF C++ Classes
+qhp.QtPdf.subprojects.classes.selectors = class fake:headerfile
+qhp.QtPdf.subprojects.classes.sortPages = true
+
+qhp.QtPdf.subprojects.qmltypes.title = QML Types
+qhp.QtPdf.subprojects.qmltypes.indexTitle = Qt Quick PDF QML Types
+qhp.QtPdf.subprojects.qmltypes.selectors = qmltype
+qhp.QtPdf.subprojects.qmltypes.sortPages = true
+
+qhp.QtPdf.subprojects.examples.title = Examples
+qhp.QtPdf.subprojects.examples.indexTitle = Qt PDF Examples
+qhp.QtPdf.subprojects.examples.selectors = doc:example
+qhp.QtPdf.subprojects.examples.sortPages = true
+
+depends += qtcore \
+ qtwidgets \
+ qtgui \
+ qtdoc \
+ qmake \
+ qtdesigner \
+ qtquick \
+ qtcmake
+
+headerdirs += ../api \
+ ../quick
+
+sourcedirs += .. \
+ ../quick
+
+exampledirs += ../../../examples/pdfwidgets \
+ snippets/
+
+imagedirs += images
+
+navigation.landingpage = "Qt PDF"
+navigation.cppclassespage = "Qt PDF C++ Classes"
+navigation.qmltypespage = "Qt WebEngine QML Types"
diff --git a/src/pdf/doc/snippets/qtpdf-build.cmake b/src/pdf/doc/snippets/qtpdf-build.cmake
new file mode 100644
index 000000000..d46b9c3ee
--- /dev/null
+++ b/src/pdf/doc/snippets/qtpdf-build.cmake
@@ -0,0 +1,2 @@
+find_package(Qt5 COMPONENTS Pdf REQUIRED)
+target_link_libraries(mytarget Qt5::Pdf)
diff --git a/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc
new file mode 100644
index 000000000..25593b1ee
--- /dev/null
+++ b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+QT += pdf
+//! [0]
+
+
+//! [1]
+#include <QtPdf>
+//! [1]
diff --git a/src/pdf/doc/src/qtpdf-examples.qdoc b/src/pdf/doc/src/qtpdf-examples.qdoc
new file mode 100644
index 000000000..9daa0e7f8
--- /dev/null
+++ b/src/pdf/doc/src/qtpdf-examples.qdoc
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \group qtpdf-examples
+ \ingroup all-examples
+
+ \title Qt PDF Examples
+ \brief Using the classes and types in the Qt PDF module.
+
+ The following examples illustrate how to use the C++ classes and QML types
+ in the \l{Qt PDF} module to render PDF documents.
+*/
diff --git a/src/pdf/doc/src/qtpdf-index.qdoc b/src/pdf/doc/src/qtpdf-index.qdoc
new file mode 100644
index 000000000..b32787eb5
--- /dev/null
+++ b/src/pdf/doc/src/qtpdf-index.qdoc
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page qtpdf-index.html
+ \title Qt PDF
+
+ \brief Renders pages from PDF documents.
+
+ The Qt PDF module contains classes and functions for rendering
+ PDF documents. The \l QPdfDocument class loads a PDF document
+ and renders pages from it according to the options provided by
+ the \l QPdfDocumentRenderOptions class. The \l QPdfPageRenderer
+ class manages a queue that collects all render requests. The
+ \l QPdfPageNavigation class handles the navigation through a
+ PDF document.
+
+ \include module-use.qdocinc using qt module
+ \quotefile qtpdf-build.cmake
+
+ See also the \l{Build with CMake} overview.
+
+ \section2 Building with qmake
+
+ To include the definitions of the module's classes, use the
+ following directive:
+
+ \snippet qtpdf_build_snippet.qdoc 1
+
+ To link against the module, add this line to your qmake project file:
+
+ \snippet qtpdf_build_snippet.qdoc 0
+
+ \section1 Articles and Guides
+
+ \list
+ \li \l{Qt PDF Overview}
+ \endlist
+
+ \section1 Examples
+
+ \list
+ \li \l{Qt PDF Examples}
+ \endlist
+
+ \section1 API Reference
+
+ \list
+ \li \l{Qt PDF C++ Classes}
+ \li \l{Qt Quick PDF QML Types}
+ \endlist
+*/
diff --git a/src/pdf/doc/src/qtpdf-module.qdoc b/src/pdf/doc/src/qtpdf-module.qdoc
new file mode 100644
index 000000000..4170deb38
--- /dev/null
+++ b/src/pdf/doc/src/qtpdf-module.qdoc
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \module QtPdf
+ \title Qt PDF C++ Classes
+ \brief Renders pages from PDF documents.
+ \since 5.14
+ \ingroup qtwebengine-modules
+ \ingroup modules
+
+ The Qt PDF module contains classes and functions for rendering
+ PDF documents.
+
+ To include the definitions of the module's classes, use the
+ following directive:
+
+ \snippet qtpdf_build_snippet.qdoc 1
+
+ \if !defined(qtforpython)
+ To link against the module, add this line to your qmake project file:
+
+ \snippet qtpdf_build_snippet.qdoc 0
+ \endif
+*/
diff --git a/src/pdf/gn_run.pro b/src/pdf/gn_run.pro
new file mode 100644
index 000000000..7fd4d4ffc
--- /dev/null
+++ b/src/pdf/gn_run.pro
@@ -0,0 +1,70 @@
+include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri)
+QT_FOR_CONFIG += buildtools-private
+
+TEMPLATE = aux
+
+qtConfig(debug_and_release): CONFIG += debug_and_release build_all
+
+qtConfig(webengine-system-ninja) {
+ QT_TOOL.ninja.binary = ninja
+} else {
+ QT_TOOL.ninja.binary = $$shell_quote($$shell_path($$ninjaPath()))
+}
+
+win32 {
+ # Add the gnuwin32/bin subdir of qt5.git to PATH. Needed for calling bison and friends.
+ gnuwin32path.name = PATH
+ gnuwin32path.value = $$shell_path($$clean_path($$QTWEBENGINE_ROOT/../gnuwin32/bin))
+ gnuwin32path.CONFIG += prepend
+ exists($$gnuwin32path.value): QT_TOOL_ENV = gnuwin32path
+}
+
+qtPrepareTool(NINJA, ninja)
+QT_TOOL_ENV =
+
+build_pass|!debug_and_release {
+ gn_binary = gn
+
+ runninja.target = run_ninja
+
+ # fixme: refine args
+ gn_args = $$gnArgs()
+
+ include($$QTWEBENGINE_ROOT/src/buildtools/config/pdf.pri)
+
+ # fixme: qtwebengine_target
+ gn_args += "qtwebengine_target=\"$$system_path($$OUT_PWD/$$getConfigDir()):QtPdf\""
+
+ # fixme:
+ !qtConfig(webengine-system-gn) {
+ gn_binary = $$system_quote($$system_path($$gnPath()))
+ }
+
+ gn_args = $$system_quote($$gn_args)
+ gn_src_root = $$system_quote($$system_path($$QTWEBENGINE_ROOT/$$getChromiumSrcDir()))
+ gn_build_root = $$system_quote($$system_path($$OUT_PWD/$$getConfigDir()))
+ gn_python = "--script-executable=$$pythonPathForSystem()"
+ gn_run = $$gn_binary gen $$gn_build_root $$gn_python --args=$$gn_args --root=$$gn_src_root
+
+ message("Running: $$gn_run ")
+ !system($$gn_run) {
+ error("GN run error!")
+ }
+
+ ninjaflags = $$(NINJAFLAGS)
+ isEmpty(ninjaflags):!silent: ninjaflags = "-v"
+
+ runninja.commands = $$NINJA $$ninjaflags -C $$gn_build_root QtPdf
+ QMAKE_EXTRA_TARGETS += runninja
+
+ build_pass:build_all: default_target.target = all
+ else: default_target.target = first
+ default_target.depends = runninja
+ QMAKE_EXTRA_TARGETS += default_target
+}
+
+!build_pass:debug_and_release {
+ # Special GNU make target for the meta Makefile that ensures that our debug and release Makefiles won't both run ninja in parallel.
+ notParallel.target = .NOTPARALLEL
+ QMAKE_EXTRA_TARGETS += notParallel
+}
diff --git a/src/pdf/jsbridge.cpp b/src/pdf/jsbridge.cpp
new file mode 100644
index 000000000..33d3b2465
--- /dev/null
+++ b/src/pdf/jsbridge.cpp
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** 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 "fpdfsdk/javascript/JS_Runtime_Stub.cpp"
+
diff --git a/src/pdf/pdf.pro b/src/pdf/pdf.pro
new file mode 100644
index 000000000..c5513ce93
--- /dev/null
+++ b/src/pdf/pdf.pro
@@ -0,0 +1,16 @@
+TEMPLATE = subdirs
+pdfcore.file = pdfcore.pro
+pdfcore_generator.file = pdfcore_generator.pro
+gn_run.file = gn_run.pro
+
+gn_run.depends = pdfcore_generator
+pdfcore.depends = gn_run
+quick.depends = pdfcore
+
+SUBDIRS += \
+ pdfcore_generator \
+ gn_run \
+ pdfcore \
+ quick
+
+
diff --git a/src/pdf/pdfcore.pro b/src/pdf/pdfcore.pro
new file mode 100644
index 000000000..c87722b7e
--- /dev/null
+++ b/src/pdf/pdfcore.pro
@@ -0,0 +1,93 @@
+TARGET = QtPdf
+MODULE = pdf
+
+QT += gui core core-private
+QT_PRIVATE += network
+
+TEMPLATE = lib
+
+INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf
+CHROMIUM_SRC_DIR = $$QTWEBENGINE_ROOT/$$getChromiumSrcDir()
+CHROMIUM_GEN_DIR = $$OUT_PWD/../$$getConfigDir()/gen
+INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf \
+ $$CHROMIUM_GEN_DIR \
+ $$CHROMIUM_SRC_DIR \
+ api
+
+DEFINES += QT_BUILD_PDF_LIB
+win32: DEFINES += NOMINMAX
+
+linking_pri = $$OUT_PWD/$$getConfigDir()/$${TARGET}.pri
+!include($$linking_pri) {
+ error("Could not find the linking information that gn should have generated.")
+}
+
+isEmpty(NINJA_OBJECTS): error("Missing object files from QtPdf linking pri.")
+isEmpty(NINJA_LFLAGS): error("Missing linker flags from QtPdf linking pri")
+isEmpty(NINJA_LIBS): error("Missing library files from QtPdf linking pri")
+
+NINJA_OBJECTS = $$eval($$list($$NINJA_OBJECTS))
+RSP_FILE = $$OUT_PWD/$$getConfigDir()/$${TARGET}.rsp
+for(object, NINJA_OBJECTS): RSP_CONTENT += $$object
+write_file($$RSP_FILE, RSP_CONTENT)
+
+macos:LIBS_PRIVATE += -Wl,-filelist,$$shell_quote($$RSP_FILE)
+linux:LIBS_PRIVATE += @$$RSP_FILE
+
+# QTBUG-58710 add main rsp file on windows
+win32:QMAKE_LFLAGS += @$$RSP_FILE
+
+!isEmpty(NINJA_ARCHIVES) {
+ linux: LIBS_PRIVATE += -Wl,--start-group $$NINJA_ARCHIVES -Wl,--end-group
+ else: LIBS_PRIVATE += $$NINJA_ARCHIVES
+}
+
+LIBS_PRIVATE += $$NINJA_LIB_DIRS $$NINJA_LIBS
+
+QMAKE_DOCS = $$PWD/doc/qtpdf.qdocconf
+
+gcc {
+ QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter
+}
+
+msvc {
+ QMAKE_CXXFLAGS_WARN_ON += -wd"4100"
+}
+
+ios: OBJECTS += $$NINJA_OBJECTS
+
+SOURCES += \
+ qpdfbookmarkmodel.cpp \
+ qpdfdestination.cpp \
+ qpdfdocument.cpp \
+ qpdflinkmodel.cpp \
+ qpdfpagenavigation.cpp \
+ qpdfpagerenderer.cpp \
+ qpdfsearchmodel.cpp \
+ qpdfsearchresult.cpp \
+ qpdfselection.cpp \
+
+# all "public" headers must be in "api" for sync script and to hide auto generated headers
+# by Chromium in case of in-source build
+
+HEADERS += \
+ api/qpdfbookmarkmodel.h \
+ api/qpdfdestination.h \
+ api/qpdfdestination_p.h \
+ api/qpdfdocument.h \
+ api/qpdfdocument_p.h \
+ api/qpdfdocumentrenderoptions.h \
+ api/qtpdfglobal.h \
+ api/qpdflinkmodel_p.h \
+ api/qpdflinkmodel_p_p.h \
+ api/qpdfnamespace.h \
+ api/qpdfpagenavigation.h \
+ api/qpdfpagerenderer.h \
+ api/qpdfsearchmodel.h \
+ api/qpdfsearchmodel_p.h \
+ api/qpdfsearchresult.h \
+ api/qpdfsearchresult_p.h \
+ api/qpdfselection.h \
+ api/qpdfselection_p.h \
+
+load(qt_module)
diff --git a/src/pdf/pdfcore_generator.pro b/src/pdf/pdfcore_generator.pro
new file mode 100644
index 000000000..c8eb13b81
--- /dev/null
+++ b/src/pdf/pdfcore_generator.pro
@@ -0,0 +1,15 @@
+qtConfig(debug_and_release): CONFIG += debug_and_release
+
+TARGET = QtPdf
+TEMPLATE = lib
+CONFIG = gn_generator $$CONFIG
+ios: CONFIG -=static # note we still do static on ios when linking
+GN_SRC_DIR = $$PWD
+GN_FILE = $$OUT_PWD/$$getConfigDir()/BUILD.gn
+GN_FIND_MOCABLES_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_find_mocables.py)
+GN_RUN_BINARY_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_run_binary.py)
+GN_IMPORTS = $$PWD/qtpdf.gni
+GN_CREATE_PRI = true
+QMAKE_INTERNAL_INCLUDED_FILES = $$GN_IMPORTS $$GN_INCLUDES $$GN_FILE
+
+
diff --git a/src/pdf/qpdfbookmarkmodel.cpp b/src/pdf/qpdfbookmarkmodel.cpp
new file mode 100644
index 000000000..c9c365568
--- /dev/null
+++ b/src/pdf/qpdfbookmarkmodel.cpp
@@ -0,0 +1,363 @@
+/****************************************************************************
+**
+** 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 "qpdfbookmarkmodel.h"
+
+#include "qpdfdocument.h"
+#include "qpdfdocument_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdfview.h"
+
+#include <QPointer>
+#include <QScopedPointer>
+#include <private/qabstractitemmodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class BookmarkNode
+{
+public:
+ explicit BookmarkNode(BookmarkNode *parentNode = nullptr)
+ : m_parentNode(parentNode)
+ , m_level(0)
+ , m_pageNumber(0)
+ {
+ }
+
+ ~BookmarkNode()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ qDeleteAll(m_childNodes);
+ m_childNodes.clear();
+ }
+
+ void appendChild(BookmarkNode *child)
+ {
+ m_childNodes.append(child);
+ }
+
+ BookmarkNode *child(int row) const
+ {
+ return m_childNodes.at(row);
+ }
+
+ int childCount() const
+ {
+ return m_childNodes.count();
+ }
+
+ int row() const
+ {
+ if (m_parentNode)
+ return m_parentNode->m_childNodes.indexOf(const_cast<BookmarkNode*>(this));
+
+ return 0;
+ }
+
+ BookmarkNode *parentNode() const
+ {
+ return m_parentNode;
+ }
+
+ QString title() const
+ {
+ return m_title;
+ }
+
+ void setTitle(const QString &title)
+ {
+ m_title = title;
+ }
+
+ int level() const
+ {
+ return m_level;
+ }
+
+ void setLevel(int level)
+ {
+ m_level = level;
+ }
+
+ int pageNumber() const
+ {
+ return m_pageNumber;
+ }
+
+ void setPageNumber(int pageNumber)
+ {
+ m_pageNumber = pageNumber;
+ }
+
+private:
+ QVector<BookmarkNode*> m_childNodes;
+ BookmarkNode *m_parentNode;
+
+ QString m_title;
+ int m_level;
+ int m_pageNumber;
+};
+
+
+class QPdfBookmarkModelPrivate : public QAbstractItemModelPrivate
+{
+public:
+ QPdfBookmarkModelPrivate()
+ : QAbstractItemModelPrivate()
+ , m_rootNode(new BookmarkNode(nullptr))
+ , m_document(nullptr)
+ , m_structureMode(QPdfBookmarkModel::TreeMode)
+ {
+ }
+
+ void rebuild()
+ {
+ Q_Q(QPdfBookmarkModel);
+
+ const bool documentAvailable = (m_document && m_document->status() == QPdfDocument::Ready);
+
+ if (documentAvailable) {
+ q->beginResetModel();
+ m_rootNode->clear();
+ QPdfMutexLocker lock;
+ appendChildNode(m_rootNode.data(), nullptr, 0, m_document->d->doc);
+ lock.unlock();
+ q->endResetModel();
+ } else {
+ if (m_rootNode->childCount() == 0) {
+ return;
+ } else {
+ q->beginResetModel();
+ m_rootNode->clear();
+ q->endResetModel();
+ }
+ }
+ }
+
+ void appendChildNode(BookmarkNode *parentBookmarkNode, FPDF_BOOKMARK parentBookmark, int level, FPDF_DOCUMENT document)
+ {
+ FPDF_BOOKMARK bookmark = FPDFBookmark_GetFirstChild(document, parentBookmark);
+
+ while (bookmark) {
+ BookmarkNode *childBookmarkNode = nullptr;
+
+ if (m_structureMode == QPdfBookmarkModel::TreeMode) {
+ childBookmarkNode = new BookmarkNode(parentBookmarkNode);
+ parentBookmarkNode->appendChild(childBookmarkNode);
+ } else if (m_structureMode == QPdfBookmarkModel::ListMode) {
+ childBookmarkNode = new BookmarkNode(m_rootNode.data());
+ m_rootNode->appendChild(childBookmarkNode);
+ }
+
+ const int titleLength = int(FPDFBookmark_GetTitle(bookmark, nullptr, 0));
+
+ QVector<ushort> titleBuffer(titleLength);
+ FPDFBookmark_GetTitle(bookmark, titleBuffer.data(), quint32(titleBuffer.length()));
+
+ const FPDF_DEST dest = FPDFBookmark_GetDest(document, bookmark);
+ const int pageNumber = FPDFDest_GetDestPageIndex(document, dest);
+
+ childBookmarkNode->setTitle(QString::fromUtf16(titleBuffer.data()));
+ childBookmarkNode->setLevel(level);
+ childBookmarkNode->setPageNumber(pageNumber);
+
+ // recurse down
+ appendChildNode(childBookmarkNode, bookmark, level + 1, document);
+
+ bookmark = FPDFBookmark_GetNextSibling(document, bookmark);
+ }
+ }
+
+ void _q_documentStatusChanged()
+ {
+ rebuild();
+ }
+
+ Q_DECLARE_PUBLIC(QPdfBookmarkModel)
+
+ QScopedPointer<BookmarkNode> m_rootNode;
+ QPointer<QPdfDocument> m_document;
+ QPdfBookmarkModel::StructureMode m_structureMode;
+};
+
+
+QPdfBookmarkModel::QPdfBookmarkModel(QObject *parent)
+ : QAbstractItemModel(*new QPdfBookmarkModelPrivate, parent)
+{
+}
+
+QPdfDocument* QPdfBookmarkModel::document() const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ return d->m_document;
+}
+
+void QPdfBookmarkModel::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfBookmarkModel);
+
+ if (d->m_document == document)
+ return;
+
+ if (d->m_document)
+ disconnect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
+
+ d->m_document = document;
+ emit documentChanged(d->m_document);
+
+ if (d->m_document)
+ connect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
+
+ d->rebuild();
+}
+
+QPdfBookmarkModel::StructureMode QPdfBookmarkModel::structureMode() const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ return d->m_structureMode;
+}
+
+void QPdfBookmarkModel::setStructureMode(StructureMode mode)
+{
+ Q_D(QPdfBookmarkModel);
+
+ if (d->m_structureMode == mode)
+ return;
+
+ d->m_structureMode = mode;
+ emit structureModeChanged(d->m_structureMode);
+
+ d->rebuild();
+}
+
+int QPdfBookmarkModel::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+QHash<int, QByteArray> QPdfBookmarkModel::roleNames() const
+{
+ QHash<int, QByteArray> names;
+
+ names[TitleRole] = "title";
+ names[LevelRole] = "level";
+ names[PageNumberRole] = "pageNumber";
+
+ return names;
+}
+
+QVariant QPdfBookmarkModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ const BookmarkNode *node = static_cast<BookmarkNode*>(index.internalPointer());
+ switch (role) {
+ case TitleRole:
+ return node->title();
+ case LevelRole:
+ return node->level();
+ case PageNumberRole:
+ return node->pageNumber();
+ default:
+ return QVariant();
+ }
+}
+
+QModelIndex QPdfBookmarkModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ BookmarkNode *parentNode;
+
+ if (!parent.isValid())
+ parentNode = d->m_rootNode.data();
+ else
+ parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
+
+ BookmarkNode *childNode = parentNode->child(row);
+ if (childNode)
+ return createIndex(row, column, childNode);
+ else
+ return QModelIndex();
+}
+
+QModelIndex QPdfBookmarkModel::parent(const QModelIndex &index) const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ if (!index.isValid())
+ return QModelIndex();
+
+ const BookmarkNode *childNode = static_cast<BookmarkNode*>(index.internalPointer());
+ BookmarkNode *parentNode = childNode->parentNode();
+
+ if (parentNode == d->m_rootNode.data())
+ return QModelIndex();
+
+ return createIndex(parentNode->row(), 0, parentNode);
+}
+
+int QPdfBookmarkModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QPdfBookmarkModel);
+
+ if (parent.column() > 0)
+ return 0;
+
+ BookmarkNode *parentNode = nullptr;
+
+ if (!parent.isValid())
+ parentNode = d->m_rootNode.data();
+ else
+ parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
+
+ return parentNode->childCount();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfbookmarkmodel.cpp"
diff --git a/src/pdf/qpdfdestination.cpp b/src/pdf/qpdfdestination.cpp
new file mode 100644
index 000000000..b70e031ca
--- /dev/null
+++ b/src/pdf/qpdfdestination.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** 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 "qpdfdestination.h"
+#include "qpdfdestination_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPdfDestination
+ \since 5.15
+ \inmodule QtPdf
+
+ \brief The QPdfDestination class defines a location on a page in a PDF
+ document, and a suggested zoom level at which it is intended to be viewed.
+*/
+
+/*!
+ Constructs an invalid Destination.
+
+ \sa valid
+*/
+QPdfDestination::QPdfDestination()
+ : d(new QPdfDestinationPrivate())
+{
+}
+
+QPdfDestination::QPdfDestination(int page, QPointF location, qreal zoom)
+ : d(new QPdfDestinationPrivate(page, location, zoom))
+{
+}
+
+QPdfDestination::QPdfDestination(QPdfDestinationPrivate *d)
+ : d(d)
+{
+}
+
+QPdfDestination::QPdfDestination(const QPdfDestination &other)
+ : d(other.d)
+{
+}
+
+QPdfDestination::QPdfDestination(QPdfDestination &&other) noexcept
+ : d(std::move(other.d))
+{
+}
+
+QPdfDestination::~QPdfDestination()
+{
+}
+
+QPdfDestination &QPdfDestination::operator=(const QPdfDestination &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \property QPdfDestination::valid
+
+ This property holds whether the destination is valid.
+*/
+bool QPdfDestination::isValid() const
+{
+ return d->page >= 0;
+}
+
+/*!
+ \property QPdfDestination::page
+
+ This property holds the page number.
+*/
+int QPdfDestination::page() const
+{
+ return d->page;
+}
+
+/*!
+ \property QPdfDestination::location
+
+ This property holds the location on the page, in units of points.
+*/
+QPointF QPdfDestination::location() const
+{
+ return d->location;
+}
+
+/*!
+ \property QPdfDestination::zoom
+
+ This property holds the suggested magnification level, where 1.0 means default scale
+ (1 pixel = 1 point).
+*/
+qreal QPdfDestination::zoom() const
+{
+ return d->zoom;
+}
+
+QDebug operator<<(QDebug dbg, const QPdfDestination& dest)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QPdfDestination(page=" << dest.page()
+ << " location=" << dest.location()
+ << " zoom=" << dest.zoom();
+ dbg << ')';
+ return dbg;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfdestination.cpp"
diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp
new file mode 100644
index 000000000..89b27da8b
--- /dev/null
+++ b/src/pdf/qpdfdocument.cpp
@@ -0,0 +1,803 @@
+/****************************************************************************
+**
+** 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 "qpdfdocument.h"
+#include "qpdfdocument_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdf_text.h"
+
+#include <QDateTime>
+#include <QDebug>
+#include <QElapsedTimer>
+#include <QFile>
+#include <QHash>
+#include <QLoggingCategory>
+#include <QMutex>
+#include <QVector2D>
+
+QT_BEGIN_NAMESPACE
+
+// The library is not thread-safe at all, it has a lot of global variables.
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, pdfMutex, (QMutex::Recursive));
+static int libraryRefCount;
+static const double CharacterHitTolerance = 6.0;
+Q_LOGGING_CATEGORY(qLcDoc, "qt.pdf.document")
+
+QPdfMutexLocker::QPdfMutexLocker()
+ : QMutexLocker(pdfMutex())
+{
+}
+
+QPdfDocumentPrivate::QPdfDocumentPrivate()
+ : avail(nullptr)
+ , doc(nullptr)
+ , loadComplete(false)
+ , status(QPdfDocument::Null)
+ , lastError(QPdfDocument::NoError)
+ , pageCount(0)
+{
+ asyncBuffer.setData(QByteArray());
+ asyncBuffer.open(QIODevice::ReadWrite);
+
+ const QPdfMutexLocker lock;
+
+ if (libraryRefCount == 0)
+ FPDF_InitLibrary();
+ ++libraryRefCount;
+
+ // FPDF_FILEACCESS setup
+ m_Param = this;
+ m_GetBlock = fpdf_GetBlock;
+
+ // FX_FILEAVAIL setup
+ FX_FILEAVAIL::version = 1;
+ IsDataAvail = fpdf_IsDataAvail;
+
+ // FX_DOWNLOADHINTS setup
+ FX_DOWNLOADHINTS::version = 1;
+ AddSegment = fpdf_AddSegment;
+}
+
+QPdfDocumentPrivate::~QPdfDocumentPrivate()
+{
+ q->close();
+
+ const QPdfMutexLocker lock;
+
+ if (!--libraryRefCount)
+ FPDF_DestroyLibrary();
+}
+
+void QPdfDocumentPrivate::clear()
+{
+ QPdfMutexLocker lock;
+
+ if (doc)
+ FPDF_CloseDocument(doc);
+ doc = nullptr;
+
+ if (avail)
+ FPDFAvail_Destroy(avail);
+ avail = nullptr;
+ lock.unlock();
+
+ if (pageCount != 0) {
+ pageCount = 0;
+ emit q->pageCountChanged(pageCount);
+ }
+
+ loadComplete = false;
+
+ asyncBuffer.close();
+ asyncBuffer.setData(QByteArray());
+ asyncBuffer.open(QIODevice::ReadWrite);
+
+ if (sequentialSourceDevice)
+ sequentialSourceDevice->disconnect(q);
+}
+
+void QPdfDocumentPrivate::updateLastError()
+{
+ if (doc) {
+ lastError = QPdfDocument::NoError;
+ return;
+ }
+
+ QPdfMutexLocker lock;
+ const unsigned long error = FPDF_GetLastError();
+ lock.unlock();
+
+ switch (error) {
+ case FPDF_ERR_SUCCESS: lastError = QPdfDocument::NoError; break;
+ case FPDF_ERR_UNKNOWN: lastError = QPdfDocument::UnknownError; break;
+ case FPDF_ERR_FILE: lastError = QPdfDocument::FileNotFoundError; break;
+ case FPDF_ERR_FORMAT: lastError = QPdfDocument::InvalidFileFormatError; break;
+ case FPDF_ERR_PASSWORD: lastError = QPdfDocument::IncorrectPasswordError; break;
+ case FPDF_ERR_SECURITY: lastError = QPdfDocument::UnsupportedSecuritySchemeError; break;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnership)
+{
+ if (transferDeviceOwnership)
+ ownDevice.reset(newDevice);
+ else
+ ownDevice.reset();
+
+ if (newDevice->isSequential()) {
+ sequentialSourceDevice = newDevice;
+ device = &asyncBuffer;
+ QNetworkReply *reply = qobject_cast<QNetworkReply*>(sequentialSourceDevice);
+
+ if (!reply) {
+ setStatus(QPdfDocument::Error);
+ qWarning() << "QPdfDocument: Loading from sequential devices only supported with QNetworkAccessManager.";
+ return;
+ }
+
+ if (reply->isFinished() && reply->error() != QNetworkReply::NoError) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ QObject::connect(reply, &QNetworkReply::finished, q, [this, reply](){
+ if (reply->error() != QNetworkReply::NoError || reply->bytesAvailable() == 0) {
+ this->setStatus(QPdfDocument::Error);
+ }
+ });
+
+ if (reply->header(QNetworkRequest::ContentLengthHeader).isValid())
+ _q_tryLoadingWithSizeFromContentHeader();
+ else
+ QObject::connect(reply, SIGNAL(metaDataChanged()), q, SLOT(_q_tryLoadingWithSizeFromContentHeader()));
+ } else {
+ device = newDevice;
+ initiateAsyncLoadWithTotalSizeKnown(device->size());
+ if (!avail) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ if (!doc)
+ tryLoadDocument();
+
+ if (!doc) {
+ updateLastError();
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ QPdfMutexLocker lock;
+ const int newPageCount = FPDF_GetPageCount(doc);
+ lock.unlock();
+ if (newPageCount != pageCount) {
+ pageCount = newPageCount;
+ emit q->pageCountChanged(pageCount);
+ }
+
+ // If it's a local file, and the first couple of pages are available,
+ // probably the whole document is available.
+ if (checkPageComplete(0) && (pageCount < 2 || checkPageComplete(1))) {
+ setStatus(QPdfDocument::Ready);
+ } else {
+ updateLastError();
+ setStatus(QPdfDocument::Error);
+ }
+ }
+}
+
+void QPdfDocumentPrivate::_q_tryLoadingWithSizeFromContentHeader()
+{
+ if (avail)
+ return;
+
+ const QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(sequentialSourceDevice);
+ if (!networkReply) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ const QVariant contentLength = networkReply->header(QNetworkRequest::ContentLengthHeader);
+ if (!contentLength.isValid()) {
+ setStatus(QPdfDocument::Error);
+ return;
+ }
+
+ QObject::connect(sequentialSourceDevice, SIGNAL(readyRead()), q, SLOT(_q_copyFromSequentialSourceDevice()));
+
+ initiateAsyncLoadWithTotalSizeKnown(contentLength.toULongLong());
+
+ if (sequentialSourceDevice->bytesAvailable())
+ _q_copyFromSequentialSourceDevice();
+}
+
+void QPdfDocumentPrivate::initiateAsyncLoadWithTotalSizeKnown(quint64 totalSize)
+{
+ // FPDF_FILEACCESS setup
+ m_FileLen = totalSize;
+
+ const QPdfMutexLocker lock;
+
+ avail = FPDFAvail_Create(this, this);
+}
+
+void QPdfDocumentPrivate::_q_copyFromSequentialSourceDevice()
+{
+ if (loadComplete)
+ return;
+
+ const QByteArray data = sequentialSourceDevice->read(sequentialSourceDevice->bytesAvailable());
+ if (data.isEmpty())
+ return;
+
+ asyncBuffer.seek(asyncBuffer.size());
+ asyncBuffer.write(data);
+
+ checkComplete();
+}
+
+void QPdfDocumentPrivate::tryLoadDocument()
+{
+ QPdfMutexLocker lock;
+ switch (FPDFAvail_IsDocAvail(avail, this)) {
+ case PDF_DATA_ERROR:
+ qCDebug(qLcDoc) << "error loading";
+ break;
+ case PDF_DATA_NOTAVAIL:
+ qCDebug(qLcDoc) << "data not yet available";
+ lastError = QPdfDocument::DataNotYetAvailableError;
+ setStatus(QPdfDocument::Error);
+ break;
+ case PDF_DATA_AVAIL:
+ // all good
+ break;
+ }
+
+ Q_ASSERT(!doc);
+
+ doc = FPDFAvail_GetDocument(avail, password);
+ lock.unlock();
+
+ updateLastError();
+
+ if (lastError == QPdfDocument::IncorrectPasswordError) {
+ FPDF_CloseDocument(doc);
+ doc = nullptr;
+
+ setStatus(QPdfDocument::Error);
+ emit q->passwordRequired();
+ }
+}
+
+void QPdfDocumentPrivate::checkComplete()
+{
+ if (!avail || loadComplete)
+ return;
+
+ if (!doc)
+ tryLoadDocument();
+
+ if (!doc)
+ return;
+
+ loadComplete = true;
+
+ QPdfMutexLocker lock;
+
+ const int newPageCount = FPDF_GetPageCount(doc);
+ for (int i = 0; i < newPageCount; ++i) {
+ int result = PDF_DATA_NOTAVAIL;
+ while (result == PDF_DATA_NOTAVAIL) {
+ result = FPDFAvail_IsPageAvail(avail, i, this);
+ }
+
+ if (result == PDF_DATA_ERROR)
+ loadComplete = false;
+ }
+
+ lock.unlock();
+
+ if (loadComplete) {
+ if (newPageCount != pageCount) {
+ pageCount = newPageCount;
+ emit q->pageCountChanged(pageCount);
+ }
+
+ setStatus(QPdfDocument::Ready);
+ }
+}
+
+bool QPdfDocumentPrivate::checkPageComplete(int page)
+{
+ if (page < 0 || page >= pageCount)
+ return false;
+
+ if (loadComplete)
+ return true;
+
+ QPdfMutexLocker lock;
+ int result = PDF_DATA_NOTAVAIL;
+ while (result == PDF_DATA_NOTAVAIL)
+ result = FPDFAvail_IsPageAvail(avail, page, this);
+ lock.unlock();
+
+ if (result == PDF_DATA_ERROR)
+ updateLastError();
+
+ return (result != PDF_DATA_ERROR);
+}
+
+void QPdfDocumentPrivate::setStatus(QPdfDocument::Status documentStatus)
+{
+ if (status == documentStatus)
+ return;
+
+ status = documentStatus;
+ emit q->statusChanged(status);
+}
+
+FPDF_BOOL QPdfDocumentPrivate::fpdf_IsDataAvail(_FX_FILEAVAIL *pThis, size_t offset, size_t size)
+{
+ QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(pThis);
+ return offset + size <= static_cast<quint64>(d->device->size());
+}
+
+int QPdfDocumentPrivate::fpdf_GetBlock(void *param, unsigned long position, unsigned char *pBuf, unsigned long size)
+{
+ QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(reinterpret_cast<FPDF_FILEACCESS*>(param));
+ d->device->seek(position);
+ return qMax(qint64(0), d->device->read(reinterpret_cast<char *>(pBuf), size));
+
+}
+
+void QPdfDocumentPrivate::fpdf_AddSegment(_FX_DOWNLOADHINTS *pThis, size_t offset, size_t size)
+{
+ Q_UNUSED(pThis);
+ Q_UNUSED(offset);
+ Q_UNUSED(size);
+}
+
+QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int count)
+{
+ QVector<ushort> buf(count + 1);
+ // TODO is that enough space in case one unicode character is more than one in utf-16?
+ int len = FPDFText_GetText(textPage, startIndex, count, buf.data());
+ Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator
+ return QString::fromUtf16(buf.constData(), len - 1);
+}
+
+/*!
+ \class QPdfDocument
+ \since 5.10
+ \inmodule QtPdf
+
+ \brief The QPdfDocument class loads a PDF document and renders pages from it.
+*/
+
+/*!
+ Constructs a new document with parent object \a parent.
+*/
+QPdfDocument::QPdfDocument(QObject *parent)
+ : QObject(parent)
+ , d(new QPdfDocumentPrivate)
+{
+ d->q = this;
+}
+
+/*!
+ Destroys the document.
+*/
+QPdfDocument::~QPdfDocument()
+{
+}
+
+QPdfDocument::DocumentError QPdfDocument::load(const QString &fileName)
+{
+ qCDebug(qLcDoc) << "loading" << fileName;
+
+ close();
+
+ d->setStatus(QPdfDocument::Loading);
+
+ QScopedPointer<QFile> f(new QFile(fileName));
+ if (!f->open(QIODevice::ReadOnly)) {
+ d->lastError = FileNotFoundError;
+ d->setStatus(QPdfDocument::Error);
+ } else {
+ d->load(f.take(), /*transfer ownership*/true);
+ }
+ return d->lastError;
+}
+
+/*!
+ \enum QPdfDocument::Status
+
+ This enum describes the current status of the document.
+
+ \value Null The initial status after the document has been created or after it has been closed.
+ \value Loading The status after load() has been called and before the document is fully loaded.
+ \value Ready The status when the document is fully loaded and its data can be accessed.
+ \value 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 Error The status after Loading, if loading has failed.
+
+ \sa QPdfDocument::status()
+*/
+
+/*!
+ Returns the current status of the document.
+*/
+QPdfDocument::Status QPdfDocument::status() const
+{
+ return d->status;
+}
+
+void QPdfDocument::load(QIODevice *device)
+{
+ close();
+
+ d->setStatus(QPdfDocument::Loading);
+
+ d->load(device, /*transfer ownership*/false);
+}
+
+void QPdfDocument::setPassword(const QString &password)
+{
+ const QByteArray newPassword = password.toUtf8();
+
+ if (d->password == newPassword)
+ return;
+
+ d->password = newPassword;
+ emit passwordChanged();
+}
+
+QString QPdfDocument::password() const
+{
+ return QString::fromUtf8(d->password);
+}
+
+/*!
+ \enum QPdfDocument::MetaDataField
+
+ This enum describes the available fields of meta data.
+
+ \value Title The document's title as QString.
+ \value Author The name of the person who created the document as QString.
+ \value Subject The subject of the document as QString.
+ \value Keywords Keywords associated with the document as QString.
+ \value Creator If the document was converted to PDF from another format,
+ the name of the conforming product that created the original document
+ from which it was converted as QString.
+ \value Producer If the document was converted to PDF from another format,
+ the name of the conforming product that converted it to PDF as QString.
+ \value CreationDate The date and time the document was created as QDateTime.
+ \value ModificationDate The date and time the document was most recently modified as QDateTime.
+
+ \sa QPdfDocument::metaData()
+*/
+
+/*!
+ Returns the meta data of the document for the given \a field.
+*/
+QVariant QPdfDocument::metaData(MetaDataField field) const
+{
+ if (!d->doc)
+ return QString();
+
+ QByteArray fieldName;
+ switch (field) {
+ case Title:
+ fieldName = "Title";
+ break;
+ case Subject:
+ fieldName = "Subject";
+ break;
+ case Author:
+ fieldName = "Author";
+ break;
+ case Keywords:
+ fieldName = "Keywords";
+ break;
+ case Producer:
+ fieldName = "Producer";
+ break;
+ case Creator:
+ fieldName = "Creator";
+ break;
+ case CreationDate:
+ fieldName = "CreationDate";
+ break;
+ case ModificationDate:
+ fieldName = "ModDate";
+ break;
+ }
+
+ QPdfMutexLocker lock;
+ const unsigned long len = FPDF_GetMetaText(d->doc, fieldName.constData(), nullptr, 0);
+
+ QVector<ushort> buf(len);
+ FPDF_GetMetaText(d->doc, fieldName.constData(), buf.data(), buf.length());
+ lock.unlock();
+
+ QString text = QString::fromUtf16(buf.data());
+
+ switch (field) {
+ case Title: // fall through
+ case Subject:
+ case Author:
+ case Keywords:
+ case Producer:
+ case Creator:
+ return text;
+ case CreationDate: // fall through
+ case ModificationDate:
+ // convert a "D:YYYYMMDDHHmmSSOHH'mm'" into "YYYY-MM-DDTHH:mm:ss+HH:mm"
+ if (text.startsWith(QLatin1String("D:")))
+ text = text.mid(2);
+ text.insert(4, QLatin1Char('-'));
+ text.insert(7, QLatin1Char('-'));
+ text.insert(10, QLatin1Char('T'));
+ text.insert(13, QLatin1Char(':'));
+ text.insert(16, QLatin1Char(':'));
+ text.replace(QLatin1Char('\''), QLatin1Char(':'));
+ if (text.endsWith(QLatin1Char(':')))
+ text.chop(1);
+
+ return QDateTime::fromString(text, Qt::ISODate);
+ }
+
+ return QVariant();
+}
+
+QPdfDocument::DocumentError QPdfDocument::error() const
+{
+ return d->lastError;
+}
+
+/*!
+ Closes the document.
+*/
+void QPdfDocument::close()
+{
+ if (!d->doc)
+ return;
+
+ d->setStatus(Unloading);
+
+ d->clear();
+
+ if (!d->password.isEmpty()) {
+ d->password.clear();
+ emit passwordChanged();
+ }
+
+ d->setStatus(Null);
+}
+
+/*!
+ Returns the amount of pages for the loaded document or \c 0 if
+ no document is loaded.
+*/
+int QPdfDocument::pageCount() const
+{
+ return d->pageCount;
+}
+
+QSizeF QPdfDocument::pageSize(int page) const
+{
+ QSizeF result;
+ if (!d->doc || !d->checkPageComplete(page))
+ return result;
+
+ const QPdfMutexLocker lock;
+
+ FPDF_GetPageSizeByIndex(d->doc, page, &result.rwidth(), &result.rheight());
+ return result;
+}
+
+/*!
+ Renders the \a page into a QImage of size \a imageSize according to the
+ provided \a renderOptions.
+
+ Returns the rendered page or an empty image in case of an error.
+
+ Note: If the \a imageSize does not match the aspect ratio of the page in the
+ PDF document, the page is rendered scaled, so that it covers the
+ complete \a imageSize.
+*/
+QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions renderOptions)
+{
+ if (!d->doc || !d->checkPageComplete(page))
+ return QImage();
+
+ const QPdfMutexLocker lock;
+
+ QElapsedTimer timer;
+ if (Q_UNLIKELY(qLcDoc().isDebugEnabled()))
+ timer.start();
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ if (!pdfPage)
+ return QImage();
+
+ QImage result(imageSize, QImage::Format_ARGB32);
+ result.fill(Qt::transparent);
+ FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(result.width(), result.height(), FPDFBitmap_BGRA, result.bits(), result.bytesPerLine());
+
+ int rotation = 0;
+ switch (renderOptions.rotation()) {
+ case QPdf::Rotate0:
+ rotation = 0;
+ break;
+ case QPdf::Rotate90:
+ rotation = 1;
+ break;
+ case QPdf::Rotate180:
+ rotation = 2;
+ break;
+ case QPdf::Rotate270:
+ rotation = 3;
+ break;
+ }
+
+ const QPdf::RenderFlags renderFlags = renderOptions.renderFlags();
+ int flags = 0;
+ if (renderFlags & QPdf::RenderAnnotations)
+ flags |= FPDF_ANNOT;
+ if (renderFlags & QPdf::RenderOptimizedForLcd)
+ flags |= FPDF_LCD_TEXT;
+ if (renderFlags & QPdf::RenderGrayscale)
+ flags |= FPDF_GRAYSCALE;
+ if (renderFlags & QPdf::RenderForceHalftone)
+ flags |= FPDF_RENDER_FORCEHALFTONE;
+ if (renderFlags & QPdf::RenderTextAliased)
+ flags |= FPDF_RENDER_NO_SMOOTHTEXT;
+ if (renderFlags & QPdf::RenderImageAliased)
+ flags |= FPDF_RENDER_NO_SMOOTHIMAGE;
+ if (renderFlags & QPdf::RenderPathAliased)
+ flags |= FPDF_RENDER_NO_SMOOTHPATH;
+
+ if (renderOptions.scaledClipRect().isValid()) {
+ const QRect &clipRect = renderOptions.scaledClipRect();
+
+ // TODO take rotation into account, like cpdf_page.cpp lines 145-178
+ float x0 = clipRect.left();
+ float y0 = clipRect.top();
+ float x1 = clipRect.left();
+ float y1 = clipRect.bottom();
+ float x2 = clipRect.right();
+ float y2 = clipRect.top();
+ QSizeF origSize = pageSize(page);
+ QVector2D pageScale(1, 1);
+ if (!renderOptions.scaledSize().isNull()) {
+ pageScale = QVector2D(renderOptions.scaledSize().width() / float(origSize.width()),
+ renderOptions.scaledSize().height() / float(origSize.height()));
+ }
+ FS_MATRIX matrix {(x2 - x0) / result.width() * pageScale.x(),
+ (y2 - y0) / result.width() * pageScale.x(),
+ (x1 - x0) / result.height() * pageScale.y(),
+ (y1 - y0) / result.height() * pageScale.y(), -x0, -y0};
+
+ FS_RECTF clipRectF { 0, 0, float(imageSize.width()), float(imageSize.height()) };
+
+ FPDF_RenderPageBitmapWithMatrix(bitmap, pdfPage, &matrix, &clipRectF, flags);
+ qCDebug(qLcDoc) << "matrix" << matrix.a << matrix.b << matrix.c << matrix.d << matrix.e << matrix.f;
+ qCDebug(qLcDoc) << "page" << page << "region" << renderOptions.scaledClipRect()
+ << "size" << imageSize << "took" << timer.elapsed() << "ms";
+ } else {
+ FPDF_RenderPageBitmap(bitmap, pdfPage, 0, 0, result.width(), result.height(), rotation, flags);
+ qCDebug(qLcDoc) << "page" << page << "size" << imageSize << "took" << timer.elapsed() << "ms";
+ }
+
+ FPDFBitmap_Destroy(bitmap);
+
+ FPDF_ClosePage(pdfPage);
+ return result;
+}
+
+/*!
+ Returns information about the text on the given \a page that can be found
+ between the given \a start and \a end points, if any.
+*/
+QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end)
+{
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int startIndex = FPDFText_GetCharIndexAtPos(textPage, start.x(), pageHeight - start.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ int endIndex = FPDFText_GetCharIndexAtPos(textPage, end.x(), pageHeight - end.y(),
+ CharacterHitTolerance, CharacterHitTolerance);
+ if (startIndex >= 0 && endIndex != startIndex) {
+ if (startIndex > endIndex)
+ qSwap(startIndex, endIndex);
+ int count = endIndex - startIndex + 1;
+ QString text = d->getText(textPage, startIndex, count);
+ QVector<QPolygonF> bounds;
+ int rectCount = FPDFText_CountRects(textPage, startIndex, endIndex - startIndex);
+ for (int i = 0; i < rectCount; ++i) {
+ double l, r, b, t;
+ FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
+ QPolygonF poly;
+ poly << QPointF(l, pageHeight - t);
+ poly << QPointF(r, pageHeight - t);
+ poly << QPointF(r, pageHeight - b);
+ poly << QPointF(l, pageHeight - b);
+ poly << QPointF(l, pageHeight - t);
+ bounds << poly;
+ }
+ qCDebug(qLcDoc) << page << start << "->" << end << "found" << startIndex << "->" << endIndex << text;
+ return QPdfSelection(text, bounds);
+ }
+
+ qCDebug(qLcDoc) << page << start << "->" << end << "nothing found";
+ return QPdfSelection();
+}
+
+QPdfSelection QPdfDocument::getAllText(int page)
+{
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page);
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ int count = FPDFText_CountChars(textPage);
+ if (count < 1)
+ return QPdfSelection();
+ QString text = d->getText(textPage, 0, count);
+ QVector<QPolygonF> bounds;
+ int rectCount = FPDFText_CountRects(textPage, 0, count);
+ for (int i = 0; i < rectCount; ++i) {
+ double l, r, b, t;
+ FPDFText_GetRect(textPage, i, &l, &t, &r, &b);
+ QPolygonF poly;
+ poly << QPointF(l, pageHeight - t);
+ poly << QPointF(r, pageHeight - t);
+ poly << QPointF(r, pageHeight - b);
+ poly << QPointF(l, pageHeight - b);
+ poly << QPointF(l, pageHeight - t);
+ bounds << poly;
+ }
+ qCDebug(qLcDoc) << "on page" << page << "got" << count << "chars" << rectCount << "rects";
+ return QPdfSelection(text, bounds);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfdocument.cpp"
diff --git a/src/pdf/qpdfdocumentrenderoptions.qdoc b/src/pdf/qpdfdocumentrenderoptions.qdoc
new file mode 100644
index 000000000..cc5083f9d
--- /dev/null
+++ b/src/pdf/qpdfdocumentrenderoptions.qdoc
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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 "qpdfdocumentrenderoptions.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPdfDocumentRenderOptions
+ \since 5.10
+ \inmodule QtPdf
+
+ \brief The QPdfDocumentRenderOptions class holds the options to render a page from a PDF document.
+
+ \sa QPdfDocument
+*/
+
+/*!
+ \fn QPdfDocumentRenderOptions::QPdfDocumentRenderOptions()
+
+ Constructs a QPdfDocumentRenderOptions object.
+*/
+
+/*!
+ \fn QPdf::Rotation QPdfDocumentRenderOptions::rotation() const
+
+ Returns the rotation used for rendering a page from a PDF document.
+
+ \sa setRotation()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setRotation(QPdf::Rotation rotation)
+
+ Sets the \a rotation used for rendering a page from a PDF document.
+
+ \sa rotation()
+*/
+
+/*!
+ \fn QPdf::RenderFlags QPdfDocumentRenderOptions::renderFlags() const
+
+ Returns the special flags used for rendering a page from a PDF document.
+
+ \sa setRenderFlags()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setRenderFlags(QPdf::RenderFlags flags)
+
+ Sets the special \a flags used for rendering a page from a PDF document.
+
+ \sa renderFlags()
+*/
+
+/*!
+ \fn QRect QPdfDocumentRenderOptions::scaledClipRect() const
+
+ Returns the rectangular region to be clipped from the page after having
+ been scaled to \l scaledSize().
+
+ \sa setScaledClipRect()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setScaledClipRect(QRect rect)
+
+ Sets the region \a rect to be clipped from the page after having been
+ scaled to \l scaledSize().
+
+ \sa scaledClipRect()
+*/
+
+/*!
+ \fn QRect QPdfDocumentRenderOptions::scaledSize() const
+
+ Returns the \a size of the page to be rendered, in pixels.
+
+ \sa setScaledSize()
+*/
+
+/*!
+ \fn void QPdfDocumentRenderOptions::setScaledSize(QSize size)
+
+ Sets the \a size of the page to be rendered, in pixels.
+
+ \sa scaledSize()
+*/
+
+/*!
+ \fn bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs)
+ \relates QPdfDocumentRenderOptions
+
+ Returns \c true if the options \a lhs and \a rhs are different, otherwise
+ returns \c false.
+*/
+
+/*!
+ \fn bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs)
+ \relates QPdfDocumentRenderOptions
+
+ Returns \c true if the options \a lhs and \a rhs are equal,
+ otherwise returns \c false.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdf/qpdflinkmodel.cpp b/src/pdf/qpdflinkmodel.cpp
new file mode 100644
index 000000000..96e6ddd5c
--- /dev/null
+++ b/src/pdf/qpdflinkmodel.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** 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 "qpdflinkmodel_p.h"
+#include "qpdflinkmodel_p_p.h"
+#include "qpdfdocument_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdf_text.h"
+
+#include <QLoggingCategory>
+#include <QMetaEnum>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcLink, "qt.pdf.links")
+
+QPdfLinkModel::QPdfLinkModel(QObject *parent)
+ : QAbstractListModel(*(new QPdfLinkModelPrivate()), parent)
+{
+ QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));
+ for (int r = Qt::UserRole; r < int(Role::_Count); ++r)
+ m_roleNames.insert(r, QByteArray(rolesMetaEnum.valueToKey(r)).toLower());
+}
+
+QPdfLinkModel::~QPdfLinkModel() {}
+
+QHash<int, QByteArray> QPdfLinkModel::roleNames() const
+{
+ return m_roleNames;
+}
+
+int QPdfLinkModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QPdfLinkModel);
+ Q_UNUSED(parent)
+ return d->links.count();
+}
+
+QVariant QPdfLinkModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QPdfLinkModel);
+ const QPdfLinkModelPrivate::Link &link = d->links.at(index.row());
+ switch (Role(role)) {
+ case Role::Rect:
+ return link.rect;
+ case Role::Url:
+ return link.url;
+ case Role::Page:
+ return link.page;
+ case Role::Location:
+ return link.location;
+ case Role::Zoom:
+ return link.zoom;
+ case Role::_Count:
+ break;
+ }
+ if (role == Qt::DisplayRole)
+ return link.toString();
+ return QVariant();
+}
+
+QPdfDocument *QPdfLinkModel::document() const
+{
+ Q_D(const QPdfLinkModel);
+ return d->document;
+}
+
+void QPdfLinkModel::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfLinkModel);
+ if (d->document == document)
+ return;
+ disconnect(d->document, &QPdfDocument::statusChanged, this, &QPdfLinkModel::onStatusChanged);
+ connect(document, &QPdfDocument::statusChanged, this, &QPdfLinkModel::onStatusChanged);
+ d->document = document;
+ emit documentChanged();
+ if (page())
+ setPage(0);
+ else
+ d->update();
+}
+
+int QPdfLinkModel::page() const
+{
+ Q_D(const QPdfLinkModel);
+ return d->page;
+}
+
+void QPdfLinkModel::setPage(int page)
+{
+ Q_D(QPdfLinkModel);
+ if (d->page == page)
+ return;
+
+ d->page = page;
+ emit pageChanged(page);
+ d->update();
+}
+
+QPdfLinkModelPrivate::QPdfLinkModelPrivate() : QAbstractItemModelPrivate()
+{
+}
+
+void QPdfLinkModelPrivate::update()
+{
+ Q_Q(QPdfLinkModel);
+ if (!document || !document->d->doc)
+ return;
+ auto doc = document->d->doc;
+ const QPdfMutexLocker lock;
+ FPDF_PAGE pdfPage = FPDF_LoadPage(doc, page);
+ if (!pdfPage) {
+ qWarning() << "failed to load page" << page;
+ return;
+ }
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ q->beginResetModel();
+ links.clear();
+
+ // Iterate the ordinary links
+ int linkStart = 0;
+ bool ok = true;
+ while (ok) {
+ FPDF_LINK linkAnnot;
+ ok = FPDFLink_Enumerate(pdfPage, &linkStart, &linkAnnot);
+ if (!ok)
+ break;
+ FS_RECTF rect;
+ ok = FPDFLink_GetAnnotRect(linkAnnot, &rect);
+ if (!ok)
+ break;
+ Link linkData;
+ linkData.rect = QRectF(rect.left, pageHeight - rect.top,
+ rect.right - rect.left, rect.top - rect.bottom);
+ FPDF_DEST dest = FPDFLink_GetDest(doc, linkAnnot);
+ FPDF_ACTION action = FPDFLink_GetAction(linkAnnot);
+ if (FPDFAction_GetType(action) != PDFACTION_GOTO) {
+ qWarning() << "link action type" << FPDFAction_GetType(action) << "is not yet supported";
+ continue;
+ }
+ linkData.page = FPDFDest_GetDestPageIndex(doc, dest);
+ FPDF_BOOL hasX, hasY, hasZoom;
+ FS_FLOAT x, y, zoom;
+ ok = FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom);
+ if (!ok)
+ break;
+ if (hasX && hasY)
+ linkData.location = QPointF(x, pageHeight - y);
+ if (hasZoom)
+ linkData.zoom = zoom;
+ links << linkData;
+ }
+
+ // Iterate the web links
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ if (textPage) {
+ FPDF_PAGELINK webLinks = FPDFLink_LoadWebLinks(textPage);
+ if (webLinks) {
+ int count = FPDFLink_CountWebLinks(webLinks);
+ for (int i = 0; i < count; ++i) {
+ Link linkData;
+ int len = FPDFLink_GetURL(webLinks, i, nullptr, 0);
+ if (len < 1) {
+ qWarning() << "URL" << i << "has length" << len;
+ } else {
+ QVector<unsigned short> buf(len);
+ int got = FPDFLink_GetURL(webLinks, i, buf.data(), len);
+ Q_ASSERT(got == len);
+ linkData.url = QString::fromUtf16(buf.data(), got - 1);
+ }
+ FPDFLink_GetTextRange(webLinks, i, &linkData.textStart, &linkData.textCharCount);
+ len = FPDFLink_CountRects(webLinks, i);
+ for (int r = 0; r < len; ++r) {
+ double left, top, right, bottom;
+ bool success = FPDFLink_GetRect(webLinks, i, r, &left, &top, &right, &bottom);
+ if (success) {
+ linkData.rect = QRectF(left, pageHeight - top, right - left, top - bottom);
+ links << linkData;
+ }
+ }
+ }
+ FPDFLink_CloseWebLinks(webLinks);
+ }
+ FPDFText_ClosePage(textPage);
+ }
+
+ // All done
+ FPDF_ClosePage(pdfPage);
+ if (Q_UNLIKELY(qLcLink().isDebugEnabled())) {
+ for (const Link &l : links)
+ qCDebug(qLcLink) << l.rect << l.toString();
+ }
+ q->endResetModel();
+}
+
+void QPdfLinkModel::onStatusChanged(QPdfDocument::Status status)
+{
+ Q_D(QPdfLinkModel);
+ qCDebug(qLcLink) << "sees document statusChanged" << status;
+ if (status == QPdfDocument::Ready)
+ d->update();
+}
+
+QString QPdfLinkModelPrivate::Link::toString() const
+{
+ QString ret;
+ if (page >= 0)
+ return QLatin1String("page ") + QString::number(page) +
+ QLatin1String(" location ") + QString::number(location.x()) + QLatin1Char(',') + QString::number(location.y()) +
+ QLatin1String(" zoom ") + QString::number(zoom);
+ else
+ return url.toString();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdflinkmodel_p.cpp"
diff --git a/src/pdf/qpdfnamespace.qdoc b/src/pdf/qpdfnamespace.qdoc
new file mode 100644
index 000000000..96bb090e9
--- /dev/null
+++ b/src/pdf/qpdfnamespace.qdoc
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \namespace QPdf
+ \inmodule QtPdf
+ \keyword QPdf Namespace
+
+ \brief The QPdf namespace contains miscellaneous identifiers
+ used throughout the QtPdf module.
+*/
+
+/*!
+ \enum QPdf::Rotation
+
+ This enum describes the rotation of the page for rendering.
+
+ \value Rotate0 Do not rotate (the default)
+ \value Rotate90 Rotate 90 degrees clockwise
+ \value Rotate180 Rotate 180 degrees
+ \value Rotate270 Rotate 270 degrees clockwise
+
+ \sa QPdfDocument::render()
+*/
+/*!
+ \enum QPdf::RenderFlag
+
+ This enum is used to describe how a page should be rendered.
+
+ \value NoRenderFlags The default value, representing no flags.
+ \value RenderAnnotations The page is rendered with annotations.
+ \value RenderOptimizedForLcd The text of the page is rendered optimized for LCD display.
+ \value RenderGrayscale The page is rendered grayscale.
+ \value RenderForceHalftone Always use halftones for rendering if the output image is stretched.
+ \value RenderTextAliased Anti-aliasing is disabled for rendering text.
+ \value RenderImageAliased Anti-aliasing is disabled for rendering images.
+ \value RenderPathAliased Anti-aliasing is disabled for rendering paths.
+
+ \sa QPdfDocument::render()
+*/
+
diff --git a/src/pdf/qpdfpagenavigation.cpp b/src/pdf/qpdfpagenavigation.cpp
new file mode 100644
index 000000000..497c1c2eb
--- /dev/null
+++ b/src/pdf/qpdfpagenavigation.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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 "qpdfpagenavigation.h"
+
+#include "qpdfdocument.h"
+
+#include <private/qobject_p.h>
+
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+
+class QPdfPageNavigationPrivate : public QObjectPrivate
+{
+public:
+ QPdfPageNavigationPrivate()
+ : QObjectPrivate()
+ {
+ }
+
+ void update()
+ {
+ Q_Q(QPdfPageNavigation);
+
+ const bool documentAvailable = m_document && m_document->status() == QPdfDocument::Ready;
+
+ if (documentAvailable) {
+ const int newPageCount = m_document->pageCount();
+ if (m_pageCount != newPageCount) {
+ m_pageCount = newPageCount;
+ emit q->pageCountChanged(m_pageCount);
+ }
+ } else {
+ if (m_pageCount != 0) {
+ m_pageCount = 0;
+ emit q->pageCountChanged(m_pageCount);
+ }
+ }
+
+ if (m_currentPage != 0) {
+ m_currentPage = 0;
+ emit q->currentPageChanged(m_currentPage);
+ }
+
+ updatePrevNext();
+ }
+
+ void updatePrevNext()
+ {
+ Q_Q(QPdfPageNavigation);
+
+ const bool hasPreviousPage = m_currentPage > 0;
+ const bool hasNextPage = m_currentPage < (m_pageCount - 1);
+
+ if (m_canGoToPreviousPage != hasPreviousPage) {
+ m_canGoToPreviousPage = hasPreviousPage;
+ emit q->canGoToPreviousPageChanged(m_canGoToPreviousPage);
+ }
+
+ if (m_canGoToNextPage != hasNextPage) {
+ m_canGoToNextPage = hasNextPage;
+ emit q->canGoToNextPageChanged(m_canGoToNextPage);
+ }
+ }
+
+ void documentStatusChanged()
+ {
+ update();
+ }
+
+ Q_DECLARE_PUBLIC(QPdfPageNavigation)
+
+ QPointer<QPdfDocument> m_document = nullptr;
+ int m_currentPage = 0;
+ int m_pageCount = 0;
+ bool m_canGoToPreviousPage = false;
+ bool m_canGoToNextPage = false;
+
+ QMetaObject::Connection m_documentStatusChangedConnection;
+};
+
+/*!
+ \class QPdfPageNavigation
+ \since 5.10
+ \inmodule QtPdf
+
+ \brief The QPdfPageNavigation class handles the navigation through a PDF document.
+
+ \sa QPdfDocument
+*/
+
+
+/*!
+ Constructs a page navigation object with parent object \a parent.
+*/
+QPdfPageNavigation::QPdfPageNavigation(QObject *parent)
+ : QObject(*new QPdfPageNavigationPrivate, parent)
+{
+}
+
+/*!
+ Destroys the page navigation object.
+*/
+QPdfPageNavigation::~QPdfPageNavigation()
+{
+}
+
+/*!
+ \property QPdfPageNavigation::document
+ \brief The document instance on which this object navigates.
+
+ By default, this property is \c nullptr.
+
+ \sa document(), setDocument(), QPdfDocument
+*/
+
+/*!
+ Returns the document on which this object navigates, or a \c nullptr
+ if none has set before.
+
+ \sa QPdfDocument
+*/
+QPdfDocument* QPdfPageNavigation::document() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_document;
+}
+
+/*!
+ Sets the \a document this object navigates on.
+
+ After a new document has been set, the currentPage will be \c 0.
+
+ \sa QPdfDocument
+*/
+void QPdfPageNavigation::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfPageNavigation);
+
+ if (d->m_document == document)
+ return;
+
+ if (d->m_document)
+ disconnect(d->m_documentStatusChangedConnection);
+
+ d->m_document = document;
+ emit documentChanged(d->m_document);
+
+ if (d->m_document)
+ d->m_documentStatusChangedConnection = connect(d->m_document.data(), &QPdfDocument::statusChanged, this, [d](){ d->documentStatusChanged(); });
+
+ d->update();
+}
+
+/*!
+ \property QPdfPageNavigation::currentPage
+ \brief The current page number in the document.
+
+ \sa currentPage(), setCurrentPage()
+*/
+
+/*!
+ Returns the current page number or \c 0 if there is no document set.
+
+ After a document has been loaded, the currentPage will always be \c 0.
+*/
+int QPdfPageNavigation::currentPage() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_currentPage;
+}
+
+/*!
+ \fn void QPdfPageNavigation::setCurrentPage(int page)
+
+ Sets the current \a page number.
+*/
+void QPdfPageNavigation::setCurrentPage(int newPage)
+{
+ Q_D(QPdfPageNavigation);
+
+ if (newPage < 0 || newPage >= d->m_pageCount)
+ return;
+
+ if (d->m_currentPage == newPage)
+ return;
+
+ d->m_currentPage = newPage;
+ emit currentPageChanged(d->m_currentPage);
+
+ d->updatePrevNext();
+}
+
+/*!
+ \property QPdfPageNavigation::pageCount
+ \brief The number of pages in the document.
+
+ \sa pageCount()
+*/
+
+/*!
+ Returns the number of pages in the document or \c 0 if there
+ is no document set.
+*/
+int QPdfPageNavigation::pageCount() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_pageCount;
+}
+
+/*!
+ \property QPdfPageNavigation::canGoToPreviousPage
+ \brief Indicates whether there is a page before the current page.
+
+ \sa canGoToPreviousPage(), goToPreviousPage()
+*/
+
+/*!
+ Returns whether there is a page before the current one.
+*/
+bool QPdfPageNavigation::canGoToPreviousPage() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_canGoToPreviousPage;
+}
+
+/*!
+ \property QPdfPageNavigation::canGoToNextPage
+ \brief Indicates whether there is a page after the current page.
+
+ \sa canGoToNextPage(), goToNextPage()
+*/
+
+/*!
+ Returns whether there is a page after the current one.
+*/
+bool QPdfPageNavigation::canGoToNextPage() const
+{
+ Q_D(const QPdfPageNavigation);
+
+ return d->m_canGoToNextPage;
+}
+
+/*!
+ Changes the current page to the previous page.
+
+ If there is no previous page in the document, nothing happens.
+
+ \sa canGoToPreviousPage
+*/
+void QPdfPageNavigation::goToPreviousPage()
+{
+ Q_D(QPdfPageNavigation);
+
+ if (d->m_currentPage > 0)
+ setCurrentPage(d->m_currentPage - 1);
+}
+
+/*!
+ Changes the current page to the next page.
+
+ If there is no next page in the document, nothing happens.
+
+ \sa canGoToNextPage
+*/
+void QPdfPageNavigation::goToNextPage()
+{
+ Q_D(QPdfPageNavigation);
+
+ if (d->m_currentPage < d->m_pageCount - 1)
+ setCurrentPage(d->m_currentPage + 1);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfpagenavigation.cpp"
diff --git a/src/pdf/qpdfpagerenderer.cpp b/src/pdf/qpdfpagerenderer.cpp
new file mode 100644
index 000000000..31d9f4e1e
--- /dev/null
+++ b/src/pdf/qpdfpagerenderer.cpp
@@ -0,0 +1,361 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
+** 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 "qpdfpagerenderer.h"
+
+#include <private/qobject_p.h>
+#include <QMutex>
+#include <QPdfDocument>
+#include <QPointer>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+class RenderWorker : public QObject
+{
+ Q_OBJECT
+
+public:
+ RenderWorker();
+ ~RenderWorker();
+
+ void setDocument(QPdfDocument *document);
+
+public Q_SLOTS:
+ void requestPage(quint64 requestId, int page, QSize imageSize,
+ QPdfDocumentRenderOptions options);
+
+Q_SIGNALS:
+ void pageRendered(int page, QSize imageSize, const QImage &image,
+ QPdfDocumentRenderOptions options, quint64 requestId);
+
+private:
+ QPointer<QPdfDocument> m_document;
+ QMutex m_mutex;
+};
+
+class QPdfPageRendererPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QPdfPageRenderer)
+
+public:
+ QPdfPageRendererPrivate();
+ ~QPdfPageRendererPrivate();
+
+ void handleNextRequest();
+ void requestFinished(int page, QSize imageSize, const QImage &image,
+ QPdfDocumentRenderOptions options, quint64 requestId);
+
+ QPdfPageRenderer::RenderMode m_renderMode = QPdfPageRenderer::RenderMode::SingleThreaded;
+ QPointer<QPdfDocument> m_document;
+
+ struct PageRequest
+ {
+ quint64 id;
+ int pageNumber;
+ QSize imageSize;
+ QPdfDocumentRenderOptions options;
+ };
+
+ QVector<PageRequest> m_requests;
+ QVector<PageRequest> m_pendingRequests;
+ quint64 m_requestIdCounter = 1;
+
+ QThread *m_renderThread = nullptr;
+ QScopedPointer<RenderWorker> m_renderWorker;
+};
+
+Q_DECLARE_TYPEINFO(QPdfPageRendererPrivate::PageRequest, Q_PRIMITIVE_TYPE);
+
+
+RenderWorker::RenderWorker()
+ : m_document(nullptr)
+{
+}
+
+RenderWorker::~RenderWorker()
+{
+}
+
+void RenderWorker::setDocument(QPdfDocument *document)
+{
+ const QMutexLocker locker(&m_mutex);
+
+ if (m_document == document)
+ return;
+
+ m_document = document;
+}
+
+void RenderWorker::requestPage(quint64 requestId, int pageNumber, QSize imageSize,
+ QPdfDocumentRenderOptions options)
+{
+ const QMutexLocker locker(&m_mutex);
+
+ if (!m_document || m_document->status() != QPdfDocument::Ready)
+ return;
+
+ const QImage image = m_document->render(pageNumber, imageSize, options);
+
+ emit pageRendered(pageNumber, imageSize, image, options, requestId);
+}
+
+
+QPdfPageRendererPrivate::QPdfPageRendererPrivate()
+ : QObjectPrivate()
+ , m_renderWorker(new RenderWorker)
+{
+}
+
+QPdfPageRendererPrivate::~QPdfPageRendererPrivate()
+{
+ if (m_renderThread) {
+ m_renderThread->quit();
+ m_renderThread->wait();
+ }
+}
+
+void QPdfPageRendererPrivate::handleNextRequest()
+{
+ if (m_requests.isEmpty())
+ return;
+
+ const PageRequest request = m_requests.takeFirst();
+ m_pendingRequests.append(request);
+
+ QMetaObject::invokeMethod(m_renderWorker.data(), "requestPage", Qt::QueuedConnection,
+ Q_ARG(quint64, request.id), Q_ARG(int, request.pageNumber),
+ Q_ARG(QSize, request.imageSize), Q_ARG(QPdfDocumentRenderOptions,
+ request.options));
+}
+
+void QPdfPageRendererPrivate::requestFinished(int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId)
+{
+ Q_UNUSED(image);
+ Q_UNUSED(requestId);
+ const auto it = std::find_if(m_pendingRequests.begin(), m_pendingRequests.end(),
+ [page, imageSize, options](const PageRequest &request){ return request.pageNumber == page && request.imageSize == imageSize && request.options == options; });
+
+ if (it != m_pendingRequests.end())
+ m_pendingRequests.erase(it);
+}
+
+/*!
+ \class QPdfPageRenderer
+ \since 5.11
+ \inmodule QtPdf
+
+ \brief The QPdfPageRenderer class encapsulates the rendering of pages of a PDF document.
+
+ The QPdfPageRenderer contains a queue that collects all render requests that are invoked through
+ requestPage(). Depending on the configured RenderMode the QPdfPageRenderer processes this queue
+ in the main UI thread on next event loop invocation (\c RenderMode::SingleThreaded) or in a separate worker thread
+ (\c RenderMode::MultiThreaded) and emits the result through the pageRendered() signal for each request once
+ the rendering is done.
+
+ \sa QPdfDocument
+*/
+
+
+/*!
+ Constructs a page renderer object with parent object \a parent.
+*/
+QPdfPageRenderer::QPdfPageRenderer(QObject *parent)
+ : QObject(*new QPdfPageRendererPrivate(), parent)
+{
+ Q_D(QPdfPageRenderer);
+
+ qRegisterMetaType<QPdfDocumentRenderOptions>();
+
+ connect(d->m_renderWorker.data(), &RenderWorker::pageRendered, this,
+ [this,d](int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId) {
+ d->requestFinished(page, imageSize, image, options, requestId);
+ emit pageRendered(page, imageSize, image, options, requestId);
+ d->handleNextRequest();
+ });
+}
+
+/*!
+ Destroys the page renderer object.
+*/
+QPdfPageRenderer::~QPdfPageRenderer()
+{
+}
+
+/*!
+ \enum QPdfPageRenderer::RenderMode
+
+ This enum describes how the pages are rendered.
+
+ \value MultiThreaded All pages are rendered in a separate worker thread.
+ \value SingleThreaded All pages are rendered in the main UI thread (default).
+
+ \sa renderMode(), setRenderMode()
+*/
+
+/*!
+ \property QPdfPageRenderer::renderMode
+ \brief The mode the renderer uses to render the pages.
+
+ By default, this property is \c RenderMode::SingleThreaded.
+
+ \sa setRenderMode(), RenderMode
+*/
+
+/*!
+ Returns the mode of how the pages are rendered.
+
+ \sa RenderMode
+*/
+QPdfPageRenderer::RenderMode QPdfPageRenderer::renderMode() const
+{
+ Q_D(const QPdfPageRenderer);
+
+ return d->m_renderMode;
+}
+
+/*!
+ Sets the mode of how the pages are rendered to \a mode.
+
+ \sa RenderMode
+*/
+void QPdfPageRenderer::setRenderMode(RenderMode mode)
+{
+ Q_D(QPdfPageRenderer);
+
+ if (d->m_renderMode == mode)
+ return;
+
+ d->m_renderMode = mode;
+ emit renderModeChanged(d->m_renderMode);
+
+ if (d->m_renderMode == RenderMode::MultiThreaded) {
+ d->m_renderThread = new QThread;
+ d->m_renderWorker->moveToThread(d->m_renderThread);
+ d->m_renderThread->start();
+ } else {
+ d->m_renderThread->quit();
+ d->m_renderThread->wait();
+ delete d->m_renderThread;
+ d->m_renderThread = nullptr;
+
+ // pulling the object from another thread should be fine, once that thread is deleted
+ d->m_renderWorker->moveToThread(this->thread());
+ }
+}
+
+/*!
+ \property QPdfPageRenderer::document
+ \brief The document instance this object renders the pages from.
+
+ By default, this property is \c nullptr.
+
+ \sa document(), setDocument(), QPdfDocument
+*/
+
+/*!
+ Returns the document this objects renders the pages from, or a \c nullptr
+ if none has been set before.
+
+ \sa QPdfDocument
+*/
+QPdfDocument* QPdfPageRenderer::document() const
+{
+ Q_D(const QPdfPageRenderer);
+
+ return d->m_document;
+}
+
+/*!
+ Sets the \a document this object renders the pages from.
+
+ \sa QPdfDocument
+*/
+void QPdfPageRenderer::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfPageRenderer);
+
+ if (d->m_document == document)
+ return;
+
+ d->m_document = document;
+ emit documentChanged(d->m_document);
+
+ d->m_renderWorker->setDocument(d->m_document);
+}
+
+/*!
+ Requests the renderer to render the page \a pageNumber into a QImage of size \a imageSize
+ according to the provided \a options.
+
+ Once the rendering is done the pageRendered() signal is emitted with the result as parameters.
+
+ The return value is an ID that uniquely identifies the render request. If a request with the
+ same parameters is still in the queue, the ID of that queued request is returned.
+*/
+quint64 QPdfPageRenderer::requestPage(int pageNumber, QSize imageSize,
+ QPdfDocumentRenderOptions options)
+{
+ Q_D(QPdfPageRenderer);
+
+ if (!d->m_document || d->m_document->status() != QPdfDocument::Ready)
+ return 0;
+
+ for (const auto &request : qAsConst(d->m_pendingRequests)) {
+ if (request.pageNumber == pageNumber
+ && request.imageSize == imageSize
+ && request.options == options)
+ return request.id;
+ }
+
+ const auto id = d->m_requestIdCounter++;
+
+ QPdfPageRendererPrivate::PageRequest request;
+ request.id = id;
+ request.pageNumber = pageNumber;
+ request.imageSize = imageSize;
+ request.options = options;
+
+ d->m_requests.append(request);
+
+ d->handleNextRequest();
+
+ return id;
+}
+
+QT_END_NAMESPACE
+
+#include "qpdfpagerenderer.moc"
diff --git a/src/pdf/qpdfsearchmodel.cpp b/src/pdf/qpdfsearchmodel.cpp
new file mode 100644
index 000000000..27b7833fc
--- /dev/null
+++ b/src/pdf/qpdfsearchmodel.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** 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 "qpdfdestination.h"
+#include "qpdfdocument_p.h"
+#include "qpdfsearchmodel.h"
+#include "qpdfsearchmodel_p.h"
+#include "qpdfsearchresult_p.h"
+
+#include "third_party/pdfium/public/fpdf_doc.h"
+#include "third_party/pdfium/public/fpdf_text.h"
+
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/QMetaEnum>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcS, "qt.pdf.search")
+
+static const int UpdateTimerInterval = 100;
+static const int ContextChars = 64;
+static const double CharacterHitTolerance = 6.0;
+
+QPdfSearchModel::QPdfSearchModel(QObject *parent)
+ : QAbstractListModel(*(new QPdfSearchModelPrivate()), parent)
+{
+ QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));
+ for (int r = Qt::UserRole; r < int(Role::_Count); ++r) {
+ QByteArray roleName = QByteArray(rolesMetaEnum.valueToKey(r));
+ if (roleName.isEmpty())
+ continue;
+ roleName[0] = QChar::toLower(roleName[0]);
+ m_roleNames.insert(r, roleName);
+ }
+}
+
+QPdfSearchModel::~QPdfSearchModel() {}
+
+QHash<int, QByteArray> QPdfSearchModel::roleNames() const
+{
+ return m_roleNames;
+}
+
+int QPdfSearchModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QPdfSearchModel);
+ Q_UNUSED(parent)
+ return d->rowCountSoFar;
+}
+
+QVariant QPdfSearchModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QPdfSearchModel);
+ const auto pi = const_cast<QPdfSearchModelPrivate*>(d)->pageAndIndexForResult(index.row());
+ if (pi.page < 0)
+ return QVariant();
+ switch (Role(role)) {
+ case Role::Page:
+ return pi.page;
+ case Role::IndexOnPage:
+ return pi.index;
+ case Role::Location:
+ return d->searchResults[pi.page][pi.index].location();
+ case Role::ContextBefore:
+ return d->searchResults[pi.page][pi.index].contextBefore();
+ case Role::ContextAfter:
+ return d->searchResults[pi.page][pi.index].contextAfter();
+ case Role::_Count:
+ break;
+ }
+ if (role == Qt::DisplayRole) {
+ const QString ret = d->searchResults[pi.page][pi.index].contextBefore() +
+ QLatin1String("<b>") + d->searchString + QLatin1String("</b>") +
+ d->searchResults[pi.page][pi.index].contextAfter();
+ return ret;
+ }
+ return QVariant();
+}
+
+void QPdfSearchModel::updatePage(int page)
+{
+ Q_D(QPdfSearchModel);
+ d->doSearch(page);
+}
+
+QString QPdfSearchModel::searchString() const
+{
+ Q_D(const QPdfSearchModel);
+ return d->searchString;
+}
+
+void QPdfSearchModel::setSearchString(QString searchString)
+{
+ Q_D(QPdfSearchModel);
+ if (d->searchString == searchString)
+ return;
+
+ d->searchString = searchString;
+ beginResetModel();
+ d->clearResults();
+ emit searchStringChanged();
+ endResetModel();
+}
+
+QVector<QPdfSearchResult> QPdfSearchModel::resultsOnPage(int page) const
+{
+ Q_D(const QPdfSearchModel);
+ const_cast<QPdfSearchModelPrivate *>(d)->doSearch(page);
+ if (d->searchResults.count() <= page)
+ return {};
+ return d->searchResults[page];
+}
+
+QPdfSearchResult QPdfSearchModel::resultAtIndex(int index) const
+{
+ Q_D(const QPdfSearchModel);
+ const auto pi = const_cast<QPdfSearchModelPrivate*>(d)->pageAndIndexForResult(index);
+ if (pi.page < 0)
+ return QPdfSearchResult();
+ return d->searchResults[pi.page][pi.index];
+}
+
+QPdfDocument *QPdfSearchModel::document() const
+{
+ Q_D(const QPdfSearchModel);
+ return d->document;
+}
+
+void QPdfSearchModel::setDocument(QPdfDocument *document)
+{
+ Q_D(QPdfSearchModel);
+ if (d->document == document)
+ return;
+
+ d->document = document;
+ d->clearResults();
+ emit documentChanged();
+}
+
+void QPdfSearchModel::timerEvent(QTimerEvent *event)
+{
+ Q_D(QPdfSearchModel);
+ if (event->timerId() != d->updateTimerId)
+ return;
+ if (!d->document || d->nextPageToUpdate >= d->document->pageCount()) {
+ if (d->document)
+ qCDebug(qLcS, "done updating search results on %d pages", d->searchResults.count());
+ killTimer(d->updateTimerId);
+ d->updateTimerId = -1;
+ }
+ d->doSearch(d->nextPageToUpdate++);
+}
+
+QPdfSearchModelPrivate::QPdfSearchModelPrivate() : QAbstractItemModelPrivate()
+{
+}
+
+void QPdfSearchModelPrivate::clearResults()
+{
+ Q_Q(QPdfSearchModel);
+ rowCountSoFar = 0;
+ searchResults.clear();
+ pagesSearched.clear();
+ if (document) {
+ searchResults.resize(document->pageCount());
+ pagesSearched.resize(document->pageCount());
+ } else {
+ searchResults.resize(0);
+ pagesSearched.resize(0);
+ }
+ nextPageToUpdate = 0;
+ updateTimerId = q->startTimer(UpdateTimerInterval);
+}
+
+bool QPdfSearchModelPrivate::doSearch(int page)
+{
+ if (page < 0 || page >= pagesSearched.count() || searchString.isEmpty())
+ return false;
+ if (pagesSearched[page])
+ return true;
+ Q_Q(QPdfSearchModel);
+
+ const QPdfMutexLocker lock;
+ QElapsedTimer timer;
+ timer.start();
+ FPDF_PAGE pdfPage = FPDF_LoadPage(document->d->doc, page);
+ if (!pdfPage) {
+ qWarning() << "failed to load page" << page;
+ return false;
+ }
+ double pageHeight = FPDF_GetPageHeight(pdfPage);
+ FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage);
+ if (!textPage) {
+ qWarning() << "failed to load text of page" << page;
+ FPDF_ClosePage(pdfPage);
+ return false;
+ }
+ FPDF_SCHHANDLE sh = FPDFText_FindStart(textPage, searchString.utf16(), 0, 0);
+ QVector<QPdfSearchResult> newSearchResults;
+ while (FPDFText_FindNext(sh)) {
+ int idx = FPDFText_GetSchResultIndex(sh);
+ int count = FPDFText_GetSchCount(sh);
+ int rectCount = FPDFText_CountRects(textPage, idx, count);
+ QVector<QRectF> rects;
+ int startIndex = -1;
+ int endIndex = -1;
+ for (int r = 0; r < rectCount; ++r) {
+ double left, top, right, bottom;
+ FPDFText_GetRect(textPage, r, &left, &top, &right, &bottom);
+ rects << QRectF(left, pageHeight - top, right - left, top - bottom);
+ if (r == 0) {
+ startIndex = FPDFText_GetCharIndexAtPos(textPage, left, top,
+ CharacterHitTolerance, CharacterHitTolerance);
+ }
+ if (r == rectCount - 1) {
+ endIndex = FPDFText_GetCharIndexAtPos(textPage, right, top,
+ CharacterHitTolerance, CharacterHitTolerance);
+ }
+ qCDebug(qLcS) << rects.last() << "char idx" << startIndex << "->" << endIndex;
+ }
+ QString contextBefore, contextAfter;
+ if (startIndex >= 0 || endIndex >= 0) {
+ startIndex = qMax(0, startIndex - ContextChars);
+ endIndex += ContextChars;
+ int count = endIndex - startIndex + 1;
+ if (count > 0) {
+ QVector<ushort> buf(count + 1);
+ int len = FPDFText_GetText(textPage, startIndex, count, buf.data());
+ Q_ASSERT(len - 1 <= count); // len is number of characters written, including the terminator
+ QString context = QString::fromUtf16(buf.constData(), len - 1);
+ context = context.replace(QLatin1Char('\n'), QStringLiteral("\u23CE"));
+ context = context.remove(QLatin1Char('\r'));
+ // try to find the search string near the middle of the context if possible
+ int si = context.indexOf(searchString, ContextChars - 5, Qt::CaseInsensitive);
+ if (si < 0)
+ si = context.indexOf(searchString, Qt::CaseInsensitive);
+ if (si < 0)
+ qWarning() << "search string" << searchString << "not found in context" << context;
+ contextBefore = context.mid(0, si);
+ contextAfter = context.mid(si + searchString.length());
+ }
+ }
+ if (!rects.isEmpty())
+ newSearchResults << QPdfSearchResult(page, rects, contextBefore, contextAfter);
+ }
+ FPDFText_FindClose(sh);
+ FPDFText_ClosePage(textPage);
+ FPDF_ClosePage(pdfPage);
+ qCDebug(qLcS) << searchString << "took" << timer.elapsed() << "ms to find"
+ << newSearchResults.count() << "results on page" << page;
+
+ pagesSearched[page] = true;
+ searchResults[page] = newSearchResults;
+ if (newSearchResults.count() > 0) {
+ int rowsBefore = rowsBeforePage(page);
+ qCDebug(qLcS) << "from row" << rowsBefore << "rowCount" << rowCountSoFar << "increasing by" << newSearchResults.count();
+ rowCountSoFar += newSearchResults.count();
+ q->beginInsertRows(QModelIndex(), rowsBefore, rowsBefore + newSearchResults.count() - 1);
+ q->endInsertRows();
+ }
+ return true;
+}
+
+QPdfSearchModelPrivate::PageAndIndex QPdfSearchModelPrivate::pageAndIndexForResult(int resultIndex)
+{
+ const int pageCount = document->pageCount();
+ int totalSoFar = 0;
+ int previousTotalSoFar = 0;
+ for (int page = 0; page < pageCount; ++page) {
+ if (!pagesSearched[page])
+ doSearch(page);
+ totalSoFar += searchResults[page].count();
+ if (totalSoFar > resultIndex)
+ return {page, resultIndex - previousTotalSoFar};
+ previousTotalSoFar = totalSoFar;
+ }
+ return {-1, -1};
+}
+
+int QPdfSearchModelPrivate::rowsBeforePage(int page)
+{
+ int ret = 0;
+ for (int i = 0; i < page; ++i)
+ ret += searchResults[i].count();
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfsearchmodel.cpp"
diff --git a/src/pdf/qpdfsearchresult.cpp b/src/pdf/qpdfsearchresult.cpp
new file mode 100644
index 000000000..53da1c165
--- /dev/null
+++ b/src/pdf/qpdfsearchresult.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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 "qpdfsearchresult.h"
+#include "qpdfsearchresult_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QPdfSearchResult::QPdfSearchResult() :
+ QPdfSearchResult(new QPdfSearchResultPrivate()) { }
+
+QPdfSearchResult::QPdfSearchResult(int page, QVector<QRectF> rects, QString contextBefore, QString contextAfter) :
+ QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, contextBefore, contextAfter)) { }
+
+QPdfSearchResult::QPdfSearchResult(QPdfSearchResultPrivate *d) :
+ QPdfDestination(static_cast<QPdfDestinationPrivate *>(d)) { }
+
+QString QPdfSearchResult::contextBefore() const
+{
+ return static_cast<QPdfSearchResultPrivate *>(d.data())->contextBefore;
+}
+
+QString QPdfSearchResult::contextAfter() const
+{
+ return static_cast<QPdfSearchResultPrivate *>(d.data())->contextAfter;
+}
+
+QVector<QRectF> QPdfSearchResult::rectangles() const
+{
+ return static_cast<QPdfSearchResultPrivate *>(d.data())->rects;
+}
+
+QDebug operator<<(QDebug dbg, const QPdfSearchResult &searchResult)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QPdfSearchResult(page=" << searchResult.page()
+ << " contextBefore=" << searchResult.contextBefore()
+ << " contextAfter=" << searchResult.contextAfter()
+ << " rects=" << searchResult.rectangles();
+ dbg << ')';
+ return dbg;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfsearchresult.cpp"
diff --git a/src/pdf/qpdfselection.cpp b/src/pdf/qpdfselection.cpp
new file mode 100644
index 000000000..e334f0fb6
--- /dev/null
+++ b/src/pdf/qpdfselection.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** 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 "qpdfselection.h"
+#include "qpdfselection_p.h"
+#include <QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPdfSelection
+ \since 5.15
+ \inmodule QtPdf
+
+ \brief The QPdfSelection class defines a range of text that has been selected
+ on one page in a PDF document, and its geometric boundaries.
+
+ \sa QPdfDocument::getSelection()
+*/
+
+/*!
+ Constructs an invalid selection.
+
+ \sa valid
+*/
+QPdfSelection::QPdfSelection()
+ : d(new QPdfSelectionPrivate())
+{
+}
+
+/*!
+ \internal
+ Constructs a selection including the range of characters that make up the
+ \a text string, and which take up space on the page within the polygon
+ regions given in \a bounds.
+*/
+QPdfSelection::QPdfSelection(const QString &text, QVector<QPolygonF> bounds)
+ : d(new QPdfSelectionPrivate(text, bounds))
+{
+}
+
+QPdfSelection::QPdfSelection(QPdfSelectionPrivate *d)
+ : d(d)
+{
+}
+
+QPdfSelection::QPdfSelection(const QPdfSelection &other)
+ : d(other.d)
+{
+}
+
+QPdfSelection::QPdfSelection(QPdfSelection &&other) noexcept
+ : d(std::move(other.d))
+{
+}
+
+QPdfSelection::~QPdfSelection()
+{
+}
+
+QPdfSelection &QPdfSelection::operator=(const QPdfSelection &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \property QPdfSelection::valid
+
+ This property holds whether the selection is valid.
+*/
+bool QPdfSelection::isValid() const
+{
+ return !d->bounds.isEmpty();
+}
+
+/*!
+ \property QPdfSelection::bounds
+
+ This property holds a set of regions that the selected text occupies on the
+ page, represented as polygons. The coordinate system for the polygons has
+ the origin at the upper-left corner of the page, and the units are
+ \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}.
+
+ \note For now, the polygons returned from \l QPdfDocument::getSelection()
+ are always rectangles; but in the future it may be possible to represent
+ more complex regions.
+*/
+QVector<QPolygonF> QPdfSelection::bounds() const
+{
+ return d->bounds;
+}
+
+/*!
+ \property QPdfSelection::text
+
+ This property holds the selected text.
+*/
+QString QPdfSelection::text() const
+{
+ return d->text;
+}
+
+#if QT_CONFIG(clipboard)
+/*!
+ Copies \l text to the \l {QGuiApplication::clipboard()}{system clipboard}.
+*/
+void QPdfSelection::copyToClipboard(QClipboard::Mode mode) const
+{
+ QGuiApplication::clipboard()->setText(d->text, mode);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qpdfselection.cpp"
diff --git a/src/pdf/qtpdf.gni b/src/pdf/qtpdf.gni
new file mode 100644
index 000000000..c31f3e9a0
--- /dev/null
+++ b/src/pdf/qtpdf.gni
@@ -0,0 +1,7 @@
+include_dirs = [
+]
+
+deps = [
+ "//third_party/pdfium"
+]
+
diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp
new file mode 100644
index 000000000..670fe0bf9
--- /dev/null
+++ b/src/pdf/quick/plugin.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtQuick.Pdf 5.15
+ \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 5.15
+ \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(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");
+ qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfScrollablePageView.qml"), uri, 5, 15, "PdfScrollablePageView");
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/pdf/quick/plugins.qmltypes b/src/pdf/quick/plugins.qmltypes
new file mode 100644
index 000000000..a30361d33
--- /dev/null
+++ b/src/pdf/quick/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/pdf/quick/qml/+material/PdfStyle.qml b/src/pdf/quick/qml/+material/PdfStyle.qml
new file mode 100644
index 000000000..12df30466
--- /dev/null
+++ b/src/pdf/quick/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/pdf/quick/qml/+universal/PdfStyle.qml b/src/pdf/quick/qml/+universal/PdfStyle.qml
new file mode 100644
index 000000000..e92f2a080
--- /dev/null
+++ b/src/pdf/quick/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/pdf/quick/qml/PdfMultiPageView.qml b/src/pdf/quick/qml/PdfMultiPageView.qml
new file mode 100644
index 000000000..70bb5454f
--- /dev/null
+++ b/src/pdf/quick/qml/PdfMultiPageView.qml
@@ -0,0 +1,379 @@
+/****************************************************************************
+**
+** 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 = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
+ if (currentItem !== null)
+ currentItem.selection.selectAll()
+ }
+ function copySelectionToClipboard() {
+ var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
+ if (debug)
+ console.log("currentItem", currentItem, "sel", currentItem.selection.text)
+ if (currentItem !== null)
+ 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(0, 0), 0)
+ }
+ function goToLocation(page, location, zoom) {
+ if (zoom > 0)
+ root.renderScale = zoom
+ navigationStack.push(page, location, zoom)
+ searchModel.currentPage = page
+ }
+
+ // 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: root.document === undefined ? 0 : root.document.pageCount
+ 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)
+ contentWidth: document === undefined ? 0 : (rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale + vscroll.width + 2
+ // workaround for missing function (see https://codereview.qt-project.org/c/qt/qtdeclarative/+/248464)
+ function itemAtPos(x, y, includeSpacing) {
+ // we don't care about x (assume col 0), and assume includeSpacing is true
+ var ret = null
+ for (var i = 0; i < contentItem.children.length; ++i) {
+ var child = contentItem.children[i];
+ if (root.debug)
+ console.log(child, "@y", child.y)
+ if (child.y < y && (!ret || child.y > ret.y))
+ ret = child
+ }
+ if (root.debug && ret !== null)
+ console.log("given y", y, "found", ret, "@", ret.y)
+ return ret // the delegate with the largest y that is less than the given y
+ }
+ rowHeightProvider: function(row) { return (rot90 ? document.pagePointSize(row).width : document.pagePointSize(row).height) * root.renderScale }
+ 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: Math.max(root.width, (tableView.rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
+ implicitHeight: tableView.rot90 ? image.width : image.height
+ onImplicitWidthChanged: tableView.forceLayout()
+ objectName: "page " + index
+ property int delegateIndex: row // expose the context property for JS outside of the delegate
+ 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()
+ }
+ }
+ 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: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ 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
+ 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
+ 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
+ }
+ }
+ ScrollBar.vertical: ScrollBar {
+ id: vscroll
+ property bool moved: false
+ onPositionChanged: moved = true
+ onActiveChanged: {
+ var currentItem = tableView.itemAtPos(0, tableView.contentY + root.height / 2)
+ var currentPage = currentItem.delegateIndex
+ var currentLocation = Qt.point((tableView.contentX - currentItem.x + root.width / 2) / root.renderScale,
+ (tableView.contentY - currentItem.y + root.height / 2) / root.renderScale)
+ if (active) {
+ moved = false
+ navigationStack.push(currentPage, currentLocation, root.renderScale)
+ } else if (moved) {
+ navigationStack.update(currentPage, currentLocation, root.renderScale)
+ }
+ }
+ }
+ ScrollBar.horizontal: ScrollBar { }
+ }
+ onRenderScaleChanged: {
+ tableView.forceLayout()
+ var currentItem = tableView.itemAtPos(tableView.contentX + root.width / 2, tableView.contentY + root.height / 2)
+ if (currentItem !== undefined)
+ navigationStack.update(currentItem.delegateIndex, Qt.point(currentItem.x / renderScale, currentItem.y / renderScale), renderScale)
+ }
+ PdfNavigationStack {
+ id: navigationStack
+ onJumped: {
+ root.renderScale = zoom
+ tableView.contentX = Math.max(0, location.x - root.width / 2) * root.renderScale
+ tableView.contentY = tableView.originY + root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale
+ if (root.debug) {
+ console.log("going to page", page,
+ "@y", root.document.heightSumBeforePage(page, tableView.rowSpacing / root.renderScale) * root.renderScale,
+ "ended up @", tableView.contentY, "originY is", tableView.originY)
+ }
+ }
+ }
+ PdfSearchModel {
+ id: searchModel
+ document: root.document === undefined ? null : root.document
+ onCurrentPageChanged: if (currentPage != navigationStack.currentPage) root.goToPage(currentPage)
+ }
+}
diff --git a/src/pdf/quick/qml/PdfPageView.qml b/src/pdf/quick/qml/PdfPageView.qml
new file mode 100644
index 000000000..b90ad2d7f
--- /dev/null
+++ b/src/pdf/quick/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/pdf/quick/qml/PdfScrollablePageView.qml b/src/pdf/quick/qml/PdfScrollablePageView.qml
new file mode 100644
index 000000000..6076e57df
--- /dev/null
+++ b/src/pdf/quick/qml/PdfScrollablePageView.qml
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** 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)
+ }
+
+ 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
+ onJumped: {
+ root.renderScale = zoom
+ root.contentX = Math.max(0, location.x * root.renderScale - root.width / 2)
+ root.contentY = Math.max(0, location.y * root.renderScale - root.height / 2)
+ 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: tapHandler
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
+ }
+ }
+
+ 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/pdf/quick/qml/PdfStyle.qml b/src/pdf/quick/qml/PdfStyle.qml
new file mode 100644
index 000000000..090465ce6
--- /dev/null
+++ b/src/pdf/quick/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/pdf/quick/qmldir b/src/pdf/quick/qmldir
new file mode 100644
index 000000000..65fa95cda
--- /dev/null
+++ b/src/pdf/quick/qmldir
@@ -0,0 +1,4 @@
+module QtQuick.Pdf
+plugin pdfplugin
+classname QtQuick2PdfPlugin
+typeinfo plugins.qmltypes
diff --git a/src/pdf/quick/qquickpdfdocument.cpp b/src/pdf/quick/qquickpdfdocument.cpp
new file mode 100644
index 000000000..3d5f0fa10
--- /dev/null
+++ b/src/pdf/quick/qquickpdfdocument.cpp
@@ -0,0 +1,306 @@
+/****************************************************************************
+**
+** 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>
+#include <private/qguiapplication_p.h>
+
+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.path());
+}
+
+/*!
+ \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/pdf/quick/qquickpdfdocument_p.h b/src/pdf/quick/qquickpdfdocument_p.h
new file mode 100644
index 000000000..cefa4f756
--- /dev/null
+++ b/src/pdf/quick/qquickpdfdocument_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** 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 <QtPdf/QPdfDocument>
+#include <QDateTime>
+#include <QJSValue>
+#include <QQmlParserStatus>
+#include <QUrl>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+
+class 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/pdf/quick/qquickpdflinkmodel.cpp b/src/pdf/quick/qquickpdflinkmodel.cpp
new file mode 100644
index 000000000..f2ff3fd22
--- /dev/null
+++ b/src/pdf/quick/qquickpdflinkmodel.cpp
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** 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>
+#include <private/qguiapplication_p.h>
+
+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/pdf/quick/qquickpdflinkmodel_p.h b/src/pdf/quick/qquickpdflinkmodel_p.h
new file mode 100644
index 000000000..23ad6c8c1
--- /dev/null
+++ b/src/pdf/quick/qquickpdflinkmodel_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 "qquickpdfdocument_p.h"
+#include "../api/qpdflinkmodel_p.h"
+
+#include <QVariant>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class 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;
+ QVector<QPolygonF> m_linksGeometry;
+
+ Q_DISABLE_COPY(QQuickPdfLinkModel)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfLinkModel)
+
+#endif // QQUICKPDFLINKMODEL_P_H
diff --git a/src/pdf/quick/qquickpdfnavigationstack.cpp b/src/pdf/quick/qquickpdfnavigationstack.cpp
new file mode 100644
index 000000000..7ba317557
--- /dev/null
+++ b/src/pdf/quick/qquickpdfnavigationstack.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** 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;
+}
+
+/*!
+ \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;
+}
+
+/*!
+ \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 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)
+{
+ 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();
+ 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(',');
+ }();
+}
+
+/*!
+ \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(',');
+ }();
+}
+
+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/pdf/quick/qquickpdfnavigationstack_p.h b/src/pdf/quick/qquickpdfnavigationstack_p.h
new file mode 100644
index 000000000..8d7102fb1
--- /dev/null
+++ b/src/pdf/quick/qquickpdfnavigationstack_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** 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 "qquickpdfdocument_p.h"
+#include "../api/qpdfdestination_p.h"
+
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class 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);
+ 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:
+ QVector<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/pdf/quick/qquickpdfsearchmodel.cpp b/src/pdf/quick/qquickpdfsearchmodel.cpp
new file mode 100644
index 000000000..a4b457841
--- /dev/null
+++ b/src/pdf/quick/qquickpdfsearchmodel.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** 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(qLcS, "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
+*/
+QVector<QPolygonF> QQuickPdfSearchModel::currentResultBoundingPolygons() const
+{
+ QVector<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;
+}
+
+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
+*/
+QVector<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
+*/
+QVector<QPolygonF> QQuickPdfSearchModel::boundingPolygonsOnPage(int page)
+{
+ if (!document() || searchString().isEmpty() || page < 0 || page > document()->pageCount())
+ return {};
+
+ updatePage(page);
+
+ QVector<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(qLcS) << "currentResult was" << m_currentResult
+ << "requested" << currentResultWas << "on page" << currentPageWas
+ << "->" << currentResult << "on page" << m_currentPage;
+
+ m_currentResult = currentResult;
+ emit currentResultChanged();
+ emit currentResultBoundingPolygonsChanged();
+}
+
+/*!
+ \qmlproperty string PdfSearchModel::searchString
+
+ The string to search for.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfsearchmodel_p.h b/src/pdf/quick/qquickpdfsearchmodel_p.h
new file mode 100644
index 000000000..3e05f80e3
--- /dev/null
+++ b/src/pdf/quick/qquickpdfsearchmodel_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** 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 "qquickpdfdocument_p.h"
+#include "../api/qpdfsearchmodel.h"
+
+#include <QtCore/qvariant.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class 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(QVector<QPolygonF> currentPageBoundingPolygons READ currentPageBoundingPolygons NOTIFY currentPageBoundingPolygonsChanged)
+ Q_PROPERTY(QVector<QPolygonF> currentResultBoundingPolygons READ currentResultBoundingPolygons NOTIFY currentResultBoundingPolygonsChanged)
+
+public:
+ explicit QQuickPdfSearchModel(QObject *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument * document);
+
+ Q_INVOKABLE QVector<QPolygonF> boundingPolygonsOnPage(int page);
+
+ int currentPage() const { return m_currentPage; }
+ void setCurrentPage(int currentPage);
+
+ int currentResult() const { return m_currentResult; }
+ void setCurrentResult(int currentResult);
+
+ QVector<QPolygonF> currentPageBoundingPolygons() const;
+ QVector<QPolygonF> currentResultBoundingPolygons() const;
+
+signals:
+ void documentChanged();
+ void currentPageChanged();
+ void currentResultChanged();
+ void currentPageBoundingPolygonsChanged();
+ void currentResultBoundingPolygonsChanged();
+
+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/pdf/quick/qquickpdfselection.cpp b/src/pdf/quick/qquickpdfselection.cpp
new file mode 100644
index 000000000..5371e85e5
--- /dev/null
+++ b/src/pdf/quick/qquickpdfselection.cpp
@@ -0,0 +1,284 @@
+/****************************************************************************
+**
+** 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 <QQuickItem>
+#include <QQmlEngine>
+#include <QStandardPaths>
+#include <private/qguiapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \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.
+*/
+
+/*!
+ Constructs a SearchModel.
+*/
+QQuickPdfSelection::QQuickPdfSelection(QObject *parent)
+ : QObject(parent)
+{
+}
+
+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
+*/
+QVector<QPolygonF> QQuickPdfSelection::geometry() const
+{
+ return m_geometry;
+}
+
+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 geometryChanged();
+ }
+}
+
+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;
+ emit pageChanged();
+ resetPoints();
+}
+
+/*!
+ \qmlproperty point PdfSelection::fromPoint
+
+ The beginning location, in \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
+ from the upper-left corner of the page, from which to find selected text.
+ This can be bound to a scaled version of 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 \l {https://en.wikipedia.org/wiki/Point_(typography)}{points}
+ from the upper-left corner of the page, from which to find selected text.
+ This can be bound to a scaled version of 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_toPoint);
+ 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 geometryChanged();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/pdf/quick/qquickpdfselection_p.h b/src/pdf/quick/qquickpdfselection_p.h
new file mode 100644
index 000000000..bb4a50fed
--- /dev/null
+++ b/src/pdf/quick/qquickpdfselection_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** 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 <QPointF>
+#include <QPolygonF>
+#include <QVariant>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPdfDocument;
+
+class QQuickPdfSelection : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged)
+ Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
+ 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(QVector<QPolygonF> geometry READ geometry NOTIFY geometryChanged)
+
+public:
+ explicit QQuickPdfSelection(QObject *parent = nullptr);
+
+ QQuickPdfDocument *document() const;
+ void setDocument(QQuickPdfDocument * document);
+ int page() const;
+ void setPage(int page);
+ QPointF fromPoint() const;
+ void setFromPoint(QPointF fromPoint);
+ QPointF toPoint() const;
+ void setToPoint(QPointF toPoint);
+ bool hold() const;
+ void setHold(bool hold);
+
+ QString text() const;
+ QVector<QPolygonF> geometry() const;
+
+ Q_INVOKABLE void selectAll();
+#if QT_CONFIG(clipboard)
+ Q_INVOKABLE void copyToClipboard() const;
+#endif
+
+signals:
+ void documentChanged();
+ void pageChanged();
+ void fromPointChanged();
+ void toPointChanged();
+ void holdChanged();
+ void textChanged();
+ void geometryChanged();
+
+private:
+ void resetPoints();
+ void updateResults();
+
+private:
+ QQuickPdfDocument *m_document = nullptr;
+ QPointF m_fromPoint;
+ QPointF m_toPoint;
+ QString m_text;
+ QVector<QPolygonF> m_geometry;
+ int m_page = 0;
+ bool m_hold = false;
+
+ Q_DISABLE_COPY(QQuickPdfSelection)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPdfSelection)
+\
+#endif // QQUICKPDFSELECTION_P_H
diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro
new file mode 100644
index 000000000..b62b80346
--- /dev/null
+++ b/src/pdf/quick/quick.pro
@@ -0,0 +1,34 @@
+CXX_MODULE = qml
+TARGET = pdfplugin
+TARGETPATH = QtQuick/Pdf
+IMPORT_VERSION = 1.0
+
+#QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf
+
+PDF_QML_FILES = \
+ qml/PdfMultiPageView.qml \
+ qml/PdfPageView.qml \
+ qml/PdfScrollablePageView.qml \
+
+QML_FILES += $$PDF_QML_FILES qmldir
+
+RESOURCES += resources.qrc
+
+SOURCES += \
+ plugin.cpp \
+ qquickpdfdocument.cpp \
+ qquickpdflinkmodel.cpp \
+ qquickpdfnavigationstack.cpp \
+ qquickpdfsearchmodel.cpp \
+ qquickpdfselection.cpp \
+
+HEADERS += \
+ qquickpdfdocument_p.h \
+ qquickpdflinkmodel_p.h \
+ qquickpdfnavigationstack_p.h \
+ qquickpdfsearchmodel_p.h \
+ qquickpdfselection_p.h \
+
+QT += pdf quick-private gui gui-private core core-private qml qml-private
+
+load(qml_plugin)
diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc
new file mode 100644
index 000000000..8270a2028
--- /dev/null
+++ b/src/pdf/quick/resources.qrc
@@ -0,0 +1,10 @@
+<RCC>
+ <qresource prefix="/qt-project.org/qtpdf">
+ <file>qml/+material/PdfStyle.qml</file>
+ <file>qml/+universal/PdfStyle.qml</file>
+ <file>qml/PdfStyle.qml</file>
+ <file>qml/PdfMultiPageView.qml</file>
+ <file>qml/PdfPageView.qml</file>
+ <file>qml/PdfScrollablePageView.qml</file>
+ </qresource>
+</RCC>