使用PDFBox更新表单后文件体积增大的问题咨询
问题描述
使用PDFBox的嵌入式字体子集功能时,即便修改PDF表单前后内容完全一致,生成的新文件体积仍会大于原文件。比如基于b.pdf重新生成c.pdf时,c.pdf的体积会比b.pdf更大。
复现代码
@Test public void updatePdf() { try { PDDocument document = Loader.loadPDF(new File("/Users/sunyangyang/Desktop/a.pdf")); PDAcroForm form = document.getDocumentCatalog().getAcroForm(); PDResources defaultResources = form.getDefaultResources(); File fontFile = new File("/Users/sunyangyang/Desktop/MicrosoftYaHei.ttf"); PDType0Font font = PDType0Font.load(document, new TTFParser().parseEmbedded(new FileInputStream(fontFile)), true); defaultResources.put(COSName.HELV, font); defaultResources.put(COSName.getPDFName("Helve"), font); PDTextField field = (PDTextField) form.getField("goods_name"); if (field != null) { String value = "大米"; field.setValue(value); for (int i = 0; i < value.length(); i++) { font.addToSubset(field.getValue().charAt(i)); } } font.subset(); document.save("/Users/sunyangyang/Desktop/b.pdf"); } catch (IOException e) { e.printStackTrace(); } }
问题原因
- 重复嵌入字体:每次执行代码都会重新加载完整的字体文件并生成新的字体子集,哪怕表单内容没变化。原PDF中已有的字体子集不会被复用,反而会新增一份字体资源到文档里,导致体积不断膨胀。
- 无意义操作触发:不管字段当前值和目标值是否一致,都会执行字体加载、子集化和字段赋值操作,这些操作会额外增加文档的结构数据。
解决方案
要实现“内容无变化时体积不增加”的需求,需要做以下优化:
- 先判断字段值是否需要更新,一致则跳过所有修改操作
- 检查文档中是否已有符合要求的字体子集,避免重复嵌入
- 仅在必要时才执行字体子集化和字段更新
修改后的代码
@Test public void updatePdf() { try { PDDocument document = Loader.loadPDF(new File("/Users/sunyangyang/Desktop/a.pdf")); PDAcroForm form = document.getDocumentCatalog().getAcroForm(); PDResources defaultResources = form.getDefaultResources(); PDTextField field = (PDTextField) form.getField("goods_name"); String targetValue = "大米"; // 字段不存在或值已一致,直接保存退出 if (field == null || targetValue.equals(field.getValue())) { document.save("/Users/sunyangyang/Desktop/b.pdf"); document.close(); return; } PDType0Font font = null; // 检查是否已有可用的子集字体 if (defaultResources != null) { PDType0Font existingFont = (PDType0Font) defaultResources.getFont(COSName.HELV); if (existingFont != null && existingFont.isSubset()) { // 验证已有子集是否包含所需所有字符 boolean hasAllChars = true; for (char c : targetValue.toCharArray()) { if (!existingFont.getFontDescriptor().getFontFile2().contains(c)) { hasAllChars = false; break; } } if (hasAllChars) { font = existingFont; } } } // 没有可用字体,重新加载生成子集 if (font == null) { File fontFile = new File("/Users/sunyangyang/Desktop/MicrosoftYaHei.ttf"); font = PDType0Font.load(document, new TTFParser().parseEmbedded(new FileInputStream(fontFile)), true); defaultResources.put(COSName.HELV, font); defaultResources.put(COSName.getPDFName("Helve"), font); } // 更新字段并完善字体子集 field.setValue(targetValue); for (char c : targetValue.toCharArray()) { font.addToSubset(c); } font.subset(); document.save("/Users/sunyangyang/Desktop/b.pdf"); document.close(); } catch (IOException e) { e.printStackTrace(); } }
优化说明
- 新增字段值一致性校验,完全跳过无意义的修改流程,直接保存原文档结构
- 复用已有字体子集,避免重复嵌入字体文件导致体积增加
- 明确关闭文档资源,避免潜在的内存泄漏和文件损坏问题
内容的提问来源于stack exchange,提问作者孙阳阳
相关产品推荐
相关产品推荐

