PDFBox 3.0导入页面后字体缺失及文档损坏问题求助
PDFBox 3.0导入含嵌入字体的PDF页面到PDF/A时字体缺失及文档损坏问题
问题背景
在PDFBox 2.0中,通过以下代码可将含嵌入字体的PDF页面导入到PDF/A文档并正确复制字体资源,生成的文档包含所需嵌入字体:
PDDocument targetDoc = Loader.loadPDF(targetDocBytes); PDPage sourcePage = Loader.loadPDF(data).getPage(0); final var copiedPage = targetDoc.importPage(sourcePage); copiedPage.setResources(sourcePage.getResources());
但升级到PDFBox 3.0.1后,执行相同操作生成的文档在Adobe Acrobat中显示损坏,且通过PDFBox PreflightParser检测出大量错误。
错误信息
1.4 Trailer Syntax error, /XRef cross reference streams are not allowed 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.1.3 Invalid Font definition, BCDFEE+Roboto-Regular: FontFile entry is missing from FontDescriptor 3.1.14 Invalid Font definition, Unknown font type: XML 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.3.1 Glyph error, The character code 0 in the font program "BCDEEE+Calibri" is missing from the Character Encoding 3.1.8 Invalid Font definition 3.1.2 Invalid Font definition, BCDGEE+TimesNewRomanPS-BoldMT: some mandatory fields are missing from the FontDescriptor: Type, ItalicAngle, FontBBox, Ascent, FontName, StemV, Flags, CapHeight, Descent. 3.1.3 Invalid Font definition, null: FontFile entry is missing from FontDescriptor 3.3.2 Glyph error, invalid font dictionary ==>
完整测试用例
@Test void importPageWithFonts_validateFontInfo() throws IOException { // given final var targetDocBytes = IOUtils.toByteArray(PdfUtilitiesTest.class.getClassLoader().getResourceAsStream("empty.pdf")); String[] additionalFiles = new String[]{ "roboto-14.pdf", }; PDDocument targetDoc = Loader.loadPDF(targetDocBytes); // when for (String fileName : Arrays.asList(additionalFiles)) { byte[] data = IOUtils.toByteArray(PdfUtilitiesTest.class.getClassLoader().getResourceAsStream(fileName)); // verify source is valid PDPage sourcePage = Loader.loadPDF(data).getPage(0); final var copiedPage = targetDoc.importPage(sourcePage); copiedPage.setResources(sourcePage.getResources()); targetDoc.save(Files.createTempFile("merged-fonts", ".pdf").toFile()); } Path tmpFile = Files.createTempFile("fscd-merged", ".pdf"); targetDoc.save(tmpFile.toFile(), CompressParameters.DEFAULT_COMPRESSION); // then // font errors, e.g. Invalid Font definition, BCDFEE+Roboto-Regular: FontFile entry is missing from FontDescriptor assertFontsAreValid(tmpFile); } private static void assertFontsAreValid(Path tmpFile) throws IOException { PreflightParser parser = new PreflightParser(tmpFile.toFile()); final var documentToVerify = (PreflightDocument) parser.parse(); // Get validation result final var result = documentToVerify.validate(); final var resultString = result.getErrorsList().stream() .filter(err -> !err.getErrorCode() .matches("7\\.11\\.2|3\\.1\\.11|2\\.1\\.2|2\\.2\\.1|2\\.4\\.3")) // filter findings from the source documents .map(err -> err.getErrorCode() + " " + err.getDetails()).collect(Collectors.joining("\n")); assertTrue(resultString.isBlank(), resultString); }
解决方案
1. 移除直接覆盖页面资源的代码
PDFBox 3.0中importPage方法已自动处理页面资源的复制,直接调用setResources(sourcePage.getResources())会导致资源对象仍关联到源文档,未被正确导入到目标文档的COS结构中。删除该行代码即可避免资源引用异常。
2. 禁用交叉引用流以符合PDF/A规范
PDF/A不允许使用交叉引用流,保存文档时需显式禁用:
targetDoc.save(tmpFile.toFile(), new SaveOptions() .setUseXRefStream(false) .setCompressParameters(CompressParameters.DEFAULT_COMPRESSION));
3. 手动确保字体资源正确导入(可选)
若importPage未自动处理字体资源,可手动遍历并导入字体到目标文档:
PDResources sourceResources = sourcePage.getResources(); PDResources targetResources = copiedPage.getResources(); for (COSName fontName : sourceResources.getFontNames()) { PDFont sourceFont = sourceResources.getFont(fontName); PDFont importedFont = targetDoc.importFont(sourceFont); targetResources.put(fontName, importedFont); }
4. 完善PDF/A文档结构
确保目标文档具备PDF/A所需的基础结构,比如添加输出Intent:
PDOutputIntent outputIntent = new PDOutputIntent(targetDoc, "sRGB IEC61966-2.1"); outputIntent.setInfo("sRGB IEC61966-2.1"); outputIntent.setOutputCondition("sRGB IEC61966-2.1"); outputIntent.setOutputConditionIdentifier("sRGB"); outputIntent.setRegistryName("http://www.color.org"); targetDoc.getDocumentCatalog().addOutputIntent(outputIntent);
修正后的核心代码
PDDocument targetDoc = Loader.loadPDF(targetDocBytes); try (PDDocument sourceDoc = Loader.loadPDF(data)) { PDPage sourcePage = sourceDoc.getPage(0); PDPage copiedPage = targetDoc.importPage(sourcePage); // 手动导入字体资源(按需使用) PDResources sourceResources = sourcePage.getResources(); PDResources targetResources = copiedPage.getResources(); for (COSName fontName : sourceResources.getFontNames()) { PDFont sourceFont = sourceResources.getFont(fontName); PDFont importedFont = targetDoc.importFont(sourceFont); targetResources.put(fontName, importedFont); } // 添加PDF/A输出Intent PDOutputIntent outputIntent = new PDOutputIntent(targetDoc, "sRGB IEC61966-2.1"); outputIntent.setInfo("sRGB IEC61966-2.1"); outputIntent.setOutputCondition("sRGB IEC61966-2.1"); outputIntent.setOutputConditionIdentifier("sRGB"); outputIntent.setRegistryName("http://www.color.org"); targetDoc.getDocumentCatalog().addOutputIntent(outputIntent); // 保存为PDF/A兼容文档 Path tmpFile = Files.createTempFile("fscd-merged", ".pdf"); targetDoc.save(tmpFile.toFile(), new SaveOptions() .setUseXRefStream(false) .setCompressParameters(CompressParameters.DEFAULT_COMPRESSION)); } finally { targetDoc.close(); }
内容的提问来源于stack exchange,提问作者karma-works
相关产品推荐
相关产品推荐

