XDocReport生成PDF时西里尔文字符间距异常问题求助
解决DOCX转PDF时西里尔字符粘连、间距异常问题
将含变量的DOCX模板转换为PDF时,英文字符显示正常,但西里尔文字符出现粘连、字符间距异常。尝试修改pdfWriter.setSpaceCharRatio未生效,DOCX模板本身格式正常。相关代码、调用方式及依赖配置如下:
原服务端方法
public static void generateReport(InputStream docxInputStream, OutputStream pdfOutputStream) throws Exception { IXDocReport report = XDocReportRegistry.getRegistry().loadReport(docxInputStream, TemplateEngineKind.Freemarker); IContext context = report.createContext(); Options options = Options.getFrom(DocumentKind.DOCX).via(ConverterTypeVia.XWPF).to(ConverterTypeTo.PDF); PdfOptions pdfOptions = PdfOptions.create() .fontEncoding("windows-1251") .fontProvider((s, s1, v, i, color) -> FontFactory.getFont(FontFactory.TIMES_ROMAN, "windows-1251", v, i, color)); options.subOptions(pdfOptions); report.convert(context, options, pdfOutputStream); }
原调用代码
FileInputStream docxInputStream = new FileInputStream("/abspath/resources/template.docx"); FileOutputStream pdfOutputStream = new FileOutputStream("/abspath/resources/output.pdf"); ReportGenerator.generateReport(docxInputStream, pdfOutputStream); docxInputStream.close(); pdfOutputStream.close();
原Maven依赖
<dependency> <groupId>fr.opensagres.xdocreport</groupId> <artifactId>xdocreport</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.32</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>2.1.7</version> </dependency> <dependency> <groupId>fr.opensagres.xdocreport</groupId> <artifactId>fr.opensagres.poi.xwpf.converter.pdf</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.0.1</version> </dependency>
解决方法
问题核心是当前使用的TIMES_ROMAN字体不完整支持西里尔字符,导致渲染时字符布局异常。可通过以下步骤修复:
1. 替换为支持西里尔文的字体
选择内置支持西里尔字符的字体,比如Times New Roman(需确保系统中存在该字体),或使用开源字体如DejaVu Serif。修改字体提供者配置:
- 依赖系统字体的方式:
.fontProvider((s, s1, v, i, color) -> FontFactory.getFont("Times New Roman", "UTF-8", v, i, color)) - 嵌入字体文件(避免依赖系统环境):
将字体文件(如DejaVuSerif.ttf)放入项目resources目录,加载逻辑如下:.fontProvider((s, s1, v, i, color) -> { InputStream fontStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("DejaVuSerif.ttf"); BaseFont baseFont = BaseFont.createFont("DejaVuSerif.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); return new Font(baseFont, v, i, color); })
2. 调整编码为UTF-8
DOCX文件本身采用UTF-8编码存储,将fontEncoding从windows-1251改为UTF-8,避免编码不匹配导致的字符解析错误:
PdfOptions pdfOptions = PdfOptions.create() .fontEncoding("UTF-8") .fontProvider(...); // 上述修改后的字体提供者
3. 确保字体正确嵌入
使用BaseFont.EMBEDDED参数嵌入字体,保证PDF在无对应字体的环境下也能正常显示西里尔字符。
修改后的完整代码
public static void generateReport(InputStream docxInputStream, OutputStream pdfOutputStream) throws Exception { IXDocReport report = XDocReportRegistry.getRegistry().loadReport(docxInputStream, TemplateEngineKind.Freemarker); IContext context = report.createContext(); Options options = Options.getFrom(DocumentKind.DOCX).via(ConverterTypeVia.XWPF).to(ConverterTypeTo.PDF); PdfOptions pdfOptions = PdfOptions.create() .fontEncoding("UTF-8") .fontProvider((s, s1, v, i, color) -> { InputStream fontStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("DejaVuSerif.ttf"); BaseFont baseFont = BaseFont.createFont("DejaVuSerif.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); return new Font(baseFont, v, i, color); }); options.subOptions(pdfOptions); report.convert(context, options, pdfOutputStream); }
额外说明
若使用DejaVu字体,可通过Maven添加依赖(无需手动下载字体文件):
<dependency> <groupId>com.github.librepdf</groupId> <artifactId>openpdf-fonts</artifactId> <version>1.3.30</version> </dependency>
内容的提问来源于stack exchange,提问作者Яков Клюев
相关产品推荐
相关产品推荐

