PDFの点線を実線におきかえる(PyMuPDF)

Table of Content

はじめに

下記の記事でPyPDF2とreportlabを用いてPDFの点線を実線に置き換える実験を行いました。

この時、PyPDF2の制限で保存したPDFのサイズが増加しました。

今回は別のライブラリを用いて、同じ処理を行います。

PyMuPDF

PyMuPDFはPythonを使用してMuPDFを操作します。
MuPDFは軽量の軽量のPDF、XPS、および電子書籍ビューアです。
ピュアなPythonで記載されたPyPDF2と異なりパフォーマンスの向上が期待できます。
パフォーマンスの詳細は下記を参照してください。
https://pymupdf.readthedocs.io/en/latest/app1/#

簡単な例

以下のサンプルはPDFの内容をSVGに出力後、左上に四角を描画します。
入力に使用しているPDFは以下のものを使用しています。
https://github.com/atlanhq/camelot/files/3565115/Test.pdf

import fitz

doc = fitz.open('test.pdf')
print(doc.pageCount)
page = doc.loadPage(0)
print('ページの領域:', page.rect)
# SVGとして出力
with open('tmp1.svg', 'w') as fp:
    fp.write(page.getSVGimage())

# 左上に四角を描画
rect = fitz.Rect(0, 10, 300, 40)
page.drawRect(rect, color=(1,0,0), width=1)

#doc.saveIncr()   # update file. Save to new instead by doc.save("new.pdf",...)
doc.save("new.pdf")

出力されたSVGをChromeで表示した結果は以下の通りとなります。

点線が削除されています。
また、残念なことにPyPDF2で行ったようにオペコードを直接取得はできないようなので、正確に座標情報を取得したい場合はPyPDF2を用いる必要があるでしょう。

次に、図形を描画したPDFを見てみます。

PyPDF2+reportlabで使用した場合とPyMuPDFで座標が異なることに注意してください。
前者は(0、0)を左下としていますが、MuPDFはページの左上隅を(0、0)としています。これについては下記のIssueで言及されています。

Question: is PyMuPDF rect compatible with reportlab rect? #348
https://github.com/pymupdf/PyMuPDF/issues/348

点線の実線にする

ではPDFの点線を実線におきかえる(PyPDF2 + reportlab)で行ったのと同じように処理を行います。

線情報の取得方法は同じですが、ファイルの書き込みをPyMuPDFで行います。

対象のPDFは以下になります。
https://github.com/mima3/yakusyopdf/blob/master/20200502/%E5%85%B5%E5%BA%AB%E7%9C%8C.pdf

import fitz
import PyPDF2
from PyPDF2.pdf import ContentStream
from PyPDF2.utils import b_

def get_rectangle(content):
    result = []
    for operands, operator in content.operations:
        if operator == b_('re'):
            result.append(fitz.Rect(operands[0], operands[1], operands[0]+ operands[2], operands[1] + operands[3]))
    return result

def read_pdf(path):
    rects = []
    with open(path, "rb") as input_stream:
        input_pdf = PyPDF2.PdfFileReader(input_stream)
        for page in range(input_pdf.numPages):
            print('read', page + 1, '/', input_pdf.numPages)
            page_obj = input_pdf.getPage(page)
            content = page_obj['/Contents'].getObject()
            if not isinstance(content, ContentStream):
                content = ContentStream(content, input_pdf)
            rects.append(get_rectangle(content))

    return rects

path = "test/兵庫県.pdf"
rects = read_pdf(path)
doc = fitz.open(path)
ix = 0
for page in doc:
    print('write', ix + 1, '/', len(doc))
    for rc in rects[ix]:
        # https://github.com/pymupdf/PyMuPDF/issues/348
        new_rc = rc * page._getTransformation()
        page.drawRect(new_rc, color=(1,0,0), width=1)
    ix += 1
# https://pymupdf.readthedocs.io/en/latest/tutorial/#saving
doc.save("new.pdf",  garbage=4, clean=1, deflate=1)  # or doc.saveIncr() 

PyMuPDFでは保存時に圧縮の指定や余分なオブジェクトの削除が行えるので、ファイルサイズを抑えることが可能になっています。

まとめ

PyMuPDFを用いることでファイルサイズを抑制して図形を描画することが可能であることが確認できました。

今回はPyPDF2で線の座標を取得していますが、完全な線でなくていいなら、SVGから線の座標情報を取得することも可能であると思います。