You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 23:17:06