pdf/a是iso 19005定义的归档标准,要求字体嵌入、禁加密、禁js、明确定义颜色空间和元数据;c#默认生成的pdf不满足这些强制约束,需用itext7.4.0+显式配置pdf/a合规性。

PDF/A 是什么,为什么 C# 默认生成的 PDF 不是 PDF/A
PDF/A 不是“带 A 的 PDF”,而是 ISO 标准(ISO 19005)定义的一类严格归档格式:所有字体必须嵌入、禁止加密、禁用 JavaScript、颜色空间需明确指定、元数据必须完整。C# 原生 System.Drawing.Printing 或常见库如 iTextSharp(旧版)、PdfSharp 默认输出的是普通 PDF,连字体嵌入都未必触发——更别说校验 PDF/A 合规性了。
关键点在于:生成 ≠ 合规。你调用 document.Add() 写入文字,若字体未显式嵌入且子集化,或未写入 XMP 元数据块,工具(如 veraPDF)一扫就报 MissingEmbeddedFont 或 InvalidOutputIntent。
用 iText7 创建 PDF/A-1b 或 PDF/A-2u 的最小必要步骤
iText7 是目前 C# 生态中对 PDF/A 支持最稳的库(注意:必须用 iText7.4.0+,旧版 iTextSharp 不支持 PDF/A)。它不靠“开关”启用 PDF/A,而靠构造特定 PdfWriter 和 PdfDocument 实例,并全程遵守约束链。
- 必须使用
PdfWriter构造时传入new WriterProperties().SetPdfVersion(PdfVersion.PDF_1_7).SetPdfAConformance(PdfAConformance.PDF_A_1B)(或PDF_A_2U) - 文档创建后立即调用
pdfDoc.SetTagged()(PDF/A-1b 要求逻辑结构标签) - 所有字体必须用
PdfFontFactory.CreateFont(..., PdfEncodings.IDENTITY_H, true)显式加载并设为嵌入(第三个参数true) - 必须设置输出意图(OutputIntent):用
sRGB IEC61966-2.1配置项,否则 veraPDF 报MissingOutputIntent - 必须写入 XMP 元数据:调用
pdfDoc.GetCatalog().SetLang("en-US")+pdfDoc.GetCatalog().SetViewerPreferences(...),再手动注入 XMP 字符串(iText7 不自动补全)
示例关键片段:
PdfWriter writer = new PdfWriter("out.pdf",
new WriterProperties().SetPdfVersion(PdfVersion.PDF_1_7)
.SetPdfAConformance(PdfAConformance.PDF_A_1B));
PdfDocument pdfDoc = new PdfDocument(writer);
pdfDoc.SetTagged();
// 加载字体(必须!)
PdfFont font = PdfFontFactory.CreateFont(StandardFonts.HELVETICA,
PdfEncodings.IDENTITY_H, true);
// 设置 OutputIntent(硬编码路径,不可省)
ColorSpace cs = new IccBased(new PdfName("sRGB IEC61966-2.1"));
pdfDoc.GetCatalog().Put(new PdfName("OutputIntents"),
new PdfArray().Add(new PdfDictionary()
.Put(new PdfName("S"), new PdfName("GTS_PDFA1"))
.Put(new PdfName("OutputCondition"), new PdfString("sRGB IEC61966-2.1"))
.Put(new PdfName("OutputConditionIdentifier"), new PdfString("sRGB IEC61966-2.1"))
.Put(new PdfName("Info"), new PdfString("sRGB IEC61966-2.1"))
.Put(new PdfName("DestOutputProfile"), new PdfStream(cs.GetProfile()))));
常见失败现象和对应检查点
用 veraPDF 或 PDF/A 验证器扫出失败,别急着改代码——先看错误类型,再定位环节:
-
MissingEmbeddedFont:不是“用了系统字体”,而是没在PdfFontFactory.CreateFont()中传true;或用了BaseFont.CreateFont()(iText7 已弃用,会静默失败) -
InvalidOutputIntent:OutputIntent 字典字段名拼错(比如写成"OutputConditionID"而非"OutputConditionIdentifier"),或 profile 流未正确绑定 -
MissingXMPMetadata:没调用pdfDoc.GetCatalog().SetLang(),或没手动注入 XMP(iText7 不自动生成,需用pdfDoc.GetCatalog().Put(new PdfName("Metadata"), metadataStream)补) -
NonEmbeddedFont:字体文件路径无效(如相对路径在服务端找不到),或字体本身不支持 Unicode 子集(某些 .ttf 有 license 限制)
验证必须用 veraPDF(开源)或 callas pdfToolbox(商业),Windows 自带的“打印为 PDF”或 Adobe Acrobat “另存为 PDF/A” 按钮生成的文件,常绕过底层校验,不可信。
PDF/A-1b vs PDF/A-2u:选哪个,差别在哪
PDF/A-1b(ISO 19005-1)只保证视觉可再现,不支持透明度、图层、JPEG2000;PDF/A-2u(ISO 19005-2)允许 JPEG2000、OpenType 字体、数字签名,且支持 Unicode 文本搜索(u 即 Unicode)。但代价是:PDF/A-2u 对字体嵌入要求更严(必须含 Unicode cmap),且部分老旧归档系统只认 PDF/A-1b。
- 如果目标系统明确要求“PDF/A-1b”,别用
PdfAConformance.PDF_A_2U—— veraPDF 会直接判失败,不给提示 - PDF/A-2u 必须用
PdfEncodings.IDENTITY_H+ 真实 Unicode 字体(如 Noto Sans CJK),用StandardFonts.HELVETICA会因缺少 cmap 报InvalidUnicodeMapping - 两者都不支持
AcroForm表单字段的交互行为,但 PDF/A-2u 允许表单域存在(只读),PDF/A-1b 连域对象都禁止
归档场景下,除非你确定下游系统支持 PDF/A-2u 且需要 Unicode 搜索,否则优先选 PDF/A-1b——兼容性陷阱少,验证通过率高。
字体嵌入、OutputIntent 字典、XMP 元数据这三处,漏一个,veraPDF 就红标。没有“差不多合规”,只有“全绿才过关”。










