コンテンツにスキップ

エコシステム連携

pylopdf は組版・PDF/A 生成・電子署名をあえて再実装しません。それぞれ実績ある ライブラリとの連携で解決し、以下のレシピはすべて tests/test_interop.py の統合テストで保証しています。

組版・新規文書の生成 = typst

レポートや帳票は typsttypst-py 経由)で組版し、bytes をそのまま pylopdf へ:

import typst
import pylopdf

pdf_bytes = typst.compile("report.typ")   # 組版は typst
doc = pylopdf.open(stream=pdf_bytes)      # 編集・抽出・結合は pylopdf

新規文書の PDF/A = typst

typst の PDF バックエンド(krilla)は検証付きの PDF/A-1b〜4・PDF/UA-1 出力に 対応しています:

pdf_a: bytes = typst.compile("report.typ", pdf_standards="a-2b")
pylopdf.open(stream=pdf_a).get_pdfa_claim()   # (2, "B")

既存 PDF の PDF/A 変換・検証は別問題で、検証は veraPDF (Java)が事実上の標準です。Document.get_pdfa_claim() は自己宣言の読み取りだけを 行います。

日本語の透かし・ヘッダ / フッタ = typst × show_pdf_page

標準 14 フォントでは日本語を描けません。代わりに typst で 1 ページの透かしを 組み(フォントはサブセット埋め込みされる)、全ページへベクタのまま焼き込みます:

from pylopdf_fonts_cjk import sans_path  # pip install pylopdf[cjk]

stamp_typ = """
#set page(width: 595pt, height: 842pt, fill: none)
#set text(font: "Noto Sans JP", size: 48pt, fill: rgb(255, 0, 0, 40%))
#align(center + horizon)[社外秘]
"""
stamp = pylopdf.open(stream=typst.compile(stamp_typ.encode(), font_paths=[str(sans_path().parent)]))
for page in doc:
    page.show_pdf_page((0, 0, page.rect.width, page.rect.height), stamp)

焼き込み後もテキストはベクタのまま — page.get_text() で「社外秘」が抽出できます。

電子署名(PAdES)= pyHanko

pyHanko(MIT)は増分更新で署名するため、 pylopdf の出力バイト列は署名後も先頭に無加工で残ります(統合テストでバイト単位に 検証済み):

import io
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
from pyhanko.sign import signers

signer = signers.SimpleSigner.load("key.pem", "cert.pem")
out = signers.sign_pdf(
    IncrementalPdfFileWriter(io.BytesIO(doc.tobytes())),
    signers.PdfSignatureMetadata(field_name="Signature1"),
    signer=signer,
)
signed_pdf: bytes = out.getvalue()

pylopdf 自身のフォーム API は署名フィールドへの記入を意図的に拒否し、ここへ 誘導します。

OCR = 好きなエンジンを持ち込む

Page.insert_ocr_text_layer(words) は、クラウド API でも Tesseract でも (x0, y0, x1, y1, テキスト) を返すあらゆる OCR の結果を不可視テキスト層として 書き込み、スキャン PDF を searchable にします(フォント非埋め込みでサイズ増は ほぼゼロ)。その後は to_markdown() もスキャン PDF に対して機能します。