SpringBoot 生成pdf 中文字體問題

概述

生成pdf

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){

        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);
        }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){
        //獲取字體路徑
        document.open();
        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();
        }

    }
    /**
     * @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() {


        FontUtil fontUtil = new FontUtil();
        fontUtil.createFtlFileByFtlArray();

        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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章