pythonpyside6qprinterqtextdocument

The HTML table length and width is not taken in consideration when printing


Using Pyside6, I am trying to print an HTML Table. The HTML table format looks good in browser. However, when I open it up in print preview in Pyside6, the length and width of the table are not considered and the table collapses.

Devlopment Environment:

  1. Ubuntu 20.04
  2. Python 3.8.10 (using python virtual environment)
  3. PySide6 version 6.1.3

I also tried the same code in Windows with Python 3.9.7 and Pyside6 version 6.2, and got the same result.

Expected:

enter image description here

Actual:

enter image description here

main.py

from PySide6.QtGui import (
    QPageSize,
    QPageLayout,
    QTextBlockFormat,
    QTextCursor,
    QTextDocument,
    QTextFormat,
)
from PySide6.QtWidgets import QApplication
from PySide6.QtPrintSupport import QPrinter, QPrintPreviewDialog

app = QApplication()

dialog = QPrintPreviewDialog()


def handle_paint_requested(printer):
    document = QTextDocument()
   
    f = open("template.html", "r")
    billTemplate = f.read()

    document.setHtml(billTemplate)
    document.print_(printer)


dialog.paintRequested.connect(handle_paint_requested)
dialog.exec()

template.html

<p>&nbsp;</p>
<table style="border-collapse: collapse; width: 300px;" border="1">
    <tbody>
        <tr>
            <td style="width: 50%; height: 50px;">test1</td>
            <td style="width: 50%; height: 50px;">&nbsp;</td>
        </tr>
    </tbody>
</table>

Solution

  • Since QTextDocument only supports a subset of HTML4 properties it causes the observed error.

    One workaround is to use QtWebEngine which was reintroduced in Qt 6.2. Currently only the windows .whl is available in pypi (See here and here for more information) so we can install the Linux and MacOs package you must execute:

    python -m pip install pyside6 \
       --index-url=http://download.qt.io/official_releases/QtForPython
    

    So translating my previous answer to PySide6:

    import os
    from pathlib import Path
    
    from PySide6.QtCore import (
        QCoreApplication,
        QEventLoop,
        QObject,
        QPointF,
        Qt,
        QUrl,
        Slot,
    )
    from PySide6.QtGui import QKeySequence, QPainter, QShortcut
    from PySide6.QtPrintSupport import QPrintDialog, QPrinter, QPrintPreviewDialog
    from PySide6.QtWebEngineWidgets import QWebEngineView
    from PySide6.QtWidgets import (
        QApplication,
        QDialog,
        QProgressBar,
        QProgressDialog,
    )
    
    CURRENT_DIRECTORY = Path(__file__).resolve().parent
    
    
    class PrintHandler(QObject):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.m_page = None
            self.m_inPrintPreview = False
    
        def setPage(self, page):
            assert not self.m_page
            self.m_page = page
            self.m_page.printRequested.connect(self.printPreview)
    
        @Slot()
        def print(self):
            printer = QPrinter(QPrinter.HighResolution)
            dialog = QPrintDialog(printer, QWebEngineView.forPage(self.m_page))
            if dialog.exec_() != QDialog.Accepted:
                return
            self.printDocument(printer)
    
        @Slot()
        def printPreview(self):
            if not self.m_page:
                return
            if self.m_inPrintPreview:
                return
            self.m_inPrintPreview = True
            printer = QPrinter()
            preview = QPrintPreviewDialog(printer, QWebEngineView.forPage(self.m_page))
            preview.paintRequested.connect(self.printDocument)
            preview.exec()
            self.m_inPrintPreview = False
    
        @Slot(QPrinter)
        def printDocument(self, printer):
            loop = QEventLoop()
            result = False
    
            def printPreview(success):
                nonlocal result
                result = success
                loop.quit()
    
            view = QWebEngineView.forPage(self.m_page)
            view.printFinished.connect(printPreview)
            progressbar = QProgressDialog(view)
            progressbar.findChild(QProgressBar).setTextVisible(False)
            progressbar.setLabelText("Wait please...")
            progressbar.setRange(0, 0)
            progressbar.show()
            progressbar.canceled.connect(loop.quit)
            view.print(printer)
            loop.exec()
            progressbar.close()
            if not result:
                painter = QPainter()
                if painter.begin(printer):
                    font = painter.font()
                    font.setPixelSize(20)
                    painter.setFont(font)
                    painter.drawText(QPointF(10, 25), "Could not generate print preview.")
                    painter.end()
    
    
    def main():
        import sys
    
        QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
        app = QApplication(sys.argv)
        app.setApplicationName("Previewer")
    
        filename = CURRENT_DIRECTORY / "template.html"
        url = QUrl.fromLocalFile(os.fspath(filename))
    
        view = QWebEngineView()
        view.setUrl(url)
        view.resize(1024, 750)
        view.show()
    
        handler = PrintHandler()
        handler.setPage(view.page())
    
        printPreviewShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_P), view)
        printShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_P), view)
    
        printPreviewShortCut.activated.connect(handler.printPreview)
        printShortCut.activated.connect(handler.print)
    
        sys.exit(app.exec())
    
    
    if __name__ == "__main__":
        main()
    

    After pressing Ctrl + P you get:

    enter image description here