Apache PDFBox生成PDF/A-2b不通过VeraPDF校验的问题求解
解决PDFBox生成PDF/A-2b时VeraPDF校验失败的问题
第一个错误:元数据流不符合要求(Rule 6.6.2.1 test 1)
问题分析
你生成的XMP元数据开头带有UTF-8 BOM(),导致VeraPDF无法正确识别为符合ISO 32000-1要求的元数据流。这是PDFBox的XmpSerializer默认行为——它会在序列化XMP时添加BOM,但PDF/A标准要求XMP元数据应使用无BOM的UTF-8编码。
修复方案
修改XMP序列化逻辑,使用Writer指定无BOM的UTF-8编码输出,同时显式设置元数据流的Subtype为XML:
// 替换原有的XMP序列化代码段 cosStream.setName(COSName.SUBTYPE, COSName.XML); try (Writer cosXMPWriter = new OutputStreamWriter(cosStream.createOutputStream(), StandardCharsets.UTF_8)) { XMPMetadata xmp = XMPMetadata.createXMPMetadata(); // 原有的XMP schema配置代码保持不变 new XmpSerializer().serialize(xmp, cosXMPWriter, true); document.getDocumentCatalog().setMetadata(new PDMetadata(cosStream)); }
第二个错误:DeviceGray色彩空间使用不符合要求(Rule 6.2.4.3 test 4)
问题分析
PDFBox绘制文本时默认使用DeviceGray色彩空间,但PDF/A-2b规范要求:使用DeviceGray时,必须满足两个条件之一:
- 已设置设备无关的
DefaultGray色彩空间 - 文档包含PDF/A输出意图(OutputIntent)
你的代码既未配置默认色彩空间,也未添加输出意图,因此校验失败。
修复方案
推荐添加PDF/A输出意图(更符合规范的完整要求),步骤如下:
- 获取sRGB ICC配置文件(可从开源渠道获取,或使用系统自带的配置文件)
- 在代码中添加输出意图:
// 加载sRGB ICC配置文件 File iccFile = new File("sRGB.icc"); // 替换为你的ICC文件实际路径 try (FileInputStream iccStream = new FileInputStream(iccFile)) { PDOutputIntent outputIntent = new PDOutputIntent(document, iccStream); outputIntent.setInfo("sRGB IEC61966-2.1"); outputIntent.setOutputCondition("sRGB IEC61966-2.1"); outputIntent.setOutputConditionIdentifier("sRGB"); outputIntent.setRegistryName("http://www.color.org"); document.getDocumentCatalog().addOutputIntent(outputIntent); }
若不想使用输出意图,也可以设置设备无关的DefaultGray色彩空间:
PDColorSpace defaultGray = PDICCBased.load(document, new FileInputStream("sRGB.icc")); document.getDocumentCatalog().setDefaultGray(defaultGray);
修正后的完整代码
import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased; import org.apache.pdfbox.pdmodel.xmp.*; import com.adobe.xmp.XMPMetadata; import com.adobe.xmp.impl.XmpSerializer; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Calendar; public class PDFA2bGenerator { public static void main(String[] args) throws Exception { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PDDocument document = new PDDocument(); COSStream cosStream = new COSStream()) { PDPage page = new PDPage(); document.addPage(page); // 设置文档基础信息 PDDocumentInformation docInfo = new PDDocumentInformation(); docInfo.setTitle("Name"); docInfo.setCreator("Creator"); docInfo.setSubject("Subject"); document.setDocumentInformation(docInfo); // 生成无BOM的XMP元数据 cosStream.setName(COSName.SUBTYPE, COSName.XML); try (Writer xmpWriter = new OutputStreamWriter(cosStream.createOutputStream(), StandardCharsets.UTF_8)) { XMPMetadata xmp = XMPMetadata.createXMPMetadata(); PDFAIdentificationSchema pdfaSchema = xmp.createAndAddPFAIdentificationSchema(); pdfaSchema.setPart(2); pdfaSchema.setConformance("B"); DublinCoreSchema dcSchema = xmp.createAndAddDublinCoreSchema(); dcSchema.setTitle("Name"); dcSchema.addCreator("Creator"); dcSchema.setDescription("Subject"); XMPBasicSchema basicSchema = xmp.createAndAddXMPBasicSchema(); Calendar now = Calendar.getInstance(); basicSchema.setCreateDate(now); basicSchema.setModifyDate(now); basicSchema.setMetadataDate(now); basicSchema.setCreatorTool("Creator Tool"); new XmpSerializer().serialize(xmp, xmpWriter, true); document.getDocumentCatalog().setMetadata(new PDMetadata(cosStream)); } // 设置查看器偏好 PDViewerPreferences prefs = new PDViewerPreferences(new COSDictionary()); prefs.setDisplayDocTitle(true); document.getDocumentCatalog().setViewerPreferences(prefs); // 添加PDF/A输出意图(解决色彩空间问题) File iccFile = new File("sRGB.icc"); // 替换为实际路径 try (FileInputStream iccStream = new FileInputStream(iccFile)) { PDOutputIntent outputIntent = new PDOutputIntent(document, iccStream); outputIntent.setInfo("sRGB IEC61966-2.1"); outputIntent.setOutputCondition("sRGB IEC61966-2.1"); outputIntent.setOutputConditionIdentifier("sRGB"); outputIntent.setRegistryName("http://www.color.org"); document.getDocumentCatalog().addOutputIntent(outputIntent); } // 加载字体并绘制文本 File fontFile = new File("C:\\Windows\\Fonts\\arial.ttf"); PDType0Font font = PDType0Font.load(document, fontFile); try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) { contentStream.beginText(); contentStream.setFont(font, 12); contentStream.newLineAtOffset(100, 700); contentStream.showText("Hello PDF/A-2b World!"); contentStream.endText(); } // 保存并校验 document.save(baos); try (PDFAParser parser = Foundries.defaultInstance().createParser(new ByteArrayInputStream(baos.toByteArray()), PDFAFlavour.PDFA_2_B)) { PDFAValidator validator = Foundries.defaultInstance().createValidator(PDFAFlavour.PDFA_2_B, false); ValidationResult result = validator.validate(parser); System.out.println(result.isCompliant()); // 预期输出true } } } }
内容的提问来源于stack exchange,提问作者SirNight
相关产品推荐
相关产品推荐

