一般生成pdf引用中文字体有以下几种方式:
方法一:使用Windows系统字体(TrueType)
方法二:使用iTextAsian.jar中的字体
方法三:使用资源字体(ClassPath)
理论上使用方式三是最好的,但是实际使用过程中,itext读取字体的方式是接受的目录,不是实际的文件。所以当使用SpringBoot部署方式以jar包方式运行,在获取字体的时候获取不到,因为在服务器上读取的字体路径为jar包中的路径:
file:/root/startup/za-minos-ms.jar!/BOOT-INF/classes!/
如上图,这种路径是无法正确读取文件的,所以合理的解决方案是通过流读取,然后在服务器上创建新的文件。也就是最终使用的是方法一字体引用方式。
读取和创建新文件的代码:
/**
* Created by zhangshukang on 2019/7/25.
*/
@Slf4j
@Component
public class FontUtil {
public static String[] fontNames = {"ping_fang_bold.ttf", "ping_fang_light.ttf", "ping_fang_regular.ttf", "SIMLI.TTF"};
public static String fontPath = "biz/pdf/fonts/";
public static String newFontPath = "resources";
private static String sourceTemplatePath;
static {
//静态方法调用一次
sourceTemplatePath = createFtlFileByFtlArray();
}
public static String createFtlFileByFtlArray() {
String path = "";
for (int i = 0; i < fontNames.length; i++) {
path = createNewFile(fontPath, fontNames[i]);
if (null == path) {
log.info("ftl not copy success:" + fontNames[i]);
}
}
return path;
}
private static String createNewFile(String fontPath, String ftlName) {
try {
String systemRootPath = System.getProperty("user.dir");
log.info("project run path:" + systemRootPath);
//获取模板下的路径
String newFilePath = systemRootPath + File.separator + newFontPath+File.separator;
newFilePath = newFilePath.replace("/", File.separator);
log.info("newFilePath:" + newFilePath);
File newFile = new File(newFilePath + ftlName);
if (newFile.isFile() && newFile.exists()) {
return newFilePath;
}
InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fontPath + ftlName);
byte[] certData = IOUtils.toByteArray(certStream);
FileUtils.writeByteArrayToFile(newFile, certData);
return newFilePath;
} catch (IOException e) {
log.error("复制new文件失败--> 异常信息:" + e);
}
return null;
}
}
在项目启动的时候会将字体文件通过流读取,生成新的文件,目录为jar包所在目录。
maven 依赖:
com.itextpdf
itextpdf
5.4.2
com.itextpdf.tool
xmlworker
5.4.1
com.itextpdf
itext-asian
5.2.0
org.xhtmlrenderer
flying-saucer-pdf
9.0.3
/**
* Created by zsk on 2019/7/24.
*
*/
@Slf4j
public class PDFKit {
public ByteArrayOutputStream exportToFile(String htmlData,String waterMarkText){
ByteArrayOutputStream outputStream = null;
try{
outputStream = new ByteArrayOutputStream();
//设置文档大小
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
PDFBuilder builder = new PDFBuilder();
builder.setPresentFontSize(10);
writer.setPageEvent(builder);
//输出为PDF文件
convertToPDF(writer,document,htmlData,waterMarkText);
}catch(Exception ex){
throw new BusinessException("PDF export to File fail",ex);
}finally{
IOUtils.closeQuietly(outputStream);
}
return outputStream;
}
/**
* @description PDF文件生成
*/
private void convertToPDF(PdfWriter writer,Document document,String htmlString,String waterMarkText){
//获取字体路径
document.open();
//添加水印
buildDocumentWaterMark(writer, document, waterMarkText);
try {
String fontPath=getFontPath();
XMLWorkerHelper.getInstance().parseXHtml(writer,document,
new ByteArrayInputStream(htmlString.getBytes()),
XMLWorkerHelper.class.getResourceAsStream("/default.css"),
Charset.forName("UTF-8"),new XMLWorkerFontProvider(fontPath));
} catch (IOException e) {
e.printStackTrace();
throw new BusinessException("PDF文件生成异常",e);
}finally {
document.close();
}
}
private void buildDocumentWaterMark(PdfWriter writer,Document document,String waterMarkText){
if (StringUtils.isNotBlank(waterMarkText)) {
document.newPage();
// 加入水印
PdfContentByte waterMar = writer.getDirectContentUnder();
// 开始设置水印
waterMar.beginText();
// 设置水印透明度
PdfGState gs = new PdfGState();
// 设置填充字体不透明度为0.4f
gs.setFillOpacity(0.4f);
try {
// 设置水印字体参数及大小 (字体参数,字体编码格式,是否将字体信息嵌入到pdf中(一般不需要嵌入),字体大小)
try {
waterMar.setFontAndSize(BaseFont.createFont("/biz/pdf/fonts/SIMLI.TTF",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 36);
} catch (DocumentException e) {
e.printStackTrace();
}
// 设置透明度
waterMar.setGState(gs);
// 设置水印对齐方式 水印内容 X坐标 Y坐标 旋转角度
waterMar.showTextAligned(Element.ALIGN_RIGHT, waterMarkText , 450, 630, 45);
// 设置水印颜色
waterMar.setColorFill(BaseColor.GRAY);
//结束设置
waterMar.endText();
waterMar.stroke();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @description 创建默认保存路径
*/
private String getDefaultSavePath(String fileName){
String classpath=PDFKit.class.getClassLoader().getResource("").getPath();
String saveFilePath=classpath+"biz/pdf/"+fileName;
File f=new File(saveFilePath);
if(!f.getParentFile().exists()){
f.mkdirs();
}
return saveFilePath;
}
/**
* @description 获取字体设置路径
*/
public static String getFontPath() {
String fontPath=PDFKit.class.getResource("/").getPath()+ FontUtil.fontPath;
String systemName = System.getProperties().getProperty("os.name");
//如果为服务器环境,jar包方式运行,取不到resource下的资源
if (!systemName.startsWith("Windows") && !systemName.startsWith("Mac")) {
fontPath = System.getProperty("user.dir")+File.separator + FontUtil.newFontPath+File.separator;
}
return fontPath;
}
}
模板用来生成html:
核保咨询保全信息确认函
保单号:${policyNo}
批单类型:${busType}
是否同意:${customerDecision}
生效日期:${effectiveDate}
二核人员:${(lockId)!}
是否退费:${(isRefund)!}
退费金额:${(refundAmount)!}