From 41e1ac9e6e1f1d98a5901253e89d7b524d3cdf6b Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 21 Jul 2021 11:32:35 +0200 Subject: Return printToPdf to QWebEnginePage It doesn't use QPrinter and can be done using QtGui classes only. Change-Id: I0e14563c1bb5e93d9803bb1a807f702b1a2a5315 Reviewed-by: Qt CI Bot Reviewed-by: Kirill Burtsev (cherry picked from commit b405ed26336a44ad4f359cdb4e1909e31c113cd4) Reviewed-by: Qt Cherry-pick Bot --- src/core/api/qwebenginepage.cpp | 96 +++++++++++++++++++++++++++- src/core/api/qwebenginepage.h | 12 ++++ src/core/api/qwebenginepage_p.h | 7 +- src/webenginewidgets/api/qwebengineview.cpp | 59 +++-------------- src/webenginewidgets/api/qwebengineview_p.h | 6 +- tests/auto/widgets/printing/tst_printing.cpp | 12 ++-- 6 files changed, 130 insertions(+), 62 deletions(-) diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp index c17276a1f..17e138920 100644 --- a/src/core/api/qwebenginepage.cpp +++ b/src/core/api/qwebenginepage.cpp @@ -321,6 +321,8 @@ void QWebEnginePagePrivate::loadFinished(QWebEngineLoadingInfo info) void QWebEnginePagePrivate::didPrintPageToPdf(const QString &filePath, bool success) { + Q_Q(QWebEnginePage); + Q_EMIT q->pdfPrintingFinished(filePath, success); if (view) view->didPrintPageToPdf(filePath, success); } @@ -470,8 +472,28 @@ void QWebEnginePagePrivate::didFetchDocumentInnerText(quint64 requestId, const Q void QWebEnginePagePrivate::didPrintPage(quint64 requestId, QSharedPointer result) { +#if QT_CONFIG(webengine_printing_and_pdf) + Q_Q(QWebEnginePage); + + // If no currentPrinter is set that means that were printing to PDF only. + if (!currentPrinter) { + if (!result.data()) + return; + if (auto callback = m_pdfResultCallbacks.take(requestId)) + callback(*(result.data())); + return; + } + if (view) - view->didPrintPage(requestId, result); + view->didPrintPage(currentPrinter, result); + else + currentPrinter = nullptr; +#else + // we should never enter this branch, but just for safe-keeping... + Q_UNUSED(result); + if (auto callback = m_pdfResultCallbacks.take(requestId)) + callback(QByteArray()); +#endif } bool QWebEnginePagePrivate::passOnFocus(bool reverse) @@ -1688,6 +1710,10 @@ void QWebEnginePagePrivate::setToolTip(const QString &toolTipText) void QWebEnginePagePrivate::printRequested() { + Q_Q(QWebEnginePage); + QTimer::singleShot(0, q, [q]() { + Q_EMIT q->printRequested(); + }); if (view) view->printRequested(); } @@ -2233,6 +2259,74 @@ QSizeF QWebEnginePage::contentsSize() const \sa createWindow(), QWebEngineNewWindowRequest::openIn() */ +/*! + \fn void QWebEnginePage::pdfPrintingFinished(const QString &filePath, bool success) + + This signal is emitted when printing the web page into a PDF file has + finished. + \a filePath will contain the path the file was requested to be created + at, and \a success will be \c true if the file was successfully created and + \c false otherwise. + + \sa printToPdf() +*/ + +/*! + Renders the current content of the page into a PDF document and saves it + in the location specified in \a filePath. + The page size and orientation of the produced PDF document are taken from + the values specified in \a pageLayout, while the range of pages printed is + taken from \a ranges with the default being printing all pages. + + This method issues an asynchronous request for printing the web page into + a PDF and returns immediately. + To be informed about the result of the request, connect to the signal + pdfPrintingFinished(). + + If a file already exists at the provided file path, it will be overwritten. + \sa pdfPrintingFinished() +*/ +void QWebEnginePage::printToPdf(const QString &filePath, const QPageLayout &layout, const QPageRanges &ranges) +{ +#if QT_CONFIG(webengine_printing_and_pdf) + Q_D(QWebEnginePage); + d->ensureInitialized(); + d->adapter->printToPDF(layout, ranges, filePath); +#else + Q_UNUSED(filePath); + Q_UNUSED(layout); + Q_UNUSED(ranges); +#endif +} + +/*! + Renders the current content of the page into a PDF document and returns a byte array containing the PDF data + as parameter to \a resultCallback. + The page size and orientation of the produced PDF document are taken from the values specified in \a pageLayout, + while the range of pages printed is taken from \a ranges with the default being printing all pages. + + The \a resultCallback must take a const reference to a QByteArray as parameter. If printing was successful, this byte array + will contain the PDF data, otherwise, the byte array will be empty. + + \warning We guarantee that the callback (\a resultCallback) is always called, but it might be done + during page destruction. When QWebEnginePage is deleted, the callback is triggered with an invalid + value and it is not safe to use the corresponding QWebEnginePage or QWebEngineView instance inside it. +*/ +void QWebEnginePage::printToPdf(const std::function &resultCallback, const QPageLayout &layout, const QPageRanges &ranges) +{ + Q_D(QWebEnginePage); +#if QT_CONFIG(webengine_printing_and_pdf) + d->ensureInitialized(); + quint64 requestId = d->adapter->printToPDFCallbackResult(layout, ranges); + d->m_pdfResultCallbacks.insert(requestId, resultCallback); +#else + Q_UNUSED(layout); + Q_UNUSED(ranges); + if (resultCallback) + resultCallback(QByteArray()); +#endif +} + /*! \internal */ diff --git a/src/core/api/qwebenginepage.h b/src/core/api/qwebenginepage.h index 1bdcf6539..9f35cffeb 100644 --- a/src/core/api/qwebenginepage.h +++ b/src/core/api/qwebenginepage.h @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include @@ -301,6 +303,13 @@ public: bool recentlyAudible() const; qint64 renderProcessPid() const; + void printToPdf(const QString &filePath, + const QPageLayout &layout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF()), + const QPageRanges &ranges = {}); + void printToPdf(const std::function &resultCallback, + const QPageLayout &layout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF()), + const QPageRanges &ranges = {}); + void setInspectedPage(QWebEnginePage *page); QWebEnginePage *inspectedPage() const; void setDevToolsPage(QWebEnginePage *page); @@ -355,6 +364,9 @@ Q_SIGNALS: void recentlyAudibleChanged(bool recentlyAudible); void renderProcessPidChanged(qint64 pid); + void pdfPrintingFinished(const QString &filePath, bool success); + void printRequested(); + void visibleChanged(bool visible); void lifecycleStateChanged(LifecycleState state); diff --git a/src/core/api/qwebenginepage_p.h b/src/core/api/qwebenginepage_p.h index f9eb2ad71..40b6ddd29 100644 --- a/src/core/api/qwebenginepage_p.h +++ b/src/core/api/qwebenginepage_p.h @@ -70,6 +70,7 @@ class WebContentsAdapter; } QT_BEGIN_NAMESPACE +class QPrinter; class QWebEngineFindTextResult; class QWebEngineHistory; class QWebEnginePage; @@ -102,7 +103,7 @@ public: virtual void unhandledKeyEvent(QKeyEvent *event) = 0; virtual bool passOnFocus(bool reverse) = 0; virtual QObject *accessibilityParentObject() = 0; - virtual void didPrintPage(quint64 requestId, QSharedPointer result) = 0; + virtual void didPrintPage(QPrinter *&printer, QSharedPointer result) = 0; virtual void didPrintPageToPdf(const QString &filePath, bool success) = 0; virtual void printRequested() = 0; }; @@ -222,9 +223,13 @@ public: qreal defaultZoomFactor; QTimer wasShownTimer; QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget *widget = nullptr; +#if QT_CONFIG(webengine_printing_and_pdf) + QPrinter *currentPrinter = nullptr; +#endif mutable QMap> m_variantCallbacks; mutable QMap> m_stringCallbacks; + QMap> m_pdfResultCallbacks; mutable QAction *actions[QWebEnginePage::WebActionCount]; }; diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp index aed80427c..d312fe70c 100644 --- a/src/webenginewidgets/api/qwebengineview.cpp +++ b/src/webenginewidgets/api/qwebengineview.cpp @@ -359,9 +359,6 @@ QWebEngineViewPrivate::QWebEngineViewPrivate() , m_dragEntered(false) , m_ownsPage(false) , m_contextRequest(nullptr) -#if QT_CONFIG(webengine_printing_and_pdf) - , currentPrinter(nullptr) -#endif { #ifndef QT_NO_ACCESSIBILITY QAccessible::installFactory(&webAccessibleFactory); @@ -512,19 +509,12 @@ QObject *QWebEngineViewPrivate::accessibilityParentObject() return q; } -void QWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointer result) +void QWebEngineViewPrivate::didPrintPage(QPrinter *¤tPrinter, QSharedPointer result) { #if QT_CONFIG(webengine_printing_and_pdf) Q_Q(QWebEngineView); - // If no currentPrinter is set that means that were printing to PDF only. - if (!currentPrinter) { - if (!result.data()) - return; - if (auto callback = m_pdfResultCallbacks.take(requestId)) - callback(*(result.data())); - return; - } + Q_ASSERT(currentPrinter); QThread *printerThread = new QThread; QObject::connect(printerThread, &QThread::finished, printerThread, &QThread::deleteLater); @@ -536,7 +526,7 @@ void QWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointerm_documentCopies = currentPrinter->copyCount(); printerWorker->m_collateCopies = currentPrinter->collateCopies(); - QObject::connect(printerWorker, &QtWebEngineCore::PrinterWorker::resultReady, q, [this, q](bool success) { + QObject::connect(printerWorker, &QtWebEngineCore::PrinterWorker::resultReady, q, [q, ¤tPrinter](bool success) { currentPrinter = nullptr; Q_EMIT q->printFinished(success); }); @@ -548,17 +538,15 @@ void QWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointerpdfPrintingFinished(filePath, success); + Q_EMIT q->pdfPrintingFinished(filePath, success); } void QWebEngineViewPrivate::printRequested() @@ -1000,19 +988,7 @@ QWebEngineContextMenuRequest *QWebEngineView::lastContextMenuRequest() const */ void QWebEngineView::printToPdf(const QString &filePath, const QPageLayout &layout, const QPageRanges &ranges) { -#if QT_CONFIG(webengine_printing_and_pdf) - Q_D(QWebEngineView); - if (d->currentPrinter) { - qWarning("Cannot print to PDF while printing at the same time."); - return; - } - page()->d_ptr->ensureInitialized(); - page()->d_ptr->adapter->printToPDF(layout, ranges, filePath); -#else - Q_UNUSED(filePath); - Q_UNUSED(layout); - Q_UNUSED(ranges); -#endif + page()->printToPdf(filePath, layout, ranges); } /*! @@ -1032,23 +1008,7 @@ void QWebEngineView::printToPdf(const QString &filePath, const QPageLayout &layo */ void QWebEngineView::printToPdf(const std::function &resultCallback, const QPageLayout &layout, const QPageRanges &ranges) { - Q_D(QWebEngineView); -#if QT_CONFIG(webengine_printing_and_pdf) - if (d->currentPrinter) { - qWarning("Cannot print to PDF while printing at the same time."); - if (resultCallback) - resultCallback(QByteArray()); - return; - } - page()->d_ptr->ensureInitialized(); - quint64 requestId = page()->d_ptr->adapter->printToPDFCallbackResult(layout, ranges); - d->m_pdfResultCallbacks.insert(requestId, resultCallback); -#else - Q_UNUSED(layout); - Q_UNUSED(ranges); - if (resultCallback) - resultCallback(QByteArray()); -#endif + page()->printToPdf(resultCallback, layout, ranges); } /*! @@ -1093,13 +1053,12 @@ void QWebEngineView::printToPdf(const std::function &re void QWebEngineView::print(QPrinter *printer) { #if QT_CONFIG(webengine_printing_and_pdf) - Q_D(QWebEngineView); - if (d->currentPrinter) { + if (page()->d_ptr->currentPrinter) { qWarning("Cannot print page on printer %ls: Already printing on a device.", qUtf16Printable(printer->printerName())); return; } - d->currentPrinter = printer; + page()->d_ptr->currentPrinter = printer; page()->d_ptr->ensureInitialized(); page()->d_ptr->adapter->printToPDFCallbackResult(printer->pageLayout(), printer->pageRanges(), diff --git a/src/webenginewidgets/api/qwebengineview_p.h b/src/webenginewidgets/api/qwebengineview_p.h index db22c95b5..13b346df9 100644 --- a/src/webenginewidgets/api/qwebengineview_p.h +++ b/src/webenginewidgets/api/qwebengineview_p.h @@ -94,7 +94,7 @@ public: QWebEngineContextMenuRequest *lastContextMenuRequest() const override; QWebEnginePage *createPageForWindow(QWebEnginePage::WebWindowType type) override; QObject *accessibilityParentObject() override; - void didPrintPage(quint64 requestId, QSharedPointer result) override; + void didPrintPage(QPrinter *&printer, QSharedPointer result) override; void didPrintPageToPdf(const QString &filePath, bool success) override; void printRequested() override; @@ -114,10 +114,6 @@ public: bool m_dragEntered; mutable bool m_ownsPage; QWebEngineContextMenuRequest *m_contextRequest; -#if QT_CONFIG(webengine_printing_and_pdf) - QPrinter *currentPrinter; -#endif - QMap> m_pdfResultCallbacks; }; #ifndef QT_NO_ACCESSIBILITY diff --git a/tests/auto/widgets/printing/tst_printing.cpp b/tests/auto/widgets/printing/tst_printing.cpp index 3f1344ed4..0559fb787 100644 --- a/tests/auto/widgets/printing/tst_printing.cpp +++ b/tests/auto/widgets/printing/tst_printing.cpp @@ -58,10 +58,10 @@ void tst_Printing::printToPdfBasic() view.load(QUrl("qrc:///resources/basic_printing_page.html")); QTRY_VERIFY(spy.count() == 1); - QSignalSpy savePdfSpy(&view, &QWebEngineView::pdfPrintingFinished); + QSignalSpy savePdfSpy(view.page(), &QWebEnginePage::pdfPrintingFinished); QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0)); QString path = tempDir.path() + "/print_1_success.pdf"; - view.printToPdf(path, layout); + view.page()->printToPdf(path, layout); QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); QList successArguments = savePdfSpy.takeFirst(); @@ -73,7 +73,7 @@ void tst_Printing::printToPdfBasic() #else path = tempDir.path() + "/print_|2_failed.pdf"; #endif - view.printToPdf(path, QPageLayout()); + view.page()->printToPdf(path, QPageLayout()); QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); QList failedArguments = savePdfSpy.takeFirst(); @@ -81,11 +81,11 @@ void tst_Printing::printToPdfBasic() QVERIFY2(failedArguments.at(1).toBool() == false, "Printing to PDF file succeeded though it should fail"); CallbackSpy successfulSpy; - view.printToPdf(successfulSpy.ref(), layout); + view.page()->printToPdf(successfulSpy.ref(), layout); QVERIFY(successfulSpy.waitForResult().length() > 0); CallbackSpy failedInvalidLayoutSpy; - view.printToPdf(failedInvalidLayoutSpy.ref(), QPageLayout()); + view.page()->printToPdf(failedInvalidLayoutSpy.ref(), QPageLayout()); QCOMPARE(failedInvalidLayoutSpy.waitForResult().length(), 0); } @@ -95,6 +95,7 @@ void tst_Printing::printRequest() QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0)); QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); QSignalSpy printRequestedSpy(&view, &QWebEngineView::printRequested); + QSignalSpy printRequestedSpy2(view.page(), &QWebEnginePage::printRequested); QSignalSpy savePdfSpy(&view, &QWebEngineView::pdfPrintingFinished); CallbackSpy resultSpy; @@ -102,6 +103,7 @@ void tst_Printing::printRequest() QTRY_VERIFY(loadFinishedSpy.count() == 1); view.page()->runJavaScript("window.print()"); QTRY_VERIFY(printRequestedSpy.count() == 1); + QVERIFY(printRequestedSpy2.count() == 1); //check if printing still works view.printToPdf(resultSpy.ref(), layout); const QByteArray data = resultSpy.waitForResult(); -- cgit v1.2.3