PyMuPDFを使ってみる

2022/02/01 categories:Python| tags:Python|PyMuPDF|PDF|

PythonでPDFを扱えるPyMuPDFを使ってみました。とりあえずチュートリアルに沿って試してみます。

インポートとバージョンの確認

コード

import fitz
print(fitz.__doc__)

実行結果

PyMuPDF 1.19.4: Python bindings for the MuPDF 1.19.0 library.
Version date: 2022-01-01 00:00:01.
Built for Python 3.9 on win32 (64-bit).

PDFを開く

コード

import fitz
doc = fitz.open('a4_page2.pdf')
print( doc )
print( 'page_count    : {}'.format(doc.page_count) )
print( 'metadata      : {}'.format(doc.metadata) )
print( 'get_toc       : {}'.format(doc.get_toc()) )
print( 'chapter_count : {}'.format(doc.chapter_count) )
print( 'is_pdf        : {}'.format(doc.is_pdf) )
print( 'name          : {}'.format(doc.name) )
print( 'outline       : {}'.format(doc.outline) )
print( 'permissions   : {}'.format(doc.permissions) )

実行結果

Document('a4_page2.pdf')
page_count    : 2
metadata      : {'format': 'PDF 1.7', 'title': 'Microsoft PowerPoint - ×ì¼óÆü·çó1', 'author': '', 'subject': '', 'keywords': '', 'creator': '', 'producer': 'Microsoft: Print To PDF', 'creationDate': "D:20220131205321+09'00'", 'modDate': "D:20220131205321+09'00'", 'trapped': '', 'encryption': None}
get_toc       : []
chapter_count : 1
is_pdf        : True
name          : a4_page2.pdf
outline       : None
permissions   : -4

ページを処理する

コード

import fitz
doc = fitz.open('a4_page2.pdf')
print( 'page_count : {}'.format(doc.page_count) )
print( doc.load_page(0) )
print( doc[0] )
print( [ page for page in doc ] )

実行結果

page_count : 2
page 0 of a4_page2.pdf
page 0 of a4_page2.pdf
[page 0 of a4_page2.pdf, page 1 of a4_page2.pdf]

PyQt5にPDFを表示してみる

サンプルとして以下のようなPDFを作成してPyQt5に表示してみました。

PDFのページをPillowのImageに変換してQGraphicsViewに表示

以下のように、チュートリアル通りにPillowを使うコードを試してみましたが、例外が発生してしまいました。

コード

import fitz
import sys
from PIL import Image, ImageQt
from PyQt5 import QtWidgets, QtCore, QtGui

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.view = QtWidgets.QGraphicsView()
        self.view.setScene( QtWidgets.QGraphicsScene() )

        self.setCentralWidget( QtWidgets.QGraphicsView() )

        doc = fitz.open('a4_page2.pdf')
        page = doc.load_page(0)
        pix = page.get_pixmap()

        mode = "RGBA" if pix.alpha else "RGB"
        img = Image.frombytes(mode, [pix.width, pix.height], pix.samples)
        qtimg = ImageQt.ImageQt(img)
        
        qpixmap = QtGui.QPixmap.fromImage(qtimg)
        self.view.scene().addPixmap(qpixmap)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec()

実行結果

qpixmap = QtGui.QPixmap.fromImage(qtimg)

fromImage(QImage, flags: Union[Qt.ImageConversionFlags, Qt.ImageConversionFlag] = Qt.AutoColor): argument 1 has unexpected type 'ImageQt'

PDFのページをPillowのImageに変換して、画像として保存してからQGraphicsViewに表示

以下のように、一度画像ファイルとして保存して、そのファイルを表示するようなコードの場合は正しく表示されました。ただし、これでは一度ファイル出力しているので、ディスクへのアクセスが無駄だと思います。

コード

import fitz
import sys
from PIL import Image, ImageQt
from PyQt5 import QtWidgets, QtCore, QtGui

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.view = QtWidgets.QGraphicsView()
        self.view.setScene( QtWidgets.QGraphicsScene() )

        self.setCentralWidget(self.view)

        doc = fitz.open('a4_page2.pdf')
        page = doc.load_page(0)
        pix = page.get_pixmap()

        mode = "RGBA" if pix.alpha else "RGB"
        img = Image.frombytes(mode, [pix.width, pix.height], pix.samples)
        qtimg = ImageQt.ImageQt(img)
        qtimg.save('temp.png')
        
        qpixmap = QtGui.QPixmap('temp.png')
        self.view.scene().addPixmap(qpixmap)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec()

実行結果

PDFのページをQImageに変換してQGraphicsViewに表示

チュートリアルに書いている通りに以下のコードで試してみましたが、カラーのPDFが白黒に表示されて、ピクセルがずれて画像が斜めに表示されてしまい、正しく表示されませんでした。おそらくフォーマットが間違っていて、バイト列を正しくQImageに変換できていないような感じでした。

コード

import fitz
import sys
from PyQt5 import QtWidgets, QtGui

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.view = QtWidgets.QGraphicsView()
        self.view.setScene( QtWidgets.QGraphicsScene() )

        self.setCentralWidget(self.view)

        doc = fitz.open('a4_page2.pdf')
        page = doc.load_page(0)
        pix = page.get_pixmap()

        fmt = QtGui.QImage.Format_RGBA8888 if pix.alpha else QtGui.QImage.Format_RGB888
        qtimg = QtGui.QImage(pix.samples_ptr, pix.width, pix.height, fmt)

        qpixmap = QtGui.QPixmap.fromImage(qtimg)
        self.view.scene().addPixmap(qpixmap)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec()

実行結果

PDFのページをQByteArrayに変換してからQGraphicsViewに表示

上記の通り、チュートリアル通りのコードでは正しく表示されなかったため、以下のようなコードを試したら正常に表示されました。

コード

import fitz
import sys
from PyQt5 import QtWidgets, QtCore, QtGui

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.view = QtWidgets.QGraphicsView()
        self.view.setScene( QtWidgets.QGraphicsScene() )

        self.setCentralWidget(self.view)

        doc = fitz.open('a4_page2.pdf')
        page = doc.load_page(0)
        pix = page.get_pixmap()
        byte_array = QtCore.QByteArray( pix.tobytes() )
        qpixmap = QtGui.QPixmap()
        qpixmap.loadFromData(byte_array)
        self.view.scene().addPixmap(qpixmap)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec()

所感

PythonでPDFを扱うツールを作る為に、以前はsubprocessでPopplerやPDFtkを実行していましたが、それらの処理をPyMuPDFに置き換えて処理を高速にできそうですので、暇があったらコードを書き換えてみようと思います。

Share post

Related Posts

Comments

comments powered by Disqus