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

Python 将 PDF 转成 PDF/A、PDF/X,再把 PDF/A 转回普通 PDF

  • 2026-10-11 06:12:38
Python 将 PDF 转成 PDF/A、PDF/X,再把 PDF/A 转回普通 PDF
命令返回码是 0,生成的文件也能打开,送进档案系统却提示“不是 PDF/A”;换到印厂,又报输出意图缺失。

这种转换最容易出现的就是假成功。

PDF/A、PDF/X 都不是在文件属性里改个标记,更不是补一段 XMP 元数据就结束了。字体有没有嵌入、颜色空间是否合规、加密和脚本有没有清掉、ICC 配置是否正确,都可能让文件验证失败。

这类活我一般不让 PyPDF2 硬扛。Python 负责参数、批处理和异常兜底,真正重写 PDF 的工作交给 Ghostscript。

PDF/A 偏长期归档,重点是文件脱离外部环境后仍能稳定显示;PDF/X 偏印刷交付,重点是颜色、页面框和输出条件。Ghostscript 的 pdfwrite 会重新生成 PDF,并尽量保持原文件的视觉效果。当前文档中,PDF/A 转换主要支持 1、2、3 三个版本的 b 级合规;PDF/X 这里我固定用 PDF/X-3,不在版本兼容上赌运气。

先准备三个东西:

  • Ghostscript 命令行程序;
  • PDFA_def.ps 和 PDFX_def.ps;
  • 对应的 ICC 颜色配置文件。

两个 .ps 文件可以从 Ghostscript 安装目录的 lib 目录复制出来。里面的 ICCProfile 要改成绝对路径。PDF/A 可以使用合适的 RGB 配置,PDF/X 则应该使用印厂或输出设备指定的 CMYK 配置。这个地方别随手找个 ICC 文件顶上,文件能生成,不代表颜色能交付。Ghostscript 官方也要求通过定义文件写入 OutputIntent。

Python 脚本不用写得太花:

from __future__ import annotations

import os
import shutil
import subprocess
from pathlib import Path


classPdfProfileConverter:
def__init__(self, gs_bin: str | None = None):
        self.gs_bin = gs_bin or self._find_ghostscript()

    @staticmethod
def_find_ghostscript() -> str:
        candidates = (
            ["gswin64c.exe", "gswin32c.exe"]
if os.name == "nt"
else ["gs"]
        )

for name in candidates:
            command = shutil.which(name)
if command:
return command

raise RuntimeError("没有找到 Ghostscript 命令行程序")

def_run(self, arguments: list[str]) -> None:
        result = subprocess.run(
            [self.gs_bin, *arguments],
            text=True,
            capture_output=True,
            check=False,
        )

if result.returncode != 0:
            message = result.stderr.strip() or result.stdout.strip()
raise RuntimeError(f"PDF 转换失败:\n{message}")

defto_pdfa(
        self,
        source: Path,
        target: Path,
        definition: Path,
    )
 -> None:

        self._run([
"-dBATCH",
"-dNOPAUSE",
"-dSAFER",
"-sDEVICE=pdfwrite",
"-dPDFA=2",
"-dPDFACompatibilityPolicy=2",
"-sColorConversionStrategy=RGB",
"-dEmbedAllFonts=true",
"-dSubsetFonts=true",
f"-sOutputFile={target.resolve()}",
            str(definition.resolve()),
            str(source.resolve()),
        ])

defto_pdfx(
        self,
        source: Path,
        target: Path,
        definition: Path,
    )
 -> None:

        self._run([
"-dBATCH",
"-dNOPAUSE",
"-dSAFER",
"-sDEVICE=pdfwrite",
"-dPDFX=3",
"-dPDFACompatibilityPolicy=2",
"-sColorConversionStrategy=CMYK",
"-sProcessColorModel=DeviceCMYK",
f"-sOutputFile={target.resolve()}",
            str(definition.resolve()),
            str(source.resolve()),
        ])

defto_normal_pdf(self, source: Path, target: Path) -> None:
        self._run([
"-dBATCH",
"-dNOPAUSE",
"-dSAFER",
"-sDEVICE=pdfwrite",
"-dCompatibilityLevel=1.7",
"-dAutoRotatePages=/None",
f"-sOutputFile={target.resolve()}",
            str(source.resolve()),
        ])

调用时把定义文件传进去:

from pathlib import Path

worker = PdfProfileConverter()

worker.to_pdfa(
    Path("contract.pdf"),
    Path("contract-a2b.pdf"),
    Path("profiles/PDFA_def.ps"),
)

worker.to_pdfx(
    Path("brochure.pdf"),
    Path("brochure-x3.pdf"),
    Path("profiles/PDFX_def.ps"),
)

worker.to_normal_pdf(
    Path("contract-a2b.pdf"),
    Path("contract-normal.pdf"),
)

我这里特意把 PDFACompatibilityPolicy 设置成了 2。

默认值 0 有点坑人:遇到不兼容内容时,Ghostscript 可能继续输出,文件里甚至还保留着 PDF/A 元数据,但实际并不合规。设置成 2 后,只要碰到无法处理的内容就直接失败。批处理宁可明确报错,也不要生产一批看起来正常的假 PDF/A。官方对这个参数的说明也是:0 继续输出,1 忽略不兼容特性,2 立即中止。

转换完成还不能直接交付。PDF/A 我会再跑一次 veraPDF:

defvalidate_pdfa(pdf_file: Path, validator: str = "verapdf") -> None:
    result = subprocess.run(
        [validator, str(pdf_file.resolve())],
        text=True,
        capture_output=True,
        check=False,
    )

    report = result.stdout + result.stderr
if"PASS"notin report.upper():
raise RuntimeError(f"PDF/A 验证未通过:\n{report}")

veraPDF 本来就是专门做 PDF/A 合规验证的,覆盖 PDF/A-1 到 PDF/A-4 的验证规则。Ghostscript 负责生成,veraPDF 负责验收,这两个角色不要混在一起。

至于“PDF/A 转回 PDF”,有一点得说清:PDF/A 本来就是 PDF 的受限子集。所谓转回普通 PDF,通常只是使用 pdfwrite 再重写一次,不再声明 PDF/A 合规。

但已经被转换过程删除的 JavaScript、加密、外部依赖或者被压平的透明效果,不会凭空恢复。这个操作只能解除交付格式上的限制,不能把原文件丢掉的能力找回来。

所以原始 PDF 别删。归档文件、印刷文件、业务原件,最好分开保存。拿一个 PDF 在三种标准之间反复横跳,最后通常没人说得清它到底还是不是原来那个文件。

最新文章

随机文章