PDFBox如何设置文本写入的备用字体?解决中文字符无字形报错
在PDFBox中配置备用字体解决无字形(Glyph)错误
这个问题我之前也碰到过!OpenSans这类西文字体根本没有中文字形,PDFBox遇到找不到的字符就会直接抛错,解决的核心思路就是自己实现字体 fallback 逻辑——把文本拆分成主字体能处理的片段和需要用备用字体的部分,分别绘制。下面是具体的实现步骤和代码:
1. 加载主字体和备用字体
首先你需要加载你的主西文字体(比如OpenSans-Regular),再加载一款支持中文的备用字体(比如Noto Sans SC、本地的黑体/宋体都可以),注意用PDType0Font加载,它支持多语言CID字体,是PDFBox推荐的方式:
// 加载主字体(确保字体文件路径正确) PDType0Font mainFont = PDType0Font.load(document, new File("OpenSans-Regular.ttf")); // 加载备用中文字体(这里用Noto Sans SC举例,替换成你有的字体文件) PDType0Font fallbackFont = PDType0Font.load(document, new File("NotoSansSC-Regular.ttf"));
2. 编写文本拆分工具方法
我们需要一个方法,把输入的文本拆分成「主字体可渲染」和「需要备用字体」的片段,每个片段对应合适的字体:
// 辅助类:存储文本片段和对应的字体 static class TextSegment { String text; PDType0Font font; TextSegment(String text, PDType0Font font) { this.text = text; this.font = font; } } // 拆分文本的核心方法 private List<TextSegment> splitTextByFont(String text, PDType0Font mainFont, PDType0Font fallbackFont) throws IOException { List<TextSegment> segments = new ArrayList<>(); StringBuilder currentSegment = new StringBuilder(); PDType0Font currentFont = null; for (char c : text.toCharArray()) { // 判断当前字符是否能被主字体渲染 PDType0Font suitableFont = mainFont.hasGlyph(c) ? mainFont : fallbackFont; if (currentFont == null) { currentFont = suitableFont; } else if (currentFont != suitableFont) { // 切换字体时,把当前片段加入列表,重置状态 segments.add(new TextSegment(currentSegment.toString(), currentFont)); currentSegment.setLength(0); currentFont = suitableFont; } currentSegment.append(c); } // 把最后一个片段加入列表 if (currentSegment.length() > 0) { segments.add(new TextSegment(currentSegment.toString(), currentFont)); } return segments; }
3. 分片段绘制文本
最后,我们遍历拆分后的文本片段,用对应的字体依次绘制,还要注意更新X坐标避免文本重叠:
float startX = 50; float startY = 700; float fontSize = 12; float currentXOffset = 0; // 拆分目标文本 List<TextSegment> textSegments = splitTextByFont("Hello 世界 笑!", mainFont, fallbackFont); contentStream.beginText(); // 初始位置 contentStream.newLineAtOffset(startX, startY); for (TextSegment segment : textSegments) { // 设置当前片段的字体 contentStream.setFont(segment.font, fontSize); // 绘制文本 contentStream.showText(segment.text); // 计算当前文本的宽度,更新偏移量 float textWidth = segment.font.getStringWidth(segment.text) * fontSize / 1000; currentXOffset += textWidth; contentStream.newLineAtOffset(textWidth, 0); } contentStream.endText();
注意事项
- 确保备用字体文件存在,并且支持你需要的字符集(比如如果还要支持日文,可以再加一款日文备用字体,扩展拆分逻辑即可)
PDType0Font会自动处理字体嵌入,避免在其他设备上无法显示的问题- 如果你的文本有换行需求,还需要在拆分时加入换行判断逻辑
内容的提问来源于stack exchange,提问作者V JDev
相关产品推荐
相关产品推荐

