PDFBOX加载日文字体时embedSubset设为true报错,如何嵌入子集字符?
PDFBOX嵌入日文字体子集报错解决方案
问题概述
- 使用PDFBOX加载日文字体(如TsukuhouMincho-Regular)时,将
embedSubset参数设为true会触发IllegalArgumentException,提示"No glyph for U+65E5 (日) in font TsukuhouMincho-Regular" - 将
embedSubset设为false时可正常运行,但输出PDF文件体积过大,需要仅嵌入所用字符子集以减小文件大小 - 测试过PDFBOX 2.0.30、多个2.x.x及3.x.x版本,问题均未解决;iText可实现该功能,但因许可证限制无法使用
报错堆栈信息
java.lang.IllegalArgumentException: No glyph for U+65E5 (日) in font TsukuhouMincho-Regular at org.apache.pdfbox.pdmodel.font.PDCIDFontType2.encode(PDCIDFontType2.java:389) at org.apache.pdfbox.pdmodel.font.PDType0Font.encode(PDType0Font.java:427) at org.apache.pdfbox.pdmodel.font.PDFont.encode(PDFont.java:337) at org.apache.pdfbox.pdmodel.font.PDFont.getStringWidth(PDFont.java:368) at org.apache.pdfbox.pdmodel.interactive.form.AppearanceGeneratorHelper.calculateFontSize(AppearanceGeneratorHelper.java:828) at org.apache.pdfbox.pdmodel.interactive.form.AppearanceGeneratorHelper.insertGeneratedAppearance(AppearanceGeneratorHelper.java:488) at org.apache.pdfbox.pdmodel.interactive.form.AppearanceGeneratorHelper.setAppearanceContent(AppearanceGeneratorHelper.java:424) at org.apache.pdfbox.pdmodel.interactive.form.AppearanceGeneratorHelper.setAppearanceValue(AppearanceGeneratorHelper.java:248) at org.apache.pdfbox.pdmodel.interactive.form.PDTextField.constructAppearances(PDTextField.java:264) at org.apache.pdfbox.pdmodel.interactive.form.PDTerminalField.applyChange(PDTerminalField.java:227) at org.apache.pdfbox.pdmodel.interactive.form.PDTextField.setValue(PDTextField.java:219) at TEST_PDFBOX.testMSMincho(TEST_PDFBOX.java:123) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:768) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
相关代码示例
PDDocument pdDoc; //For ver3.0.1 //pdDoc = Loader.loadPDF(new File(pdffile)); //For ver2.x.x pdDoc = PDDocument.load(new File(pdffile)); //TsukuhouMincho is free Japanese font. If I set true to embedSubset argument, it generates error. PDFont font = PDType0Font.load(pdDoc, PDFBOXTester.class.getResourceAsStream("/font/TsukuhouMincho-Regular.ttf"), false); PDDocumentCatalog pdCatalog = pdDoc.getDocumentCatalog(); PDAcroForm pdAcroForm = pdCatalog.getAcroForm(); PDTextField textfield = (PDTextField) pdAcroForm.getField("Text_TaxOffice_JP"); PDResources res = pdAcroForm.getDefaultResources(); String fontName = res.add(font).getName(); textfield.setDefaultAppearance("/" + fontName + " 0 Tf 0 g"); textfield.setValue(someJapanaeseStrings); pdDoc.save(new File(args[1]));
解决方案
1. 预编码需要使用的字符
在加载字体并开启子集嵌入后,手动编码所有需要用到的日文字符,确保字体子集包含对应的字形:
// 开启子集嵌入加载字体 PDFont font = PDType0Font.load(pdDoc, PDFBOXTester.class.getResourceAsStream("/font/TsukuhouMincho-Regular.ttf"), true); // 预编码所有需要使用的字符 for (char c : someJapanaeseStrings.toCharArray()) { font.encode(String.valueOf(c)); } // 后续表单字段设置逻辑不变 PDDocumentCatalog pdCatalog = pdDoc.getDocumentCatalog(); PDAcroForm pdAcroForm = pdCatalog.getAcroForm(); PDTextField textfield = (PDTextField) pdAcroForm.getField("Text_TaxOffice_JP"); PDResources res = pdAcroForm.getDefaultResources(); String fontName = res.add(font).getName(); textfield.setDefaultAppearance("/" + fontName + " 0 Tf 0 g"); textfield.setValue(someJapanaeseStrings); pdDoc.save(new File(args[1]));
原理:PDFBOX默认可能不会自动收集表单字段中使用的字符到子集,提前编码这些字符可以强制字体子集包含对应的字形数据。
2. 验证字体文件完整性
确认使用的TsukuhouMincho-Regular.ttf字体文件本身包含所需的日文字形(如U+65E5),部分精简版字体可能存在字形缺失,建议下载官方完整版字体文件测试。
3. 手动生成表单外观
禁用PDFBOX自动生成表单外观的逻辑,手动绘制文本外观,更精确地控制字体子集的嵌入:
// 开启子集嵌入加载字体 PDFont font = PDType0Font.load(pdDoc, PDFBOXTester.class.getResourceAsStream("/font/TsukuhouMincho-Regular.ttf"), true); PDDocumentCatalog pdCatalog = pdDoc.getDocumentCatalog(); PDAcroForm pdAcroForm = pdCatalog.getAcroForm(); PDTextField textfield = (PDTextField) pdAcroForm.getField("Text_TaxOffice_JP"); PDResources res = pdAcroForm.getDefaultResources(); String fontName = res.add(font).getName(); textfield.setDefaultAppearance("/" + fontName + " 12 Tf 0 g"); // 设置字体大小 textfield.setValue(someJapanaeseStrings); // 禁用自动外观生成 textfield.setNeedAppearances(false); // 手动创建外观流 PDAppearanceDictionary appearance = new PDAppearanceDictionary(); PDAppearanceEntry appearanceEntry = new PDAppearanceEntry(); PDFormXObject form = new PDFormXObject(pdDoc); PDRectangle rect = textfield.getWidget().getRectangle(); form.setBBox(new PDRectangle(rect.getWidth(), rect.getHeight())); PDPageContentStream contentStream = new PDPageContentStream(pdDoc, form); contentStream.setFont(font, 12); contentStream.beginText(); // 设置文本起始位置(可根据需求调整) contentStream.newLineAtOffset(5, rect.getHeight() - 15); contentStream.showText(someJapanaeseStrings); contentStream.endText(); contentStream.close(); appearanceEntry.setNormalAppearance(form); appearance.setNormalAppearance(appearanceEntry); textfield.getWidget().setAppearance(appearance); pdDoc.save(new File(args[1]));
原理:自动生成外观时,PDFBOX可能在子集嵌入逻辑上存在冲突,手动绘制外观可以绕过该问题,同时确保只嵌入使用到的字符。
内容的提问来源于stack exchange,提问作者K M
相关产品推荐
相关产品推荐

