如何让QWebEngineView响应printJS的打印调用?
解决QWebEngineView响应printJS PDF打印的方案
问题根源
printJS处理PDF打印时,通常会创建隐藏iframe加载目标PDF,再调用该iframe的window.print()。但QWebEnginePage的printRequested信号默认仅监听主页面的打印请求,iframe的请求不会触发主页面的该信号;同时QWebEngine对PDF的内置渲染和打印逻辑与普通网页存在差异,直接依赖前端库的调用无法触发Qt的打印流程。
方案一:注入JS重写printJS逻辑,通过Qt处理PDF打印
通过向网页注入自定义JS,替换printJS处理PDF的行为,让它直接将PDF URL传递给Qt端,由Qt负责加载并打印PDF。
代码实现
#include <QApplication> #include <QWebEngineView> #include <QWebEnginePage> #include <QWebChannel> #include <QObject> #include <QPrinter> #include <QPrintDialog> // 接收JS传递的PDF打印请求的Qt对象 class PdfPrintReceiver : public QObject { Q_OBJECT public slots: void printPdf(const QString& pdfUrl) { // 创建临时视图加载PDF并打印 QWebEngineView* pdfView = new QWebEngineView(); pdfView->load(QUrl(pdfUrl)); connect(pdfView->page(), &QWebEnginePage::loadFinished, [pdfView](bool ok) { if (ok) { QPrinter printer; if (QPrintDialog(&printer).exec() == QDialog::Accepted) { pdfView->page()->print(&printer, [pdfView](bool success) { qWarning() << (success ? "PDF打印成功" : "PDF打印失败"); pdfView->deleteLater(); }); } else { pdfView->deleteLater(); } } else { qWarning() << "PDF加载失败"; pdfView->deleteLater(); } }); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QWebEngineView view; // 建立JS与Qt的通信通道 QWebChannel* channel = new QWebChannel(&view); PdfPrintReceiver* receiver = new PdfPrintReceiver(); channel->registerObject("pdfPrinter", receiver); view.page()->setWebChannel(channel); // 页面加载完成后注入重写逻辑 connect(view.page(), &QWebEnginePage::loadFinished, [&view](bool ok) { if (ok) { QString jsCode = R"( const originalPrintJS = window.printJS; window.printJS = function(options) { let pdfUrl; // 兼容printJS的参数格式 if (typeof options === 'string') { pdfUrl = options; } else if (options?.printable) { pdfUrl = options.printable; } // 仅拦截PDF打印请求 if (pdfUrl?.endsWith('.pdf')) { window.qwebChannel.objects.pdfPrinter.printPdf(pdfUrl); return; } // 非PDF请求保留原逻辑 originalPrintJS(options); }; )"; view.page()->runJavaScript(jsCode); } }); view.setUrl(QUrl("http://localhost:8080/")); view.resize(1024, 750); view.show(); return a.exec(); } #include "main.moc"
说明
- 通过QWebChannel实现JS与Qt的双向通信,仅拦截PDF打印请求,不影响printJS处理HTML元素的原有功能。
- Qt端创建临时视图加载PDF,加载完成后触发系统打印对话框,流程与浏览器一致。
方案二:拦截所有页面的printRequested信号
重写QWebEnginePage的窗口创建逻辑,给所有子页面(包括printJS创建的隐藏iframe)绑定printRequested信号,确保iframe的打印请求被捕获。
代码实现
#include <QApplication> #include <QWebEngineView> #include <QWebEnginePage> #include <QPrinter> #include <QPrintDialog> class CustomWebPage : public QWebEnginePage { Q_OBJECT protected: QWebEnginePage* createWindow(QWebEnginePage::WebWindowType type) override { CustomWebPage* page = new CustomWebPage(this); // 给子页面绑定打印信号 connect(page, &QWebEnginePage::printRequested, this, &CustomWebPage::handlePrint); return page; } private slots: void handlePrint() { QPrinter printer; if (QPrintDialog(&printer).exec() == QDialog::Accepted) { this->print(&printer, [](bool success) { qWarning() << (success ? "打印成功" : "打印失败"); }); } } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QWebEngineView view; view.setPage(new CustomWebPage(&view)); // 给主页面绑定打印信号 connect(view.page(), &QWebEnginePage::printRequested, dynamic_cast<CustomWebPage*>(view.page()), &CustomWebPage::handlePrint); view.setUrl(QUrl("http://localhost:8080/")); view.resize(1024, 750); view.show(); return a.exec(); } #include "main.moc"
说明
- 重写
createWindow方法,确保所有子页面(包括iframe)使用自定义页面类,统一处理打印请求。 - 无需修改前端代码,依赖printJS的原有iframe打印逻辑,若printJS实现变更可能失效。
方案三:直接使用Qt PDF模块打印(Qt 5.15+)
若使用Qt 5.15及以上版本,可直接用QPdfDocument加载PDF并打印,无需依赖QWebEngine的PDF渲染,稳定性更强。
代码片段(替换方案一的printPdf方法)
#include <QPdfDocument> #include <QPainter> void printPdf(const QString& pdfUrl) { QPdfDocument pdfDoc; if (pdfDoc.load(QUrl(pdfUrl))) { QPrinter printer; if (QPrintDialog(&printer).exec() == QDialog::Accepted) { QPainter painter(&printer); int pageCount = pdfDoc.pageCount(); for (int i = 0; i < pageCount; ++i) { if (i > 0) printer.newPage(); QImage pageImg = pdfDoc.render(i, printer.pageRect(QPrinter::DevicePixel).size().toSize()); painter.drawImage(printer.pageRect(), pageImg); } painter.end(); } } else { qWarning() << "无法加载PDF文件"; } }
说明
- 需要在项目.pro文件中添加
QT += pdf以启用PDF模块。 - 不依赖QWebEngine的渲染逻辑,兼容性更好,无需创建额外视图。
内容的提问来源于stack exchange,提问作者Lidbey
相关产品推荐
相关产品推荐

