0

I'm using QT (PySide) to view PDFs (using the PyMuPDF library) but when I resize I get a shearing artifact.

Like this:enter image description here

Here is a minimal example:

import sys
import fitz

from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QScrollArea
from PySide6.QtGui import QPixmap, QImage
from PySide6.QtCore import Qt

class PDFWindow(QMainWindow):
    def __init__(self, filename):
        super().__init__()
        
        # Open the PDF and get the first page
        self.pdf_doc = fitz.open(filename)
        self.page = self.pdf_doc[0]

        # Create a QLabel to hold the rendered page
        self.label = QLabel()
        self.label.setAlignment(Qt.AlignCenter)

        # Create a QScrollArea to hold the label
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidget(self.label)
        self.scroll_area.setWidgetResizable(True)
        self.setCentralWidget(self.scroll_area)

        # Render the page and set it as the pixmap for the label
        self.render_page()

        # Connect the windowResized signal to the render_page function
        self.resizeEvent = self.render_page

    def render_page(self, event=None):
        zoom = 1
        if event is not None:
            bound = self.page.bound()
            page_width, page_height = bound.width, bound.height
            zoom = min(event.size().width() / page_width,
                   event.size().height() / page_height)

        pixmap = self.page.get_pixmap(matrix=fitz.Matrix(zoom, zoom),
                                 colorspace='rgb')
        image = QImage(pixmap.samples, pixmap.width, pixmap.height,
                       QImage.Format_RGB888)
        cur_pixmap= QPixmap.fromImage(image)
        self.label.setPixmap(cur_pixmap)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = PDFWindow("example.pdf")
    window.show()
    sys.exit(app.exec())


Matt Harrison
  • 1,225
  • 11
  • 12
  • 1
    Some Qt version (PyQt and PySide) require to also specify the size of one image line (so-called "stride"). Please retry after changing image creation to `image = Image(pixmap.samples, pixmap.width, pixmap.height, pixmap.stride, QImage.Format_RGB888)`. – Jorj McKie Feb 02 '23 at 17:27
  • You will see a **big** performance boost if you use `pixmap.samples_ptr` instead of simply `pixmap.samples`. Can be 1000 times faster, because Qt will address the image area itself via the provided pointer - instead of requesting pixmap to make a `bytes` which afterwards will be copied once more by Qt ... – Jorj McKie Feb 02 '23 at 17:32
  • This code fails `image = QImage(pixmap.samples_ptr, pixmap.width, pixmap.height, pixmap.stride, QImage.Format_RGB888)` with `TypeError`. – Matt Harrison Feb 02 '23 at 18:37
  • It looks like adding the stride alone works. If you want to write that up in an answer, I'll accept it. Still researching about ptr because I would love to get better perf. – Matt Harrison Feb 02 '23 at 18:56
  • not sure where the type error came from. As per the books, the call pattern `(data, width, height, stride, format)` is one of the supported method overloads. But maybe that PySide - in conrast to PyQt - does not support a Python pointer instead of data (`bytes`). Recent Qt versions do support both, `bytes` and extended pointer (`int`). – Jorj McKie Feb 03 '23 at 22:29

0 Answers0