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に置き換えて処理を高速にできそうですので、暇があったらコードを書き換えてみようと思います。