From af2535018b1553e351198f3d9c21538de1c328a1 Mon Sep 17 00:00:00 2001 From: Michael Bruning Date: Wed, 10 Aug 2016 17:22:05 +0200 Subject: Add widgets API for printing on a QPrinter Renders the content to a PDF document and then renders this on a QPrinter-backed QPainter using the PDFium library. PDFium bitmap to QImage conversion based on work by Paulo Pinheiro . [ChangeLog][QtWebEngineWidgets][Printing] Enables printing QWebPage content on a QPrinter. Currently does not support previewing the document. Widgets only for the moment. Change-Id: I9a5264433093379aee90f5e4f69bf2aee8814f2b Reviewed-by: Allan Sandfeld Jensen --- src/core/print_view_manager_qt.cpp | 89 +++++++++++++++++------------ src/core/print_view_manager_qt.h | 6 +- src/core/web_contents_adapter.cpp | 41 ++++++++++++- src/core/web_contents_adapter.h | 7 +++ src/core/web_contents_adapter_client.h | 3 + src/webengine/api/qquickwebengineview_p_p.h | 3 + src/webenginewidgets/api/qwebenginepage.cpp | 29 ++++++++++ src/webenginewidgets/api/qwebenginepage.h | 11 ++++ src/webenginewidgets/api/qwebenginepage_p.h | 3 + 9 files changed, 150 insertions(+), 42 deletions(-) diff --git a/src/core/print_view_manager_qt.cpp b/src/core/print_view_manager_qt.cpp index 4cb0e06eb..bb01f531c 100644 --- a/src/core/print_view_manager_qt.cpp +++ b/src/core/print_view_manager_qt.cpp @@ -43,8 +43,8 @@ #include "type_conversion.h" #include "web_engine_context.h" -#include -#include +#include +#include #include "base/values.h" #include "chrome/browser/printing/print_job_manager.h" @@ -101,43 +101,58 @@ static void SavePdfFile(scoped_refptr data, metafile.SaveTo(&file); } -static void applyQPageLayoutSettingsToDictionary(const QPageLayout& pageLayout, base::DictionaryValue& print_settings) +static base::DictionaryValue *createPrintSettings() { + base::DictionaryValue *printSettings = new base::DictionaryValue(); // TO DO: Check if we can use the request ID from Qt here somehow. static int internalRequestId = 0; - print_settings.SetBoolean(printing::kIsFirstRequest, internalRequestId++ == 0); - print_settings.SetInteger(printing::kPreviewRequestID, internalRequestId); + printSettings->SetBoolean(printing::kIsFirstRequest, internalRequestId++ == 0); + printSettings->SetInteger(printing::kPreviewRequestID, internalRequestId); + + // The following are standard settings that Chromium expects to be set. + printSettings->SetBoolean(printing::kSettingPrintToPDF, true); + printSettings->SetBoolean(printing::kSettingCloudPrintDialog, false); + printSettings->SetBoolean(printing::kSettingPrintWithPrivet, false); + printSettings->SetBoolean(printing::kSettingPrintWithExtension, false); + + printSettings->SetBoolean(printing::kSettingGenerateDraftData, false); + printSettings->SetBoolean(printing::kSettingPreviewModifiable, false); + printSettings->SetInteger(printing::kSettingDuplexMode, printing::SIMPLEX); + printSettings->SetInteger(printing::kSettingCopies, 1); + printSettings->SetBoolean(printing::kSettingCollate, false); + printSettings->SetBoolean(printing::kSettingGenerateDraftData, false); + printSettings->SetBoolean(printing::kSettingPreviewModifiable, false); + + printSettings->SetBoolean(printing::kSettingShouldPrintSelectionOnly, false); + printSettings->SetBoolean(printing::kSettingShouldPrintBackgrounds, false); + printSettings->SetBoolean(printing::kSettingHeaderFooterEnabled, false); + printSettings->SetString(printing::kSettingDeviceName, ""); + printSettings->SetInteger(printing::kPreviewUIID, 12345678); + + return printSettings; +} + +static void applyQPageLayoutSettingsToDictionary(const QPageLayout &pageLayout, base::DictionaryValue &printSettings) +{ //Set page size attributes, chromium expects these in micrometers QSizeF pageSizeInMilimeter = pageLayout.pageSize().size(QPageSize::Millimeter); scoped_ptr sizeDict(new base::DictionaryValue); sizeDict->SetInteger(printing::kSettingMediaSizeWidthMicrons, pageSizeInMilimeter.width() * kMicronsToMillimeter); sizeDict->SetInteger(printing::kSettingMediaSizeHeightMicrons, pageSizeInMilimeter.height() * kMicronsToMillimeter); - print_settings.Set(printing::kSettingMediaSize, std::move(sizeDict)); - - print_settings.SetBoolean(printing::kSettingLandscape, pageLayout.orientation() == QPageLayout::Landscape); - - // The following are standard settings that Chromium expects to be set. - print_settings.SetBoolean(printing::kSettingPrintToPDF, true); - print_settings.SetBoolean(printing::kSettingCloudPrintDialog, false); - print_settings.SetBoolean(printing::kSettingPrintWithPrivet, false); - print_settings.SetBoolean(printing::kSettingPrintWithExtension, false); - - print_settings.SetBoolean(printing::kSettingGenerateDraftData, false); - print_settings.SetBoolean(printing::kSettingPreviewModifiable, false); - print_settings.SetInteger(printing::kSettingColor, printing::COLOR); - print_settings.SetInteger(printing::kSettingDuplexMode, printing::SIMPLEX); - print_settings.SetInteger(printing::kSettingDuplexMode, printing::UNKNOWN_DUPLEX_MODE); - print_settings.SetInteger(printing::kSettingCopies, 1); - print_settings.SetBoolean(printing::kSettingCollate, false); - print_settings.SetBoolean(printing::kSettingGenerateDraftData, false); - print_settings.SetBoolean(printing::kSettingPreviewModifiable, false); - - print_settings.SetBoolean(printing::kSettingShouldPrintSelectionOnly, false); - print_settings.SetBoolean(printing::kSettingShouldPrintBackgrounds, false); - print_settings.SetBoolean(printing::kSettingHeaderFooterEnabled, false); - print_settings.SetString(printing::kSettingDeviceName, ""); - print_settings.SetInteger(printing::kPreviewUIID, 12345678); + printSettings.Set(printing::kSettingMediaSize, std::move(sizeDict)); + + // Apply page margins + QMargins pageMarginsInPoints = pageLayout.marginsPoints(); + scoped_ptr marginsDict(new base::DictionaryValue); + marginsDict->SetInteger(printing::kSettingMarginTop, pageMarginsInPoints.top()); + marginsDict->SetInteger(printing::kSettingMarginBottom, pageMarginsInPoints.bottom()); + marginsDict->SetInteger(printing::kSettingMarginLeft, pageMarginsInPoints.left()); + marginsDict->SetInteger(printing::kSettingMarginRight, pageMarginsInPoints.right()); + printSettings.Set(printing::kSettingMarginsCustom, std::move(marginsDict)); + printSettings.SetInteger(printing::kSettingMarginsType, printing::CUSTOM_MARGINS); + + printSettings.SetBoolean(printing::kSettingLandscape, pageLayout.orientation() == QPageLayout::Landscape); } } // namespace @@ -149,20 +164,20 @@ PrintViewManagerQt::~PrintViewManagerQt() } #if defined(ENABLE_BASIC_PRINTING) -bool PrintViewManagerQt::PrintToPDF(const QPageLayout &pageLayout, const QString &filePath) +bool PrintViewManagerQt::PrintToPDF(const QPageLayout &pageLayout, bool printInColor, const QString &filePath) { if (m_printSettings || !filePath.length()) return false; m_pdfOutputPath = toFilePath(filePath); - if (!PrintToPDFInternal(pageLayout)) { + if (!PrintToPDFInternal(pageLayout, printInColor)) { resetPdfState(); return false; } return true; } -bool PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, const PrintToPDFCallback& callback) +bool PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, bool printInColor, const PrintToPDFCallback& callback) { if (callback.is_null()) return false; @@ -176,7 +191,7 @@ bool PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, c } m_pdfPrintCallback = callback; - if (!PrintToPDFInternal(pageLayout)) { + if (!PrintToPDFInternal(pageLayout, printInColor)) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(callback, std::vector())); @@ -187,11 +202,13 @@ bool PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, c return true; } -bool PrintViewManagerQt::PrintToPDFInternal(const QPageLayout &pageLayout) +bool PrintViewManagerQt::PrintToPDFInternal(const QPageLayout &pageLayout, bool printInColor) { if (!pageLayout.isValid()) return false; - m_printSettings.reset(new base::DictionaryValue()); + m_printSettings.reset(createPrintSettings()); + + m_printSettings->SetInteger(printing::kSettingColor, printInColor ? printing::COLOR : printing::GRAYSCALE); applyQPageLayoutSettingsToDictionary(pageLayout, *m_printSettings); return Send(new PrintMsg_InitiatePrintPreview(routing_id(), false)); } diff --git a/src/core/print_view_manager_qt.h b/src/core/print_view_manager_qt.h index 8856a5b2b..668516096 100644 --- a/src/core/print_view_manager_qt.h +++ b/src/core/print_view_manager_qt.h @@ -83,8 +83,8 @@ public: typedef base::Callback &result)> PrintToPDFCallback; #if defined(ENABLE_BASIC_PRINTING) // Method to print a page to a Pdf document with page size \a pageSize in location \a filePath. - bool PrintToPDF(const QPageLayout& pageLayout, const QString& filePath); - bool PrintToPDFWithCallback(const QPageLayout& pageLayout, const PrintToPDFCallback& callback); + bool PrintToPDF(const QPageLayout &pageLayout, bool printInColor, const QString &filePath); + bool PrintToPDFWithCallback(const QPageLayout &pageLayout, bool printInColor, const PrintToPDFCallback &callback); #endif // ENABLE_BASIC_PRINTING // PrintedPagesSource implementation. @@ -109,7 +109,7 @@ protected: void OnMetafileReadyForPrinting(const PrintHostMsg_DidPreviewDocument_Params& params); #if defined(ENABLE_BASIC_PRINTING) - bool PrintToPDFInternal(const QPageLayout &); + bool PrintToPDFInternal(const QPageLayout &, bool printInColor); #endif // base::FilePath m_pdfOutputPath; diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index a7799544b..ea11d5e9c 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -50,6 +50,7 @@ #include "browser_context_qt.h" #include "download_manager_delegate_qt.h" #include "media_capture_devices_dispatcher.h" +#include "pdfium_printing_wrapper_qt.h" #include "print_view_manager_qt.h" #include "qwebenginecallback_p.h" #include "render_view_observer_host_qt.h" @@ -91,6 +92,9 @@ #include #include #include +#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) +#include +#endif // QT_NO_PRINTER #include namespace QtWebEngineCore { @@ -189,6 +193,17 @@ static void callbackOnPrintingFinished(WebContentsAdapterClient *adapterClient, } } +#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) +static void callbackOnPrintingOnPrinterFinished(WebContentsAdapterClient *adapterClient, int requestId, QPrinter *printer, const std::vector &result) +{ + if (requestId) { + PdfiumPrintingWrapperQt printWrapper(result.data(), result.size()); + bool printerResult = printWrapper.printOnPrinter(*printer); + adapterClient->didPrintPageOnPrinter(requestId, printerResult); + } +} +#endif // QT_NO_PRINTER + static content::WebContents *createBlankWebContents(WebContentsAdapterClient *adapterClient, content::BrowserContext *browserContext) { content::WebContents::CreateParams create_params(browserContext, NULL); @@ -948,7 +963,7 @@ void WebContentsAdapter::wasHidden() void WebContentsAdapter::printToPDF(const QPageLayout &pageLayout, const QString &filePath) { #if defined(ENABLE_BASIC_PRINTING) - PrintViewManagerQt::FromWebContents(webContents())->PrintToPDF(pageLayout, filePath); + PrintViewManagerQt::FromWebContents(webContents())->PrintToPDF(pageLayout, true, filePath); #endif // if defined(ENABLE_BASIC_PRINTING) } @@ -956,13 +971,33 @@ quint64 WebContentsAdapter::printToPDFCallbackResult(const QPageLayout &pageLayo { #if defined(ENABLE_BASIC_PRINTING) Q_D(WebContentsAdapter); - PrintViewManagerQt::PrintToPDFCallback callback = base::Bind(&callbackOnPrintingFinished, d->adapterClient, d->nextRequestId); - PrintViewManagerQt::FromWebContents(webContents())->PrintToPDFWithCallback(pageLayout, callback); + PrintViewManagerQt::PrintToPDFCallback callback = base::Bind(&callbackOnPrintingFinished + , d->adapterClient + , d->nextRequestId); + PrintViewManagerQt::FromWebContents(webContents())->PrintToPDFWithCallback(pageLayout, true + , callback); + return d->nextRequestId++; +#else + return 0; +#endif // if defined(ENABLE_BASIC_PRINTING) +} + +#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) +quint64 WebContentsAdapter::printOnPrinterCallbackResult(QPrinter *printer) +{ +#if defined(ENABLE_BASIC_PRINTING) + Q_D(WebContentsAdapter); + PrintViewManagerQt::PrintToPDFCallback callback + = base::Bind(&callbackOnPrintingOnPrinterFinished, d->adapterClient + , d->nextRequestId, printer); + PrintViewManagerQt::FromWebContents(webContents())->PrintToPDFWithCallback( + printer->pageLayout(), printer->colorMode() == QPrinter::Color, callback); return d->nextRequestId++; #else return 0; #endif // if defined(ENABLE_BASIC_PRINTING) } +#endif // QT_NO_PRINTER QPointF WebContentsAdapter::lastScrollOffset() const { diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index 08f68e76b..7a109770e 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -58,6 +58,9 @@ class QAccessibleInterface; class QDragEnterEvent; class QDragMoveEvent; class QPageLayout; +#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) +class QPrinter; +#endif // QT_NO_PRINTER class QString; class QWebChannel; QT_END_NAMESPACE @@ -174,6 +177,10 @@ public: void printToPDF(const QPageLayout&, const QString&); quint64 printToPDFCallbackResult(const QPageLayout &); +#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) + quint64 printOnPrinterCallbackResult(QPrinter *printer); +#endif + // meant to be used within WebEngineCore only content::WebContents *webContents() const; void replaceMisspelling(const QString &word); diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 31786a8f8..165d84e37 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -234,6 +234,9 @@ public: virtual void didFetchDocumentInnerText(quint64 requestId, const QString& result) = 0; virtual void didFindText(quint64 requestId, int matchCount) = 0; virtual void didPrintPage(quint64 requestId, const QByteArray &result) = 0; +#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) + virtual void didPrintPageOnPrinter(quint64 requestId, bool result) = 0; +#endif virtual void passOnFocus(bool reverse) = 0; // returns the last QObject (QWidget/QQuickItem) based object in the accessibility // hierarchy before going into the BrowserAccessibility tree diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index 7e5e07b95..60692b8ae 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -168,6 +168,9 @@ public: virtual void didFetchDocumentInnerText(quint64, const QString&) Q_DECL_OVERRIDE { } virtual void didFindText(quint64, int) Q_DECL_OVERRIDE; virtual void didPrintPage(quint64 requestId, const QByteArray &result) Q_DECL_OVERRIDE; +#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) + virtual void didPrintPageOnPrinter(quint64, bool) Q_DECL_OVERRIDE { } +#endif virtual void passOnFocus(bool reverse) Q_DECL_OVERRIDE; virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) Q_DECL_OVERRIDE; virtual void authenticationRequired(QSharedPointer) Q_DECL_OVERRIDE; diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index c180699f3..428cccd9a 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -373,6 +373,13 @@ void QWebEnginePagePrivate::didPrintPage(quint64 requestId, const QByteArray &re m_callbacks.invoke(requestId, result); } +#ifndef QT_NO_PRINTER +void QWebEnginePagePrivate::didPrintPageOnPrinter(quint64 requestId, bool result) +{ + m_callbacks.invoke(requestId, result); +} +#endif + void QWebEnginePagePrivate::passOnFocus(bool reverse) { if (view) @@ -1841,6 +1848,28 @@ void QWebEnginePage::printToPdf(const QWebEngineCallback &res d->m_callbacks.registerCallback(requestId, resultCallback); } +#ifndef QT_NO_PRINTER +/*! + \fn void QWebEnginePage::print(const QPrinter &printer, FunctorOrLambda resultCallback) + Renders the current content of the page into a temporary PDF document, then prints it using \a printer. + + The settings for creating and printing the PDF document will be retrieved from the \a printer + object. + It is the users responsibility to ensure the \a printer remains valid until \a resultCallback + has been called. + + The \a resultCallback must take a boolean as parameter. If printing was successful, this + boolean will have the value \c true, otherwise, its value will be \c false. + \since 5.8 +*/ +void QWebEnginePage::print(QPrinter *printer, const QWebEngineCallback &resultCallback) +{ + Q_D(QWebEnginePage); + quint64 requestId = d->adapter->printOnPrinterCallbackResult(printer); + d->m_callbacks.registerCallback(requestId, resultCallback); +} +#endif // QT_NO_PRINTER + /*! \since 5.7 diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index bb46cd9fb..923c3bb70 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -54,6 +54,9 @@ QT_BEGIN_NAMESPACE class QMenu; +#ifndef QT_NO_PRINTER +class QPrinter; +#endif class QWebChannel; class QWebEngineContextMenuData; class QWebEngineFullScreenRequest; @@ -280,6 +283,14 @@ public: void printToPdf(const QWebEngineCallback &resultCallback, const QPageLayout &layout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF())); #endif +#ifndef QT_NO_PRINTER +#ifdef Q_QDOC + void print(QPrinter *printer, FunctorOrLambda resultCallback) +#else + void print(QPrinter *printer, const QWebEngineCallback &resultCallback); +#endif // QDOC +#endif // QT_NO_PRINTER + const QWebEngineContextMenuData &contextMenuData() const; void viewSource(); bool canViewSource() const; diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index 8d2a86350..93ebdf6e6 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -119,6 +119,9 @@ public: virtual void didFetchDocumentInnerText(quint64 requestId, const QString& result) Q_DECL_OVERRIDE; virtual void didFindText(quint64 requestId, int matchCount) Q_DECL_OVERRIDE; virtual void didPrintPage(quint64 requestId, const QByteArray &result) Q_DECL_OVERRIDE; +#ifndef QT_NO_PRINTER + virtual void didPrintPageOnPrinter(quint64 requestId, bool result) Q_DECL_OVERRIDE; +#endif virtual void passOnFocus(bool reverse) Q_DECL_OVERRIDE; virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) Q_DECL_OVERRIDE; virtual void authenticationRequired(QSharedPointer) Q_DECL_OVERRIDE; -- cgit v1.2.3