pythonqpainterpyside6qtextdocument

Combining QPainter and QTextDocument on QPdfWriter


In another question, I learned about QTextDocument, and I was told that you can use a QPainter and a QTextDocument on the same page of a PDF. However, when I tried to do that, they each restart the document, wiping out the other's content.

from PySide6.QtGui import QPdfWriter, QPainter, QPageSize, QTextDocument, Qt
from PySide6.QtWidgets import QApplication


def main():
    app = QApplication()
    pdf = QPdfWriter('example.pdf')
    pdf.setPageSize(QPageSize.Letter)

    # Whichever of these goes second, overwrites the first.
    draw_diagram(pdf)
    print_document(pdf)

    app.exit(0)


def draw_diagram(pdf: QPdfWriter):
    painter = QPainter(pdf)
    painter.drawArc(painter.window().width()//4,
                    painter.window().height()//2 - painter.window().width()//4,
                    painter.window().width()//2,
                    painter.window().width()//2,
                    0,
                    5760)
    painter.drawText(0,
                     painter.window().height()//2,
                     painter.window().width(),
                     painter.window().height()//10,
                     Qt.AlignHCenter | Qt.AlignTop,
                     'https://donkirkby.github.io')
    print(pdf.newPage())
    painter.drawText(painter.window().width()//2,
                     painter.window().height()//2,
                     'Bar')
    print(pdf.newPage())
    painter.end()


def print_document(pdf: QPdfWriter):
    html = "<a href='https://donkirkby.github.io'>donkirkby.github.io</a>"

    document = QTextDocument()
    document.setHtml(html)
    document.print_(pdf)


main()

Ideally, I'd like text and drawing on the same page, but this code tries to keep them separate so they don't overwrite each other. It doesn't work either way.

How can I combine drawings with a text document? Is QTextCursor helpful?


Solution

  • The problem is that every time a QPainter is set then the QPdfWriter is reset. A possible solution is to use the same QPainter and instead of the print method you should use drawContents, you will also have to handle the paging manually.

    from PySide6.QtGui import QPdfWriter, QPainter, QPageSize, QTextDocument, Qt
    from PySide6.QtWidgets import QApplication
    
    
    def main():
        app = QApplication()
        pdf = QPdfWriter("example.pdf")
        pdf.setPageSize(QPageSize.Letter)
    
        painter = QPainter(pdf)
    
        draw_diagram(painter, pdf)
        print_document(painter, pdf)
    
        painter.end()
    
    
    def draw_diagram(painter: QPainter, pdf: QPdfWriter):
        painter.drawArc(
            painter.window().width() // 4,
            painter.window().height() // 2 - painter.window().width() // 4,
            painter.window().width() // 2,
            painter.window().width() // 2,
            0,
            5760,
        )
        painter.drawText(
            0,
            painter.window().height() // 2,
            painter.window().width(),
            painter.window().height() // 10,
            Qt.AlignHCenter | Qt.AlignTop,
            "https://donkirkby.github.io",
        )
        print(pdf.newPage())
        painter.drawText(
            painter.window().width() // 2, painter.window().height() // 2, "Bar"
        )
        print(pdf.newPage())
    
    
    def print_document(painter: QPainter, pdf: QPdfWriter):
        document = QTextDocument()
        document.documentLayout().setPaintDevice(pdf)
    
        html = "<a href='https://donkirkby.github.io'>donkirkby.github.io</a>"
        document.setHtml(html)
    
        document.drawContents(painter)
    
    
    if __name__ == "__main__":
        main()