如何用PDFBox 2.x嵌入Helvetica字体且不修改PDF文本内容?
如何用PDFBox 2.x为PDF嵌入未嵌入的Helvetica字体
当然可行!不过你之前的代码存在编码不匹配的问题,这就是替换后内容空白的核心原因。咱们一步步拆解问题,然后给出正确的解决方案。
问题根源
你的PDF原本使用的是未嵌入的Type1格式Helvetica,它采用的是StandardEncoding编码。但你替换成了CID TrueType格式的Helvetica(通过PDType0Font加载),这种字体用的是Unicode/CID编码体系——两者的字符映射规则完全不同,直接替换后,原文本的字节在新字体里找不到对应字符,自然就显示空白了。
另外,你最初的代码只是把新字体添加到资源中,没有替换掉原有的Helvetica引用,这也会导致字体并没有真正被使用。
解决方案
推荐两种方案,优先选第一种,因为它更简单且不会引入编码问题:
方案1:用Type1格式Helvetica替换(最稳妥)
直接用同格式的Type1 Helvetica字体文件(通常是.pfb或.pfa后缀)来替换原字体,编码完全匹配,不需要修改文本内容:
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.cos.COSName; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class EmbedHelveticaType1 { public static void main(String[] args) throws Exception { // 加载目标PDF PDDocument document = PDDocument.load(new File("your-input.pdf")); // 加载Type1格式的Helvetica字体文件(替换成你的文件路径) InputStream fontStream = new FileInputStream(new File("/path/to/Helvetica.pfb")); PDType1Font embeddedHelvetica = PDType1Font.load(document, fontStream); // 遍历所有页面,替换Helvetica字体引用 for (PDPage page : document.getPages()) { PDResources resources = page.getResources(); for (COSName fontName : resources.getFontNames()) { PDFont existingFont = resources.getFont(fontName); // 匹配原字体名称 if ("Helvetica".equals(existingFont.getName())) { resources.put(fontName, embeddedHelvetica); } } } // 保存嵌入后的PDF document.save("output-embedded.pdf"); document.close(); fontStream.close(); } }
方案2:用TrueType Helvetica替换(需处理编码转换)
如果你只有TrueType格式的Helvetica(.ttf),就必须手动把原文本的StandardEncoding编码转换成Unicode,再适配新字体的编码。这个过程需要解析页面内容流,修改文本字节:
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.font.*; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSString; import org.apache.pdfbox.pdfparser.PDFStreamParser; import org.apache.pdfbox.pdmodel.common.PDStream; import org.apache.pdfbox.contentstream.ContentStreamWriter; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.List; public class EmbedHelveticaTTF { public static void main(String[] args) throws Exception { PDDocument document = PDDocument.load(new File("your-input.pdf")); // 加载TrueType Helvetica字体,设置为嵌入 InputStream fontStream = new FileInputStream(new File("/path/to/Helvetica.ttf")); PDType0Font embeddedHelvetica = PDType0Font.load(document, fontStream, true); // 初始化StandardEncoding映射,用于转换原文本编码 StandardEncoding standardEncoding = new StandardEncoding(); for (PDPage page : document.getPages()) { PDResources resources = page.getResources(); // 先替换资源中的Helvetica引用 for (COSName fontName : resources.getFontNames()) { PDFont existingFont = resources.getFont(fontName); if ("Helvetica".equals(existingFont.getName())) { resources.put(fontName, embeddedHelvetica); } } // 解析并修改页面内容流的文本编码 PDStream contentStream = page.getContents(); if (contentStream != null) { try (InputStream is = contentStream.createInputStream()) { PDFStreamParser parser = new PDFStreamParser(is); parser.parse(); List<Object> tokens = parser.getTokens(); for (int i = 0; i < tokens.size(); i++) { Object token = tokens.get(i); if (token instanceof COSString) { COSString cosString = (COSString) token; byte[] originalBytes = cosString.getBytes(); // 将StandardEncoding字节转换为Unicode字符串 StringBuilder unicodeText = new StringBuilder(); for (byte b : originalBytes) { int code = b & 0xFF; unicodeText.append(standardEncoding.getChar(code)); } // 将Unicode字符串转换为新字体支持的编码字节 byte[] newBytes = embeddedHelvetica.encode(unicodeText.toString()); cosString.setValue(newBytes); } } // 更新页面内容流 PDStream newContentStream = new PDStream(document); try (OutputStream os = newContentStream.createOutputStream(COSName.FLATE_DECODE)) { ContentStreamWriter writer = new ContentStreamWriter(os); writer.writeTokens(tokens); } page.setContents(newContentStream); } } } document.save("output-embedded-ttf.pdf"); document.close(); fontStream.close(); } }
注意事项
- 请确保你使用的Helvetica字体是合法授权的,避免版权纠纷。
- 方案1的Type1字体替换几乎不会出问题,因为和原字体格式、编码完全一致,是优先选择的方案。
- 如果用方案2,要注意
StandardEncoding的映射覆盖范围,某些特殊字符可能需要额外处理。
内容的提问来源于stack exchange,提问作者jeffest
相关产品推荐
相关产品推荐

