跳转至

生态系统方案

pylopdf有意不重复实现排版、PDF/A生成或数字签名。每个领域都与成熟的库组合解决, 以下方案均由 tests/test_interop.py 中的集成测试保护。

排版与新文档 — typst

使用typst(通过 typst-py)排版报告,然后将字节直接交给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是另一个问题。veraPDF(Java)是事实上的 验证标准。Document.get_pdfa_claim()只读取文档的自我声明。

CJK水印与页眉 — typst × show_pdf_page

Standard-14字体无法绘制中文、日文或韩文。可改用typst排版一页水印 (字体会以子集嵌入),再以矢量形式写入每一页:

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)可接收任何OCR结果:云API、Tesseract,或其他能够返回 (x0, y0, x1, y1, text)的引擎。它会将结果写成不可见文本层,使扫描PDF可搜索, 且因不嵌入字体,文件大小几乎不增加。之后to_markdown()也可用于扫描PDF。