项目场景:
在我的项目里有一个将数据内容打印的功能(当然包括打印为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自带了字体文件。我便试试。
方案二
- 在
C:\Windows\Fonts
该路径下去找一个自己喜欢或符合要求的字体; - 将字体复制到自己的项目中(其实这一步在测试阶段不需要,直接用字体绝对路径即可,如
C:\Users\victor\AppData\Local\Microsoft\Windows\Fonts\DFKai-SB.ttf
) - 在代码中创建新的字体
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不能显示中文的问题和自定义页眉页脚页码的方案。