解决itext导出pdf时中文不显示问题及页眉页脚页码问题

项目场景:

在我的项目里有一个将数据内容打印的功能(当然包括打印为pdf文件)。首先我直接前端调用window.print()进行打印,但它无法满足我对页眉、页脚、页码的设置,而且样式调整也比较麻烦。索性缓存后端直接生成一个PDF,再将PDF路径返回给前端。这样就可以满足我的需求了。


问题描述

在上述场景下,我遇到一个中文不显示的问题。

原因分析:

itext对中文的支持不友好,因此需要自己去创建一个中文字体。


解决方案:

方案一

如果对字体没啥要求,其实很简单,直接通过下面的代码就能搞定。

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;

import java.io.FileOutputStream;

public class ChinesePdfGenerator {

    public static void main(String[] args) {
        String outputPath = "path/to/chinese_output.pdf";

        try {
            generateChinesePdf(outputPath);
            System.out.println("PDF with Chinese characters generated successfully.");
        } catch (Exception e) {
            System.err.println("Error generating PDF: " + e.getMessage());
        }
    }

    public static void generateChinesePdf(String outputPath) throws Exception {
        // 创建 Document
        Document document = new Document();

        // 创建 PdfWriter
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputPath));

        // 设置可以显示中文的字体STSong-Light
        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Font font = new Font(baseFont, 12, Font.NORMAL, BaseColor.BLACK);

        // 打开文档并添加内容
        document.open();

        // 添加包含中文的内容
        Paragraph paragraph = new Paragraph("你好,世界!", font);
        document.add(paragraph);

        // 关闭文档
        document.close();
    }
}

这字体上来直接对文字加粗(不是真正意义上的加粗,是看起来加粗了),对于我来说,我觉得这字体有点丑,黑呼呼的一团,还看不清字。
在这里插入图片描述
于是乎,我就去找一些其他字体,一开始我没想到Windows系统自带很多字体文件。我就去网上搜,在GitHub上找到了一些,但是试了很多字体,发现都报错,说是“不是有效的资源”。最终在这里看到Windows自带了字体文件。我便试试。

方案二

  1. C:\Windows\Fonts该路径下去找一个自己喜欢或符合要求的字体;
  2. 将字体复制到自己的项目中(其实这一步在测试阶段不需要,直接用字体绝对路径即可,如C:\Users\victor\AppData\Local\Microsoft\Windows\Fonts\DFKai-SB.ttf
  3. 在代码中创建新的字体
package com.mieasy.chilin.es.assess.system.java2pdf;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.FileOutputStream;
import java.io.IOException;

public class PdfGenerator {
	// 保存路径
    public static final String DEST = "C:\\Users\\victor\\Desktop\\simple_table.pdf";
    // 字体文件绝对路径
    public static final String FONT = "D:\\dev\\assess\\entrance\\src\\main\\resources\\static\\font\\DFKai-SB.ttf";

    public static BaseFont bfChinese;

    static {
        try {
        	// 这个方案是项目启动时加载字体用的
            Resource resource = new ClassPathResource("static/font/DFKai-SB.ttf");
            if (resource.exists()) {
                bfChinese = BaseFont.createFont(resource.getURL().getPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            } else {
            	// 如果是本地测试,会直接加载绝对路径的字体文件
                bfChinese = BaseFont.createFont(FONT,
                        BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            }
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void generatePdfWithCustomList(String outputPath) throws IOException, DocumentException {
        Document document = new Document();
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputPath));
        // 添加页事件处理器(这是我对页眉页脚设置的方案,可以不要,我下面也会把这个类的代码加上)
        HeaderFooterPageEvent event = new HeaderFooterPageEvent("页眉左", 2);
        writer.setPageEvent(event);

        // 打开文档,添加内容
        document.open();

        // 一级标题字体
        Font firstTitleFont = new Font(bfChinese, 20, Font.BOLD, BaseColor.BLACK);
        // 添加标题 - 居中
        Paragraph title = new Paragraph(new Chunk("院友意外報告", firstTitleFont));
        title.setAlignment(Element.ALIGN_CENTER);

        document.add(title);

        // 关闭文档
        document.close();
    }

    public static void main(String[] args) {
        try {
            generatePdfWithCustomList(DEST);
            System.out.println("PDF with underline in cell generated successfully.");
        } catch (Exception e) {
            System.err.println("Error generating PDF: " + e.getMessage());
        }
    }
}

在这里插入图片描述
这个字体就好看多了。

补充

如果对页眉页脚的设定有需要,我也附上处理方案。

package com.mieasy.chilin.es.assess.system.java2pdf;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;
import com.mieasy.chilin.es.assess.common.tools.datetime.DateUtil;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;

public class HeaderFooterPageEvent extends PdfPageEventHelper {
    private String leftHeader=""; // 页眉左
    private String centerHeader=""; // 页眉中
    private String rightHeader="列印時間:"+ DateUtil.dateTimeNow("dd/MM/yyyy HH:mm"); // 页眉右
    private String leftFooter=""; // 页脚左
    private String centerFooter=""; // 页脚中
    private String rightFooter=""; // 页脚右
    private int pages=1;// 总页数

    public static final String FONT = "D:\\dev\\assess\\entrance\\src\\main\\resources\\static\\font\\DFKai-SB.ttf";
    private static BaseFont bfChinese;
    static {
        try {
            Resource resource = new ClassPathResource("static/font/DFKai-SB.ttf");
            if (resource.exists()) {
                bfChinese = BaseFont.createFont(resource.getURL().getPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            } else {
                bfChinese = BaseFont.createFont(FONT,
                        BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            }

        } catch (DocumentException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public HeaderFooterPageEvent(String leftHeader, int pages) {
        this.leftHeader = leftHeader;
        this.pages = pages;
    }

    public HeaderFooterPageEvent(String leftHeader, String centerHeader, String rightHeader,
                                 String leftFooter, String centerFooter, String rightFooter) {
        this.leftHeader = leftHeader;
        this.centerHeader = centerHeader;
        this.rightHeader = rightHeader;
        this.leftFooter = leftFooter;
        this.centerFooter = centerFooter;
        this.rightFooter = rightFooter;
    }

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        PdfContentByte cb = writer.getDirectContent();

        // 页眉
        cb.beginText();
        cb.setFontAndSize(bfChinese, 12);
        cb.showTextAligned(Element.ALIGN_LEFT, leftHeader, document.left(), document.top() + 10, 0);
        cb.showTextAligned(Element.ALIGN_CENTER, centerHeader, (document.left() + document.right()) / 2, document.top() + 10, 0);
        cb.showTextAligned(Element.ALIGN_RIGHT, rightHeader, document.right(), document.top() + 10, 0);
        cb.endText();

        // 页脚
        cb.beginText();
        try {
            cb.setFontAndSize(BaseFont.createFont(), 12);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        cb.showTextAligned(Element.ALIGN_LEFT, leftFooter, document.left(), document.bottom() - 20, 0);
        cb.showTextAligned(Element.ALIGN_CENTER, centerFooter, (document.left() + document.right()) / 2, document.bottom() - 20, 0);
        // 页脚右侧写入页码
        Phrase pageNum = new Phrase(writer.getPageNumber() + "/" + pages);
        cb.showTextAligned(Element.ALIGN_RIGHT, pageNum.getContent(), document.right(), document.bottom() - 20, 0);
        cb.endText();
    }
}

小结一下

以上内容可以解决itext不能显示中文的问题自定义页眉页脚页码的方案

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值