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

