当前位置:首页>python>Python 将 PDF 转成 PDF/A、PDF/X,以及 PDF/A 转回普通 PDF

Python 将 PDF 转成 PDF/A、PDF/X,以及 PDF/A 转回普通 PDF

  • 2026-09-18 18:24:45
Python 将 PDF 转成 PDF/A、PDF/X,以及 PDF/A 转回普通 PDF
文件能打开,档案系统却提示“不符合 PDF/A”;设计稿在电脑上看着正常,送到印厂又被退回来,说不是 PDF/X。

这种问题我一般不先翻 Python 库。

PDF/A、PDF/X 都不是改个扩展名,也不是往元数据里塞一行版本号。字体、颜色空间、ICC Profile、透明度、加密、输出意图,只要有一处不符合,文件照样能打开,但验证就是不过。

Python 在这里更适合做调度,真正重写 PDF 的活交给 Ghostscript。

PDF/A 偏长期归档,要求文件尽量自包含;PDF/X 偏印刷交付,更关心 CMYK、字体嵌入、页面裁切框和输出条件。Ghostscript 的 pdfwrite 可以生成 PDF/A 1~3 的 B 级文件,也能处理常用 PDF/X 输出,但必须提供对应的定义文件和 ICC Profile。

先准备三个东西:

pip install pikepdf

系统安装 Ghostscript,然后从它的 lib 目录复制:

PDFA_def.psPDFX_def.ps

这两个文件不能原样扔进去跑。里面的 ICCProfile 要改成机器上的绝对路径。做 PDF/A-2b,我一般放一个可靠的 RGB ICC;做 PDF/X-3,则按印厂要求放 CMYK ICC。ICC 用错了,程序可能不报错,颜色却已经不是原来的颜色,这种问题比转换失败更麻烦。

Python 包装层不用写得太花:

from pathlib import Pathimport shutilimport subprocessclassPdfStandardizer:def__init__(self) -> None:        self.gs = self._find_ghostscript()    @staticmethoddef_find_ghostscript() -> str:for name in ("gs", "gswin64c", "gswin32c"):            executable = shutil.which(name)if executable:return executableraise RuntimeError("没有找到 Ghostscript 命令行程序")def_convert(        self,        source: str,        target: str,        definition: str,        standard_args: list[str],    ) -> None:        src = Path(source).resolve()        dst = Path(target).resolve()        rule = Path(definition).resolve()ifnot src.is_file():raise FileNotFoundError(f"输入文件不存在:{src}")ifnot rule.is_file():raise FileNotFoundError(f"定义文件不存在:{rule}")        dst.parent.mkdir(parents=True, exist_ok=True)        command = [            self.gs,"-dBATCH","-dNOPAUSE","-dSAFER","-sDEVICE=pdfwrite","-dPDFACompatibilityPolicy=2",f"-sOutputFile={dst}",            *standard_args,            str(rule),            str(src),        ]        result = subprocess.run(            command,            capture_output=True,            text=True,            encoding="utf-8",            errors="replace",        )if result.returncode != 0:raise RuntimeError("PDF 转换失败:\n"                + result.stderr[-2000:]            )defto_pdfa2b(self, source: str, target: str, definition: str) -> None:        self._convert(            source,            target,            definition,            ["-dPDFA=2","-sColorConversionStrategy=RGB",            ],        )defto_pdfx3(self, source: str, target: str, definition: str) -> None:        self._convert(            source,            target,            definition,            ["-dPDFX=3","-sColorConversionStrategy=CMYK",            ],        )

调用时把归档和印刷分开,别共用一套 ICC:

worker = PdfStandardizer()worker.to_pdfa2b("incoming/contract.pdf","out/contract-a2b.pdf","profiles/PDFA_def.ps",)worker.to_pdfx3("incoming/brochure.pdf","out/brochure-x3.pdf","profiles/PDFX_def.ps",)

这里我把 PDFACompatibilityPolicy 设成了 2。遇到不兼容内容就直接失败,不让它生成一个带着 PDF/A 声明、实际却不合规的文件。Ghostscript 默认策略可能保留不兼容特性,只打印警告,这种“看起来成功”的结果我不太信。

转换完成也不能只看返回码。至少再跑一次 veraPDF:

verapdf -f 2b out/contract-a2b.pdf

看到 isCompliant="true" 才算过。pikepdf 能读出文件声称的 PDF/A 级别,但它检查的是“文件怎么声明自己”,不是完整合规性;真正验收还得用验证器。

再说 PDF/A 转回普通 PDF。

这句话容易把人带偏。PDF/A 本身就是 PDF 的受限版本,不存在把页面内容“解码回去”。多数业务所谓转回普通 PDF,只是取消 PDF/A 合规声明,后面允许继续编辑、合并或者重新加密。

我一般只移除 XMP 里的 PDF/A 身份,不随手删 OutputIntent。输出意图不是垃圾元数据,颜色管理后面可能还要用。

from pathlib import Pathimport pikepdfdefremove_pdfa_claim(source: str, target: str) -> None:    src = Path(source)    dst = Path(target)    dst.parent.mkdir(parents=True, exist_ok=True)with pikepdf.open(src) as pdf:with pdf.open_metadata() as metadata:for key in ("pdfaid:part", "pdfaid:conformance"):if key in metadata:del metadata[key]        pdf.save(dst, fix_metadata_version=False)

调用:

remove_pdfa_claim("out/contract-a2b.pdf","out/contract-normal.pdf",)

还有一个容易漏的点:Ghostscript 会重新组织 PDF 内部对象,目标是尽量保持视觉效果一致,不保证原始结构逐项不变。带数字签名、交互表单、图层或者特殊书签的文件,转换后必须单独回归,尤其是数字签名,重写文件后基本不能再按原签名验。

所以这套流程不是“执行命令—文件生成—结束”,而是转换、验证、抽样打开、检查字体和颜色。少一步,问题通常不会在测试环境暴露,往往等档案系统或者印厂替你发现。

最新文章

随机文章