You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iText生成PDF时中文不显示问题求助

问题描述

我的数据库采用UTF-8编码存储中文,将数据读取为字符串传入iText单元格时,中文无法显示。我尝试了testEncoding方法将字符串转为Unicode,但PDF中显示的是\u6b98\u528d这类编码而非中文;但直接定义String text="\u6b98\u528d"传入单元格却能正常显示。我已使用NotoSansCJKsc-Regular.otf字体,相关代码如下,请问我哪里操作有误?

自定义转码方法

public String testEncoding(String str) { 
    String result = ""; 
    for(char ch : str.toCharArray()) 
        result += "\\u" + Integer.toHexString(ch | 0x10000).substring(1); 
    return result; 
}

字体加载与单元格创建代码

final String PATH_FONT_ARIALUNI = "src/main/resources/fonts/NotoSansCJKsc-Regular.otf"; // FOR Chinese
BaseFont baseFont = null;
try {
    baseFont = BaseFont.createFont(PATH_FONT_ARIALUNI, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
} catch (DocumentException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
Font font = new Font(baseFont, 6.8f);
Font fontNormal = new Font(FontFamily.HELVETICA, 10, Font.NORMAL, BaseColor.RED);

char[] aa = title.toCharArray();
boolean isLastChineseChar = false;
for (int j = 0; j < title.length(); j++) {
    if((Character.UnicodeBlock.of(aa[j]) == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS)){
        isLastChineseChar = true;
    }
}

Phrase phrase = null;
if(isLastChineseChar){
    phrase = new Phrase(title, font); // 也曾传入testEncoding(title),但PDF显示的是Unicode编码而非中文
    PdfPCell cell = new PdfPCell(phrase);
    cell.setVerticalAlignment(Element.ALIGN_CENTER);
    if (align == 2) {
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
    }
    return cell;
} else {
    phrase = new Phrase(title, fontNormal);
    PdfPCell cell = new PdfPCell(phrase);
    cell.setVerticalAlignment(Element.ALIGN_CENTER);
    if (align == 2) {
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
    }
    return cell;
}

问题分析与解决办法

1. 核心错误:自定义转码方法完全没必要,反而破坏了字符串

你写的testEncoding方法犯了一个关键错误:它把每个中文字符转换成了字面量的Unicode转义字符串(比如把“前”变成了字符串"\u524d")。而Java代码里直接写"\u524d"时,是编译器在编译阶段就把这个转义序列解析成了对应的中文字符;但你通过方法生成的"\u524d"是一个普通字符串,包含的是\、u、5、2、4、d这几个字符,所以PDF里自然会显示这些字符,而不是中文。

解决:直接删除testEncoding方法,用从数据库读取的原始字符串即可——Java的String本身就是基于Unicode编码的,只要你从数据库读取时用UTF-8正确解码,字符串里的中文就是正常的,不需要任何额外转码。

2. 字体加载的潜在问题

你的字体路径"src/main/resources/fonts/NotoSansCJKsc-Regular.otf"是开发时的路径,当项目打包运行后,这个路径会失效(因为src/main/resources下的文件会被放到classpath根目录)。建议改用classpath方式加载字体,避免路径问题:

// 改用类加载器获取字体输入流,更可靠
InputStream fontStream = getClass().getResourceAsStream("/fonts/NotoSansCJKsc-Regular.otf");
if (fontStream == null) {
    throw new RuntimeException("找不到字体文件");
}
baseFont = BaseFont.createFont(
    "NotoSansCJKsc-Regular.otf", 
    BaseFont.IDENTITY_H, 
    BaseFont.EMBEDDED, 
    true, 
    fontStream, 
    null
);

另外,确保你的NotoSansCJKsc-Regular.otf确实包含需要显示的中文字符集,避免字体本身缺失字符。

3. 数据库读取的编码验证

确认从数据库读取数据时,JDBC连接参数已经指定了UTF-8编码,比如MySQL的连接URL要加上:

useUnicode=true&characterEncoding=UTF-8

这样才能保证数据库返回的字节流被正确解码成Java字符串。

修正后的核心代码示例

// 字体加载(修正版)
BaseFont baseFont = null;
try {
    InputStream fontStream = getClass().getResourceAsStream("/fonts/NotoSansCJKsc-Regular.otf");
    baseFont = BaseFont.createFont(
        "NotoSansCJKsc-Regular.otf", 
        BaseFont.IDENTITY_H, 
        BaseFont.EMBEDDED, 
        true, 
        fontStream, 
        null
    );
} catch (Exception e) {
    e.printStackTrace();
}
Font chineseFont = new Font(baseFont, 6.8f);
Font defaultFont = new Font(FontFamily.HELVETICA, 10, Font.NORMAL, BaseColor.RED);

// 判断是否包含中文(逻辑保留,但不需要转码)
boolean hasChinese = false;
for (char ch : title.toCharArray()) {
    if (Character.UnicodeBlock.of(ch) == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS) {
        hasChinese = true;
        break; // 找到一个中文就可以停止循环,优化性能
    }
}

// 创建单元格
PdfPCell cell = new PdfPCell(new Phrase(title, hasChinese ? chineseFont : defaultFont));
cell.setVerticalAlignment(Element.ALIGN_CENTER);
if (align == 2) {
    cell.setHorizontalAlignment(Element.ALIGN_CENTER);
}
return cell;

内容的提问来源于stack exchange,提问作者Daniel B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:10:30