summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2024-01-04 11:06:22 -0700
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2024-01-07 06:42:49 +0000
commit6ba530da27a7359d7d39b36d0792a7d929d5b274 (patch)
tree234b1761b784dfe14d834ebbb904b738df98b82f
parent38c44866200b393608c4cfc14910191fd3459945 (diff)
QQuickPdfPageImage::load(): fall back to base class impl to avoid crash
Plain Image is ok for loading one PDF page at a time, except that the pdf plugin has to create its own PDF document object if it cannot reuse an instance that is already open. So PdfPageImage was created just as an optimization, with the expectation that the document will be given to the document property. But in case someone doesn't understand and tries to set only the inherited Image.source property instead, fall back to QQuickImageBase::load() to avoid crashing. Amends 7b8832ca2b84d549c9d374550c3c46b3d4d42a38 If both the document and source properties are set, prefer the document (for the sake of performance). Add test coverage of other combinations of properties, while we're at it. Pick-to: 6.5 Fixes: QTBUG-104767 Task-number: QTBUG-77506 Change-Id: I1ee0d0bb2a6c5f399234ddddd969be02e7a6c020 Reviewed-by: Axel Spoerl <axel.spoerl@qt.io> (cherry picked from commit b60c00f4adec9ea4b75af0226b04a7125e166ae2) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit 2180ce8901ac1caddf5b7b5cd73ba67d2d08349a)
-rw-r--r--src/pdfquick/qquickpdfpageimage.cpp14
-rw-r--r--tests/auto/pdfquick/CMakeLists.txt1
-rw-r--r--tests/auto/pdfquick/pdfpageimage/CMakeLists.txt30
-rw-r--r--tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf317
-rw-r--r--tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml16
-rw-r--r--tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp131
6 files changed, 508 insertions, 1 deletions
diff --git a/src/pdfquick/qquickpdfpageimage.cpp b/src/pdfquick/qquickpdfpageimage.cpp
index 2ea8ebc12..f2da067f1 100644
--- a/src/pdfquick/qquickpdfpageimage.cpp
+++ b/src/pdfquick/qquickpdfpageimage.cpp
@@ -76,6 +76,18 @@ QQuickPdfDocument *QQuickPdfPageImage::document() const
void QQuickPdfPageImage::load()
{
Q_D(QQuickPdfPageImage);
+ QUrl url = source();
+ if (!d->doc || !d->doc->carrierFile()) {
+ if (!url.isEmpty()) {
+ qmlWarning(this) << "document property not set: falling back to inefficient loading of " << url;
+ QQuickImageBase::load();
+ }
+ return;
+ }
+ if (url != d->doc->resolvedSource()) {
+ url = d->doc->resolvedSource();
+ qmlWarning(this) << "document and source properties in conflict: preferring document source " << url;
+ }
auto carrierFile = d->doc->carrierFile();
static int thisRequestProgress = -1;
static int thisRequestFinished = -1;
@@ -86,7 +98,7 @@ void QQuickPdfPageImage::load()
QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()");
}
- d->pix.loadImageFromDevice(qmlEngine(this), carrierFile, d->url,
+ d->pix.loadImageFromDevice(qmlEngine(this), carrierFile, url,
d->sourceClipRect.toRect(), d->sourcesize * d->devicePixelRatio,
QQuickImageProviderOptions(), d->currentFrame, d->frameCount);
diff --git a/tests/auto/pdfquick/CMakeLists.txt b/tests/auto/pdfquick/CMakeLists.txt
index 9f79459b2..e6a3a460c 100644
--- a/tests/auto/pdfquick/CMakeLists.txt
+++ b/tests/auto/pdfquick/CMakeLists.txt
@@ -1 +1,2 @@
add_subdirectory(multipageview)
+add_subdirectory(pdfpageimage)
diff --git a/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt b/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt
new file mode 100644
index 000000000..da67d8721
--- /dev/null
+++ b/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_pdfpageimage
+ SOURCES
+ tst_pdfpageimage.cpp
+ ../shared/util.cpp ../shared/util.h
+ LIBRARIES
+ Qt::Gui
+ Qt::Quick
+ Qt::PdfQuickPrivate
+ TESTDATA ${test_data}
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_pdfpageimage CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_pdfpageimage CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
+
diff --git a/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf b/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf
new file mode 100644
index 000000000..aa0b99039
--- /dev/null
+++ b/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf
@@ -0,0 +1,317 @@
+%PDF-1.7
+
+1 0 obj
+<<
+ /Type /Catalog
+ /PageMode /FullScreen
+ /Outlines 6 0 R
+ /Pages 2 0 R
+ /Names 50 0 R
+ /PageLabels 23 0 R
+ /ViewerPreferences<</NonFullScreenPageMode (UseThumbs)>>
+>>
+endobj
+
+50 0 obj
+<<
+ /Dests <</Names [ (ToTest2) [4 0 R /XYZ 300 300 1] (ToTest3) [5 0 R /XYZ 290 10 0.5] (ToTest1) [3 0 R /XYZ 600 800 1] ]>>
+>>
+endobj
+
+23 0 obj
+<<
+ /Nums [0 <</S /D /P(test )>> 3 <</S /A >> 4<</S /R/St >> 5<</S /r/St >> ]
+ /Limits [0 5]
+>>
+endobj
+
+2 0 obj
+<<
+ /Type /Pages
+ /Kids [3 0 R 4 0 R 5 0 R]
+ /Count 3
+>>
+endobj
+
+3 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 10 600 800]
+ /Annots [24 0 R 25 0 R]
+ /Contents 16 0 R
+ /Resources <<
+ /Font <</F1 18 0 R>>
+ >>
+>>
+endobj
+
+24 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /A << /Type /Action
+ /S /GoTo
+ /D [5 0 R /FitR ¨C4 399 199 533]
+ >>
+ /Rect [10 690 150 720]
+
+>>
+endobj
+
+25 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest3)
+ /Rect [10 630 150 650]
+>>
+endobj
+
+
+16 0 obj
+<< /Length 0 >>
+ stream
+ BT
+ /F1 72 Tf
+ 200 200 TD
+ 0 0 1 RG
+ 5 Tr
+ (Test_1) Tj
+ 0 800 m
+ 600 0 l S
+ /F1 30 Tf
+ 0 1 0 RG
+ 1 Tr
+ -190 490 TD
+ (GO Test_2) Tj
+ 0 -50 TD
+ 5 w
+ 2 Tr
+ 1 0 0 RG
+ (GO Test_3) Tj
+ ET
+ endstream
+endobj
+
+
+endobj
+
+18 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F1
+ /BaseFont /Helvetica
+>>
+endobj
+
+4 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [10 0 500 700]
+ /Annots [60 0 R]
+ /Contents 19 0 R
+ /Resources <<
+ /Font <</F2 20 0 R>>
+ >>
+>>
+endobj
+
+19 0 obj
+<< /Length 0 >>
+stream
+BT
+ 1 -0.7 0 1 30 100 cm
+ /F2 50 Tf
+ 10 50 TD
+ (TEST_2) Tj
+
+ 1 0.7 0 1 -30 -100 cm
+ /F2 25 Tf
+ 1 0 1 RG
+ 7 w
+ 100 60 TD
+
+ (GO Test_1) Tj
+ 100 100 140 40 re S f
+ET
+endstream
+endobj
+
+20 0 obj
+<<
+ /Type /Font
+ /Subtype /TrueType
+ /Name /F2
+ /BaseFont /NewYork , Bold
+ /FirstChar 0
+ /LastChar 255
+ /Widths 23 0 R
+ /FontDescriptor 7 0 R
+ /Encoding /MacRomanEncoding
+>>
+endobj
+
+60 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest1)
+ /Rect [110 110 230 150]
+>>
+endobj
+
+5 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [-10 -10 400 600]
+ /Annots [61 0 R]
+ /Contents 21 0 R
+ /Resources << /Font <</F3 22 0 R>> >>
+>>
+endobj
+
+21 0 obj
+<< /Length 0 >>
+stream
+BT
+ /F3 30 Tf
+ 290 10 TD
+ (TEST_3) Tj
+ -50 90 TD
+ (GO Test_2)Tj
+ET
+endstream
+endobj
+
+22 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F3
+ /BaseFont /Courier-Bold
+>>
+endobj
+
+61 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /Rect [240 90 400 130]
+>>
+
+6 0 obj
+<<
+ /Type /Outlines
+ /First 7 0 R
+ /Last 11 0 R
+ /Count 4 0 R
+>>
+endobj
+
+7 0 obj
+<<
+ /Title (First)
+ /Parent 6 0 R
+ /Next 8 0 R
+ /C [1 0 0]
+ /Dest [ 3 0 R /XYZ 600 800 0.5 ]
+>>
+endobj
+
+8 0 obj
+<<
+ /Title (Second)
+ /Parent 6 0 R
+ /Prev 7 0 R
+ /Next 9 0 R
+ /C [0 1 0]
+ % /Dest [ 4 0 R /XYZ 500 700 null ]
+/Dest (ToTest2)
+>>
+endobj
+
+9 0 obj
+<<
+ /Title (Third)
+ /Parent 6 0 R
+ /Prev 8 0 R
+ /Next 10 0 R
+ /C [0 0 1]
+ /Dest [ 5 0 R /XYZ 400 600 0.8 ]
+>>
+endobj
+
+10 0 obj
+<<
+ /Title (Fourth)
+ /Parent 6 0 R
+ /Prev 9 0 R
+ /Next 11 0 R
+>>
+endobj
+
+11 0 obj
+<<
+ /Title (Fivth)
+ /Parent 6 0 R
+ /Prev 10 0 R
+ /First 12 0 R
+ /Last 15 0 R
+ /Count 4
+>>
+endobj
+
+12 0 obj
+<<
+ /Title (Fivth_1)
+ /Parent 11 0 R
+ /Next 13 0 R
+>>
+endobj
+
+13 0 obj
+<<
+ /Title (Fivth_2)
+ /Parent 11 0 R
+ /Prev 12 0 R
+ /Next 14 0 R
+>>
+endobj
+
+14 0 obj
+<<
+ /Title (Fivth_3)
+ /Parent 11 0 R
+ /Prev 13 0 R
+ /Next 15 0 R
+>>
+endobj
+
+15 0 obj
+<<
+ /Title (Fivth_4)
+ /Parent 11 0 R
+ /Prev 14 0 R
+>>
+endobj
+
+
+
+
+xref
+0000000000 65536 f
+
+trailer
+<<
+ /Size 0
+ /Root 1 0 R
+>>
+startxref
+0
+%%EOF
diff --git a/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml b/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml
new file mode 100644
index 000000000..a268bf14b
--- /dev/null
+++ b/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml
@@ -0,0 +1,16 @@
+import QtQuick
+import QtQuick.Pdf
+
+Item {
+ width: 320
+ height: 320
+
+ PdfDocument {
+ id: doc
+ source: "bookmarksAndLinks.pdf"
+ }
+
+ PdfPageImage {
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp
new file mode 100644
index 000000000..d2c9c8709
--- /dev/null
+++ b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp
@@ -0,0 +1,131 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QRegularExpression>
+#include <QSignalSpy>
+#include <QTest>
+#include <QtQuick/QQuickView>
+#include <QtPdfQuick/private/qquickpdfdocument_p.h>
+#include <QtPdfQuick/private/qquickpdfpageimage_p.h>
+#include "../shared/util.h"
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests")
+
+// #define DEBUG_WRITE_OUTPUT
+
+class tst_PdfPageImage : public QQuickDataTest
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void settableProperties_data();
+ void settableProperties();
+
+public:
+ enum Property {
+ Source = 0x01,
+ Document = 0x02,
+ SourceSize = 0x04,
+ SourceClipRect = 0x08,
+ MipMap = 0x10,
+ AutoTransform = 0x20,
+ Asynchronous = 0x40,
+ NoCache = 0x80,
+ Mirror = 0x100,
+ MirrorVertically = 0x200,
+ ColorSpace = 0x400,
+ };
+ Q_DECLARE_FLAGS(Properties, Property)
+ Q_FLAG(Properties)
+
+private:
+#ifdef DEBUG_WRITE_OUTPUT
+ QTemporaryDir m_tmpDir;
+#endif
+};
+
+void tst_PdfPageImage::settableProperties_data()
+{
+ QTest::addColumn<tst_PdfPageImage::Properties>("toSet");
+ QTest::addColumn<QSize>("expectedSize");
+ QTest::addColumn<QRegularExpression>("expectedWarning");
+
+ const QRegularExpression NoWarning;
+ const qreal dpr = qGuiApp->devicePixelRatio();
+
+ QTest::newRow("source") << Properties(Source) << (QSizeF(600, 790) * dpr).toSize()
+ << QRegularExpression("document property not set: falling back to inefficient loading"); // QTBUG-104767
+ QTest::newRow("document") << Properties(Document) << QSize(600, 790) << NoWarning;
+ QTest::newRow("source and document") << Properties(Source | Document) << QSize(600, 790)
+ << QRegularExpression("document and source properties in conflict");
+ QTest::newRow("document and sourceSize") << Properties(Document | SourceSize) << QSize(100, 100) << NoWarning;
+ QTest::newRow("document and sourceClipRect") << Properties(Document | SourceClipRect) << QSize(100, 100) << NoWarning;
+ QTest::newRow("document and autoTransform") << Properties(Document | AutoTransform) << QSize(600, 790) << NoWarning;
+ QTest::newRow("document and async") << Properties(Document | Asynchronous) << QSize(600, 790) << NoWarning;
+ QTest::newRow("document and nocache") << Properties(Document | NoCache) << QSize(600, 790) << NoWarning;
+ QTest::newRow("document and mirror") << Properties(Document | Mirror) << QSize(600, 790) << NoWarning;
+ QTest::newRow("document and mirrorVertically") << Properties(Document | MirrorVertically) << QSize(600, 790) << NoWarning;
+ QTest::newRow("document and colorSpace") << Properties(Document | ColorSpace) << QSize(600, 790) << NoWarning;
+}
+
+void tst_PdfPageImage::settableProperties()
+{
+ QFETCH(tst_PdfPageImage::Properties, toSet);
+ QFETCH(QSize, expectedSize);
+ QFETCH(QRegularExpression, expectedWarning);
+
+ QQuickView window;
+ if (!expectedWarning.pattern().isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, expectedWarning);
+ QVERIFY(showView(window, testFileUrl("pdfPageImage.qml")));
+ QQuickPdfPageImage *pdfImage = window.rootObject()->findChild<QQuickPdfPageImage *>();
+ QVERIFY(pdfImage);
+ QQuickPdfDocument *doc = window.rootObject()->findChild<QQuickPdfDocument *>();
+ QVERIFY(doc);
+ if (toSet.testFlag(Document))
+ pdfImage->setDocument(doc);
+ if (toSet.testFlag(Source))
+ pdfImage->setSource(doc->source());
+ if (toSet.testFlag(SourceSize))
+ pdfImage->setSourceSize({100, 100});
+ if (toSet.testFlag(SourceClipRect))
+ pdfImage->setSourceClipRect({100, 100, 100, 100});
+ if (toSet.testFlag(MipMap))
+ pdfImage->setMipmap(true);
+ if (toSet.testFlag(AutoTransform))
+ pdfImage->setAutoTransform(true);
+ if (toSet.testFlag(Asynchronous)) {
+ QCOMPARE(pdfImage->asynchronous(), false);
+ // test the opposite of the default
+ pdfImage->setAsynchronous(true);
+ }
+ if (toSet.testFlag(NoCache)) {
+ QCOMPARE(pdfImage->cache(), true);
+ // test the opposite of the default
+ pdfImage->setCache(false);
+ }
+ if (toSet.testFlag(Mirror)) {
+ QCOMPARE(pdfImage->mirror(), false);
+ pdfImage->setMirror(true);
+ }
+ if (toSet.testFlag(MirrorVertically)) {
+ QCOMPARE(pdfImage->mirrorVertically(), false);
+ pdfImage->setMirrorVertically(true);
+ }
+ if (toSet.testFlag(ColorSpace))
+ pdfImage->setColorSpace(QColorSpace::ProPhotoRgb);
+ QTRY_COMPARE(pdfImage->status(), QQuickPdfPageImage::Ready);
+ const QImage img = pdfImage->image();
+ QCOMPARE(img.size(), expectedSize);
+#ifdef DEBUG_WRITE_OUTPUT
+ m_tmpDir.setAutoRemove(false);
+ const auto path = m_tmpDir.filePath(QString::fromLocal8Bit(QTest::currentDataTag()) + ".png");
+ qCDebug(lcTests) << "saving to" << path;
+ img.save(path);
+#endif
+}
+
+QTEST_MAIN(tst_PdfPageImage)
+#include "tst_pdfpageimage.moc"