diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2019-08-26 15:31:00 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2020-01-23 19:44:17 +0100 |
commit | b6dd845ec4a6bfb6b620686681e20d38a2f24101 (patch) | |
tree | cafcf5b7c8f3185fb7fa5dfe07b38cd9b17a8cb7 /src/pdf/quick | |
parent | 32fb888f3c5b6d415d84895e3cab594de026d3dd (diff) |
Add QPdfSearchModel, QML PdfSearchModel and PdfPageView
This enables searching a PDF for a text string and getting the
boundaries of the areas where it is found. The boundaries are returned
as polygons intended to be rendered with PathMultiline.
PdfPageView is a QML component intended to be a drop-in viewer for use
in applications that need the most common PDF viewing functionality.
More advanced applications are free to use it as a starting point for
customization.
Task-number: QTBUG-77507
Task-number: QTBUG-77514
Change-Id: Id08ac30224e41b6cdfb9300cc4288d5750259f78
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/pdf/quick')
-rw-r--r-- | src/pdf/quick/plugin.cpp | 4 | ||||
-rw-r--r-- | src/pdf/quick/qml/PdfPageView.qml | 120 | ||||
-rw-r--r-- | src/pdf/quick/qquickpdfdocument.cpp | 35 | ||||
-rw-r--r-- | src/pdf/quick/qquickpdfdocument_p.h | 4 | ||||
-rw-r--r-- | src/pdf/quick/qquickpdfsearchmodel.cpp | 165 | ||||
-rw-r--r-- | src/pdf/quick/qquickpdfsearchmodel_p.h | 103 | ||||
-rw-r--r-- | src/pdf/quick/quick.pro | 13 | ||||
-rw-r--r-- | src/pdf/quick/resources.qrc | 5 |
8 files changed, 447 insertions, 2 deletions
diff --git a/src/pdf/quick/plugin.cpp b/src/pdf/quick/plugin.cpp index 59aca576b..664ba51ab 100644 --- a/src/pdf/quick/plugin.cpp +++ b/src/pdf/quick/plugin.cpp @@ -39,6 +39,7 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlextensionplugin.h> #include "qquickpdfdocument_p.h" +#include "qquickpdfsearchmodel_p.h" QT_BEGIN_NAMESPACE @@ -79,6 +80,9 @@ public: qmlRegisterModule(uri, 2, QT_VERSION_MINOR); qmlRegisterType<QQuickPdfDocument>(uri, 5, 15, "PdfDocument"); + qmlRegisterType<QQuickPdfSearchModel>(uri, 5, 15, "PdfSearchModel"); + + qmlRegisterType(QUrl("qrc:/qt-project.org/qtpdf/qml/PdfPageView.qml"), uri, 5, 15, "PdfPageView"); } }; diff --git a/src/pdf/quick/qml/PdfPageView.qml b/src/pdf/quick/qml/PdfPageView.qml new file mode 100644 index 000000000..adcdc807a --- /dev/null +++ b/src/pdf/quick/qml/PdfPageView.qml @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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.15 +import QtQuick.Controls 2.15 +import QtQuick.Pdf 5.15 +import QtQuick.Shapes 1.15 + +Rectangle { + id: paper + width: image.width + height: image.height + transformOrigin: Item.TopLeft + + // public API + // TODO 5.15: required property + property var document: null + property real renderScale: 1 + property alias sourceSize: image.sourceSize + property alias currentPage: image.currentFrame + property alias pageCount: image.frameCount + property alias searchString: searchModel.searchString + property alias status: image.status + + property real __pageScale: image.paintedWidth / document.pagePointSize(image.currentFrame).width + + PdfSearchModel { + id: searchModel + document: paper.document + page: image.currentFrame + } + + Image { + id: image + source: document.status === PdfDocument.Ready ? document.source : "" + asynchronous: true + fillMode: Image.PreserveAspectFit + } + function reRenderIfNecessary() { + var newSourceWidth = image.sourceSize.width * paper.scale + var ratio = newSourceWidth / image.sourceSize.width + if (ratio > 1.1 || ratio < 0.9) { + image.sourceSize.width = newSourceWidth + image.sourceSize.height = 1 + paper.scale = 1 + } + } + onRenderScaleChanged: { + image.sourceSize.width = document.pagePointSize(image.currentFrame).width * renderScale + image.sourceSize.height = 0 + paper.scale = 1 + } + + Shape { + anchors.fill: parent + opacity: 0.25 + visible: image.status === Image.Ready + ShapePath { + strokeWidth: 1 + strokeColor: "blue" + fillColor: "cyan" + scale: Qt.size(paper.__pageScale, paper.__pageScale) + PathMultiline { + id: searchResultBoundaries + paths: searchModel.matchGeometry + } + } + } + PinchHandler { + id: pinch + minimumScale: 0.1 + maximumScale: 10 + minimumRotation: 0 + maximumRotation: 0 + onActiveChanged: if (!active) paper.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 + } +} diff --git a/src/pdf/quick/qquickpdfdocument.cpp b/src/pdf/quick/qquickpdfdocument.cpp index f0f40c227..73b3d4537 100644 --- a/src/pdf/quick/qquickpdfdocument.cpp +++ b/src/pdf/quick/qquickpdfdocument.cpp @@ -95,6 +95,41 @@ void QQuickPdfDocument::setSource(QUrl source) } /*! + \qmlproperty string Document::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 Document::password This property holds the document password. If the passwordRequired() diff --git a/src/pdf/quick/qquickpdfdocument_p.h b/src/pdf/quick/qquickpdfdocument_p.h index ee6195679..1ec7edb1a 100644 --- a/src/pdf/quick/qquickpdfdocument_p.h +++ b/src/pdf/quick/qquickpdfdocument_p.h @@ -64,6 +64,7 @@ class QQuickPdfDocument : public QObject, public QQmlParserStatus Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL) 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) @@ -86,6 +87,8 @@ public: 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); @@ -115,6 +118,7 @@ private: QUrl m_source; QPdfDocument m_doc; + friend class QQuickPdfSearchModel; friend class QQuickPdfSelection; Q_DISABLE_COPY(QQuickPdfDocument) diff --git a/src/pdf/quick/qquickpdfsearchmodel.cpp b/src/pdf/quick/qquickpdfsearchmodel.cpp new file mode 100644 index 000000000..8b0e88673 --- /dev/null +++ b/src/pdf/quick/qquickpdfsearchmodel.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** 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 <QQuickItem> +#include <QQmlEngine> +#include <QStandardPaths> +#include <private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \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) +{ +} + +QQuickPdfDocument *QQuickPdfSearchModel::document() const +{ + return m_quickDocument; +} + +void QQuickPdfSearchModel::setDocument(QQuickPdfDocument *document) +{ + if (document == m_quickDocument) + return; + m_quickDocument = document; + QPdfSearchModel::setDocument(&document->m_doc); +} + +/*! + \qmlproperty list<list<point>> PdfSearchModel::matchGeometry + + 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 + page: doc.currentPage + } + Shape { + ShapePath { + PathMultiline { + paths: searchModel.matchGeometry + } + } + } + \endqml + + \sa PathMultiline +*/ +QVector<QPolygonF> QQuickPdfSearchModel::matchGeometry() const +{ + return m_matchGeometry; +} + +/*! + \qmlproperty string PdfSearchModel::searchString + + The string to search for. +*/ +QString QQuickPdfSearchModel::searchString() const +{ + return m_searchString; +} + +void QQuickPdfSearchModel::setSearchString(QString searchString) +{ + if (m_searchString == searchString) + return; + + m_searchString = searchString; + emit searchStringChanged(); + updateResults(); +} + +/*! + \qmlproperty int PdfSearchModel::page + + The page number on which to search. + + \sa QtQuick::Image::currentFrame +*/ +int QQuickPdfSearchModel::page() const +{ + return m_page; +} + +void QQuickPdfSearchModel::setPage(int page) +{ + if (m_page == page) + return; + + m_page = page; + emit pageChanged(); + updateResults(); +} + +void QQuickPdfSearchModel::updateResults() +{ + if (!document() || (m_searchString.isEmpty() && !m_matchGeometry.isEmpty()) || m_page < 0 || m_page > document()->pageCount()) { + m_matchGeometry.clear(); + emit matchGeometryChanged(); + } + QVector<QRectF> m = QPdfSearchModel::matches(m_page, m_searchString); + QVector<QPolygonF> matches; + for (QRectF r : m) + matches << QPolygonF(r); + if (matches != m_matchGeometry) { + m_matchGeometry = matches; + emit matchGeometryChanged(); + } +} + +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..799ef825f --- /dev/null +++ b/src/pdf/quick/qquickpdfsearchmodel_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPDF module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 <QVariant> +#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 page READ page WRITE setPage NOTIFY pageChanged) + Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY searchStringChanged) + Q_PROPERTY(QVector<QPolygonF> matchGeometry READ matchGeometry NOTIFY matchGeometryChanged) + +public: + explicit QQuickPdfSearchModel(QObject *parent = nullptr); + + QQuickPdfDocument *document() const; + void setDocument(QQuickPdfDocument * document); + + int page() const; + void setPage(int page); + + QString searchString() const; + void setSearchString(QString searchString); + + QVector<QPolygonF> matchGeometry() const; + +signals: + void documentChanged(); + void pageChanged(); + void searchStringChanged(); + void matchGeometryChanged(); + +private: + void updateResults(); + +private: + QQuickPdfDocument *m_quickDocument = nullptr; + QString m_searchString; + QVector<QPolygonF> m_matchGeometry; + int m_page; + + Q_DISABLE_COPY(QQuickPdfSearchModel) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPdfSearchModel) + +#endif // QQUICKPDFSEARCHMODEL_P_H diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro index eb88bc003..cda768369 100644 --- a/src/pdf/quick/quick.pro +++ b/src/pdf/quick/quick.pro @@ -5,12 +5,21 @@ IMPORT_VERSION = 1.0 #QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf +PDF_QML_FILES = \ + qml/PdfPageView.qml \ + +QML_FILES += $$PDF_QML_FILES qmldir + +RESOURCES += resources.qrc + SOURCES += \ + plugin.cpp \ qquickpdfdocument.cpp \ - plugin.cpp + qquickpdfsearchmodel.cpp \ HEADERS += \ - qquickpdfdocument_p.h + qquickpdfdocument_p.h \ + qquickpdfsearchmodel_p.h \ QT += pdf quick-private gui gui-private core core-private qml qml-private diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc new file mode 100644 index 000000000..a3f34189c --- /dev/null +++ b/src/pdf/quick/resources.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/qt-project.org/qtpdf"> + <file>qml/PdfPageView.qml</file> + </qresource> +</RCC> |