SpringBoot 生成带水印pdf | 中文字体问题

概述

生成pdf

中文字体问题

一般生成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包所在目录。


生成pdf工具类

maven 依赖:

<!--pdf生成 itext-->
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>itextpdf</artifactId>
			<version>5.4.2</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf.tool</groupId>
			<artifactId>xmlworker</artifactId>
			<version>5.4.1</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>itext-asian</artifactId>
			<version>5.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.xhtmlrenderer</groupId>
			<artifactId>flying-saucer-pdf</artifactId>
			<version>9.0.3</version>
		</dependency>
/**
 * 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;
    }


}

ftl模板

模板用来生成html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta http-equiv="Content-Style-Type" content="text/css"/>
    <title></title>
    <style type="text/css">
        body {
            font-family: pingfang sc light;
        }
        .center{
            text-align: center;
            width: 100%;
        }
    </style>
</head>
<body>
<div class="page" >
    <div class="center"><p>核保咨询保全信息确认函</p></div>
    <div><p>保单号:${policyNo}</p></div>
    <div><p>批单类型:${busType}</p></div>
    <div><p>是否同意:${customerDecision}</p></div>
    <div><p>生效日期:${effectiveDate}</p></div>
    <div><p>二核人员:${(lockId)!}</p></div>
    <div><p>是否退费:${(isRefund)!}</p></div>
    <div><p>退费金额:${(refundAmount)!}</p></div>
</div>
</body>
</html>
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值