PyMuPDFでPDFをSVGに変換してPyQt5に表示する
2022/02/13 categories:Python| tags:Python|PyMuPDF|PyQt5|SVG|
PyMuPDFでPDFをSVGに変換して、PyQt5に表示するプログラムを作ってみました。
処理内容
PyMuPDFには、PDFのページをPixmapに変換する機能があり、それを利用するとPyQt5のQGraphicsViewにPDFを表示することが出来ます。しかしPixmapはラスター画像なので、ベクターのPDFをPixmapに変換してしまうとズームをしたときに表示が荒くなってしまいます。PyMuPDFにはPixmapへの変換のほかにSVGへの変換機能もあるようなので、SVGに変換してQGraphicsViewに表示してみました。
PyMuPDFでPDFをSVGに変換するコードは下記の通りで、変換後のsvg_stringはSVGデータの文字列です。
doc = fitz.open('test01.pdf')
page = doc.load_page(0)
svg_string = page.get_svg_image()
QtでSVGデータの文字列をQGraphicsViewに表示可能なデータに変化するためには以下の手順で変換すると良いようです。
- SVGデータの文字列をQtCore.QXmlStreamReaderに渡す
- QXmlStreamReaderをQSvgRendererに渡す
- QSvgRendererをQGraphicsSvgItem.setSharedRendererに渡す
- アイテムをビューにセット
実行結果
pixmapの表示では荒い画像が表示されていますが、SVGの表示ではズームしても描画が荒くなることが無いことが確認できます。しかし、PDFにフォントが埋め込まれていないからか、コンソールには以下のようなエラーがいくつか表示されていました。
qt.svg: link #font_0_4b is undefined!
Pythonコード
import fitz
import sys
from PyQt5 import QtWidgets, QtCore, QtGui, QtSvg
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.resize(600, 800)
self.setWindowTitle('PyQt5 test of PyMuPDF pixmap svg')
self.pixmap_view = GraphicsView()
self.pixmap_view.setScene( QtWidgets.QGraphicsScene() )
self.svg_view = GraphicsView()
self.svg_view.setScene( QtWidgets.QGraphicsScene() )
self.setCentralWidget( QtWidgets.QWidget() )
self.centralWidget().setLayout( QtWidgets.QGridLayout() )
self.centralWidget().layout().addWidget( QtWidgets.QLabel('Pixmap View'), 0, 0, 1, 1)
self.centralWidget().layout().addWidget( QtWidgets.QLabel('SVG View'), 0, 1, 1, 1)
self.centralWidget().layout().addWidget( self.pixmap_view, 1, 0, 1, 1)
self.centralWidget().layout().addWidget( self.svg_view, 1, 1, 1, 1)
self.doc = fitz.open('test01.pdf')
page = self.doc.load_page(0)
self.set_pixmap(page)
self.set_svg(page)
def set_pixmap(self, page):
pix = page.get_pixmap()
byte_array = QtCore.QByteArray( pix.tobytes() )
qpixmap = QtGui.QPixmap()
qpixmap.loadFromData(byte_array)
self.pixmap_view.scene().addPixmap(qpixmap)
def set_svg(self, page):
reader = QtCore.QXmlStreamReader( page.get_svg_image() )
renderer = QtSvg.QSvgRenderer(reader)
item = QtSvg.QGraphicsSvgItem()
item.setSharedRenderer(renderer)
self.svg_view.scene().addItem(item)
class GraphicsView(QtWidgets.QGraphicsView):
dragged = QtCore.pyqtSignal(QtCore.QPointF, QtCore.QPointF)
def __init__(self, *argv, **keywords):
super(GraphicsView, self).__init__(*argv, **keywords)
self._numScheduledScalings = 0
self.points = []
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(200,200,200)))
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
def wheelEvent(self, event):
scale = 1 + event.angleDelta().y()/120 * 0.2
self.scale(scale, scale)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()