diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2024-01-04 11:06:22 -0700 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2024-01-07 06:42:49 +0000 |
commit | 6ba530da27a7359d7d39b36d0792a7d929d5b274 (patch) | |
tree | 234b1761b784dfe14d834ebbb904b738df98b82f | |
parent | 38c44866200b393608c4cfc14910191fd3459945 (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.cpp | 14 | ||||
-rw-r--r-- | tests/auto/pdfquick/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/pdfquick/pdfpageimage/CMakeLists.txt | 30 | ||||
-rw-r--r-- | tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf | 317 | ||||
-rw-r--r-- | tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml | 16 | ||||
-rw-r--r-- | tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp | 131 |
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" |