普通网友 2025-11-16 21:45 采纳率: 98.8%
浏览 4
已采纳

Spring Boot加水印中文乱码如何解决?

在使用Spring Boot集成iText或Apache PDFBox为PDF文件添加中文水印时,常出现中文乱码问题。主要原因是默认字体不支持中文字符,导致文本显示为方框或问号。如何正确加载并嵌入中文字体(如SimSun、微软雅黑),确保水印中的中文正常渲染,是开发中常见的技术难题。尤其在Linux服务器环境下,系统缺失中文字体文件时问题更为突出。
  • 写回答

2条回答 默认 最新

  • 玛勒隔壁的老王 2025-11-16 21:47
    关注

    Spring Boot集成iText与PDFBox实现中文水印:从乱码到完美渲染的深度解析

    1. 问题背景与常见现象

    在使用Spring Boot构建文档处理服务时,常需为PDF文件添加水印。当水印内容包含中文字符(如“机密”、“内部资料”)时,开发者普遍遇到中文显示为方框、问号或完全缺失的问题。

    • iText默认字体不支持中文,如BaseFont.HELVETICA
    • PDFBox未显式加载中文字体时,系统回退至无中文支持的替代字体
    • Linux服务器通常缺少Windows自带的SimSun、微软雅黑等字体文件
    • 字体未嵌入PDF,导致跨平台查看时出现渲染异常

    2. 核心原因分析

    PDF标准要求所有文本必须绑定具体字体。若字体未正确加载或未嵌入,则依赖目标设备的字体库。以下是导致中文乱码的关键因素:

    技术栈默认字体是否支持中文典型错误表现
    iText 5/7Helvetica□□□ 或
    Apache PDFBoxStandard 14 Fonts空白或占位符
    Linux环境系统字体路径缺失部分支持客户端正常,服务端异常

    3. 解决方案总览

    解决中文乱码的核心在于:显式加载并嵌入支持中文的TrueType字体(TTF),确保PDF文件自包含所需字形数据。主要路径如下:

    1. 获取合法授权的中文字体文件(如simsun.ttc、msyh.ttf)
    2. 在项目资源目录中存放字体文件
    3. 通过代码动态加载字体并注册到PDF生成引擎
    4. 设置文本渲染时使用该字体,并启用字体嵌入
    5. 在生产环境中验证跨平台一致性

    4. iText 实现中文水印(以iText 7为例)

    以下为Spring Boot中使用iText 7添加中文水印的完整示例:

    
    @Autowired
    private ResourceLoader resourceLoader;
    
    public void addChineseWatermark(String src, String dest) throws IOException {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
        Document document = new Document(pdfDoc);
    
        // 加载宋体字体(支持中文)
        Resource fontResource = resourceLoader.getResource("classpath:fonts/simsun.ttc");
        byte[] fontBytes = StreamUtils.copyToByteArray(fontResource.getInputStream());
        PdfFont font = PdfFontFactory.createFont(fontBytes, PdfEncodings.IDENTITY_H, true); // 开启嵌入
    
        for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {
            PdfPage page = pdfDoc.getPage(i);
            PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
            
            canvas.beginText();
            canvas.setFontAndSize(font, 40);
            canvas.setFillColor(ColorConstants.LIGHT_GRAY);
            canvas.showTextAligned("内部文档 禁止外传", 
                page.getPageSize().getWidth() / 2, 
                page.getPageSize().getHeight() / 2, 
                i, TextAlignment.CENTER, VerticalAlignment.MIDDLE, (float) Math.toRadians(30));
            canvas.endText();
        }
    
        document.close();
    }
        

    5. Apache PDFBox 实现中文水印

    PDFBox需手动注册字体并创建字体对象:

    
    public void addWatermarkWithPDFBox(String inputPath, String outputPath) throws IOException {
        PDDocument doc = PDDocument.load(new File(inputPath));
        PDType0Font font = PDType0Font.load(doc, resourceLoader.getResource("classpath:fonts/msyh.ttf").getInputStream(), true); // 嵌入字体
    
        for (PDPage page : doc.getPages()) {
            PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.PREPEND, true, true);
            cs.beginText();
            cs.setFont(font, 36);
            cs.setNonStrokingColor(200, 200, 200);
            cs.newLineAtOffset(200, 400);
            cs.showText("机密文件");
            cs.endText();
            cs.close();
        }
    
        doc.save(outputPath);
        doc.close();
    }
        

    6. 字体文件管理策略

    为保障多环境一致性,建议采用以下字体管理方式:

    • 将字体文件置于src/main/resources/fonts/目录下
    • 使用ResourceLoader加载,避免硬编码路径
    • 优先选择开源或已获商业授权的字体(如Noto Sans CJK SC)
    • 禁止直接引用系统路径(如C:\\Windows\\Fonts\\...),因Linux不存在该路径

    7. 生产环境部署注意事项

    在Kubernetes或Docker容器中运行时,需特别注意:

    检查项推荐做法
    字体文件打包确认JAR/WAR中包含fonts目录
    Docker镜像COPY resources/fonts 到镜像内
    权限控制确保应用对字体流有读取权限
    性能优化缓存PdfFont实例,避免重复加载

    8. 可视化流程图:中文水印生成流程

    graph TD A[开始] --> B{PDF源文件存在?} B -- 是 --> C[加载中文字体(TTF)] B -- 否 --> Z[抛出异常] C --> D[创建PdfDocument/PDDocument] D --> E[遍历每一页] E --> F[创建ContentStream] F --> G[设置字体+大小+颜色] G --> H[绘制旋转中文文本] H --> I[关闭ContentStream] I --> J{是否最后一页?} J -- 否 --> E J -- 是 --> K[保存输出文件] K --> L[结束]

    9. 高级技巧与最佳实践

    提升稳定性和可维护性的进阶方法:

    • 封装字体工厂类,统一管理PdfFont实例池
    • 使用@PostConstruct预加载常用字体,减少运行时开销
    • 添加字体健康检查接口,用于运维诊断
    • 对用户上传的PDF进行字体子集嵌入,控制文件体积
    • 结合AOP记录水印操作日志,满足审计需求

    10. 跨平台兼容性测试建议

    确保在不同操作系统和阅读器中表现一致:

    1. 在Windows开发机上生成PDF
    2. 上传至Linux服务器重新生成对比
    3. 使用Adobe Acrobat、Foxit、浏览器内置PDF查看器分别打开
    4. 检查手机端微信/QQ内的PDF预览效果
    5. 验证打印输出是否保留水印
    6. 测试超大中文文本(如万字报告)的性能影响
    7. 模拟断网环境下打开PDF,确认字体仍可显示
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 已采纳回答 11月17日
  • 创建了问题 11月16日