PyQt5 / PyQt

PyQt Examples(PyQt各种测试和例子) PyQt4 PyQt5
GNU Lesser General Public License v2.1
6.65k stars 1.97k forks source link

PyQt5 QWebEngineView does NOT print the same thing shows in the browser #159

Closed godomainz closed 2 years ago

godomainz commented 2 years ago

So I have QWebEngineView app like below

import sys
from PyQt5 import QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import QUrl, QTimer, QDateTime, Qt
from PyQt5.QtGui import QPageLayout, QPageSize
from PyQt5.QtWidgets import QApplication
from PyQt5.QtNetwork import QNetworkCookie
import argparse
import os
import json

cookie_file = None

class Window(QtWebEngineWidgets.QWebEngineView):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        self.cookieStore = self.page().profile().cookieStore()

    def initCookies(self, cookie_file):
        if cookie_file:
            with open("output/"+cookie_file, encoding='utf8') as f:
                cookies = json.load(f)

            for cookie in cookies:
                qcookie = QNetworkCookie()
                qcookie.setName(cookie.get('name', '').encode())
                qcookie.setValue(cookie.get('value', '').encode())
                qcookie.setDomain(cookie.get('domain', ''))
                qcookie.setPath(cookie.get('path', ''))
                qcookie.setExpirationDate(
                    QDateTime.fromString(str(cookie.get('expirationDate', 0)),
                                         Qt.ISODate))
                qcookie.setHttpOnly(cookie.get('httpOnly', False))
                qcookie.setSecure(cookie.get('secure', False))
                self.cookieStore.setCookie(qcookie, QUrl())

def main():
    file_name = 'ABC123.pdf'
    parser = argparse.ArgumentParser(description="Just an example", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("--url", help="Type url", required=True)
    parser.add_argument("--output", help="Type output pdf file name")
    parser.add_argument("--cookie", help="Type cookie file name")
    args = parser.parse_args()
    config = vars(args)
    url = config['url']
    output = config['output']
    cookie = config['cookie']
    if output:
        file_name = output
    if cookie:
        cookie_file = cookie

    app = QtWidgets.QApplication(sys.argv)
    loader = Window()
    loader.initCookies(cookie_file)
    loader.setZoomFactor(1)
    layout = QPageLayout()
    layout.setPageSize(QPageSize(QPageSize.A4Extra))
    layout.setOrientation(QPageLayout.Portrait)
    loader.load(QUrl(url))
    loader.page().pdfPrintingFinished.connect(lambda *args: QApplication.exit())

    def emit_pdf(finished):
        directory = "/tmp/"
        if not os.path.exists(directory):
            os.makedirs(directory)
        QTimer.singleShot(2000, lambda: loader.page().printToPdf(directory+file_name, pageLayout=layout))

    loader.loadFinished.connect(emit_pdf)
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

I run the program like this python3 htmlToPdfnew.py --url https://www.w3schools.com/howto/howto_css_register_form.asp

so IF you load the url https://www.w3schools.com/howto/howto_css_register_form.asp from your browser you will see screen like below R64bv

IF you open the output ABC123.pdf, you will see something like below N7pwq

Compare above screens.

Why they are different ?

How can I get the full screen embedded to pdf like first picture ?

I had a look at online converter https://www.sejda.com/html-to-pdf

That online converter embeds full view to pdf.

So can I achieve the same with above python code ? I already tried changing this line to Custom sizes layout.setPageSize(QPageSize(QPageSize.A4Extra))

892768447 commented 2 years ago

I open url in Google Browser, and get same result.

image

test url: https://cn.vuejs.org/v2/api/ it's same problem, I guess the web browser print function will load new page without js.

I test my other demo, can work ok

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Created on 2019年7月8日
@author: Irony
@site: https://pyqt.site , https://github.com/PyQt5
@email: 892768447@qq.com
@file: ScreenShotPage
@description: 网页整体截图
"""
import base64
import cgitb
import os
import sys

try:
    from PyQt5.QtCore import QUrl, Qt, pyqtSlot, QSize, QTimer, QPoint
    from PyQt5.QtGui import QImage, QPainter, QIcon, QPixmap
    from PyQt5.QtWebChannel import QWebChannel
    from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
    from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, \
        QGroupBox, QLineEdit, QHBoxLayout, QListWidget, QListWidgetItem, \
        QProgressDialog
except ImportError:
    from PySide2.QtCore import QUrl, Qt, Slot as pyqtSlot, QSize, QTimer, QPoint
    from PySide2.QtGui import QImage, QPainter, QIcon, QPixmap
    from PySide2.QtWebChannel import QWebChannel
    from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
    from PySide2.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, \
        QGroupBox, QLineEdit, QHBoxLayout, QListWidget, QListWidgetItem, \
        QProgressDialog

class Window(QWidget):

    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        self.resize(600, 400)
        layout = QHBoxLayout(self)

        # 左侧
        widgetLeft = QWidget(self)
        layoutLeft = QVBoxLayout(widgetLeft)
        # 右侧
        self.widgetRight = QListWidget(
            self, minimumWidth=200, iconSize=QSize(150, 150))
        self.widgetRight.setViewMode(QListWidget.IconMode)
        layout.addWidget(widgetLeft, 3)
        layout.addWidget(self.widgetRight, 1)

        self.webView = QWebEngineView()
        layoutLeft.addWidget(self.webView)

        # 截图方式一
        groupBox1 = QGroupBox('截图方式一', self)
        layout1 = QVBoxLayout(groupBox1)
        layout1.addWidget(QPushButton('截图1', self, clicked=self.onScreenShot1))
        layoutLeft.addWidget(groupBox1)

        # 支持截图
        settings = QWebEngineSettings.globalSettings()
        settings.setAttribute(QWebEngineSettings.ScreenCaptureEnabled, True)
        self.webView.load(QUrl("https://www.w3schools.com/howto/howto_css_register_form.asp"))

    def onScreenShot1(self):
        # 截图方式1
        page = self.webView.page()
        oldSize = self.webView.size()
        self.webView.resize(page.contentsSize().toSize())

        def doScreenShot():
            rect = self.webView.contentsRect()
            size = rect.size()
            image = QImage(size, QImage.Format_ARGB32_Premultiplied)
            image.fill(Qt.transparent)

            painter = QPainter()
            painter.begin(image)
            painter.setRenderHint(QPainter.Antialiasing, True)
            painter.setRenderHint(QPainter.TextAntialiasing, True)
            painter.setRenderHint(QPainter.SmoothPixmapTransform, True)

            self.webView.render(painter, QPoint())
            painter.end()
            self.webView.resize(oldSize)
            image.save('1.png')

            # 添加到左侧list中
            item = QListWidgetItem(self.widgetRight)
            image = QPixmap.fromImage(image)
            item.setIcon(QIcon(image))
            item.setData(Qt.UserRole + 1, image)

        # 先等一下再截图吧
        QTimer.singleShot(2000, doScreenShot)

if __name__ == '__main__':
    # 开启F12 控制台功能,需要单独通过浏览器打开这个页面
    # 这里可以做个保护, 发布软件,启动时把这个环境变量删掉。防止他人通过环境变量开启
    os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '9966'
    cgitb.enable(format='text')
    app = QApplication(sys.argv)
    w = Window()
    w.show()

    # 打开调试页面
    dw = QWebEngineView()
    dw.setWindowTitle('开发人员工具')
    dw.load(QUrl('http://127.0.0.1:9966'))
    dw.show()
    dw.move(100, 100)
    sys.exit(app.exec_())