Java生成pdf,兼富文本


Java生成pdf,兼容富文本內容

 

使用技術,freemark + jsoup + flying saucer 

使用freemark替換模板文件中指定的占位符,生成一個完整的的html字符串,

使用jsoup對html進行格式化,

使用flying saucer 將整個html進行pdf轉換(flying saucer對css的支持不是很完整,存在連續中文換行問題,需要在轉換的時候特殊處理)

 

  1. maven地址

<!--freemarker-->

<!--https://mvnrepository.com/artifact/org.freemarker/freemarker-->

<dependency>

  <groupId>org.freemarker</groupId>

  <artifactId>freemarker</artifactId>

  <version>2.3.23</version>

</dependency>

<!--JavaHTMLParser-->

<!--https://mvnrepository.com/artifact/org.jsoup/jsoup-->

<dependency>

  <groupId>org.jsoup</groupId>

  <artifactId>jsoup</artifactId>

  <version>1.10.2</version>

</dependency>

 

<dependency>

  <groupId>org.xhtmlrenderer</groupId>

  <artifactId>flying-saucer-pdf</artifactId>

  <version>9.0.8</version>

</dependency>

 

<dependency>

  <groupId>org.xhtmlrenderer</groupId>

  <artifactId>flying-saucer-pdf-itext5</artifactId>

  <version>9.1.5</version>

</dependency>

 

  1. 模板文件生成
    1. 先將wrod的格式內容定義好,如果需要插入參數的地方以${xxx}為表示,例:${product}

模板例子:

 

 

    2. 將word文檔另存為 “篩選過的網頁(*.htm;*.html)” 的文件,打開該文件檢查每個變量(${product})是否完整,有可能在${}中出現其他代碼,需要刪除。

    3. 檢查文件沒問題之后,將文件改成后綴為ftl的文件,引入到項目中,和生成的word的模板文件不同,html模板不需要引入圖片字段占位符,html可以直接通過img標簽展示圖片

  2. 獲取模板文件,生成html

    freemark獲取模板

    下面這種方式能獲取模板,但是在項目打包之后無法獲取jar包內的文件

      Configuration configuration=newConfiguration(Configuration.getVersion());

      configuration.setDefaultEncoding(StandardCharsets.UTF_8.toString());

      configuration.setDirectoryForTemplateLoading(newFile(templatePath));

      Template template=configuration.getTemplate("xxx.ftl");

 

    通過流的形式直接創建模板對象

      Configuration configuration=newConfiguration(Configuration.getVersion());

      configuration.setDefaultEncoding(StandardCharsets.UTF_8.toString());

      configuration.setDirectoryForTemplateLoading(newFile(templatePath));

      InputStream inputStream=newFileInputStream(newFile(templatePath+"/"+templateName));

      InputStreamReader inputStreamReader=newInputStreamReader(inputStream,StandardCharsets.UTF_8);

      Template template=newTemplate("xxx.ftl",inputStreamReader,configuration);

 

 

  1. 通過template替換ftl中的占位符,再將結果寫入字符流中,返回結果字符串

    dataMap為Map格式,占位符為key,結果值為value

 

    StringWriter stringWriter = new StringWriter();

    BufferedWriter wirter = new BufferedWriter(stringWriter);

    template.process(dataMap,writer);

    return writer.toString();

 

  1. flying saucer對html有嚴格的檢查,必須要以<a></a>的形式存在,使用 jsoup 對html進行格式化操作

    Document doc = Jsoup.parse(htmlStr);

    Elements imageElements = doc.select("img");

    For(Element ele : Elements  ) {    

      …

    }

 

Jsoup對html有很好的支持,可以對一些代碼添加、修改樣式。

flying saucer 是基於Itext的包,itext存在連續中文不換行的問題,可以通過 jsoup 修改所有的文字,在文字后面加上空格,這樣就能修復無法自動換行的問題

 

  1. 加載中文字體

    flying saucer 不支持中文,需要加載中文字體。

    在ftl的模板文件中的body標簽引入字體

    例: <body  style="font-family:'Arial Unicode MS'">

 

  在代碼中字體文件:

 

    ITextRenderer renderer = new ITextRenderer();

 

    // mac

    renderer .getFontResolver().addFont("/library/fonts/Arial Unicode.ttf",  BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);    

 

    // linux:

    renderer .getFontResolver().addFont("/usr/share/fonts/TTF/ARIALUNI.TTF",   BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);     

 

     // windows:

    renderer .getFontResolver().addFont("C:/Windows/Fonts/ARIALUNI.TTF",     BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);   

 

  在項目中文字可以放在項目目錄里面  

    ClassPathResource resource = new ClassPathResource("ARIALUNI.TTF");

    renderer .getFontResolver().addFont(resource.getPath(),  BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);     

 

 

 

 

  源碼如下:

 

 

<<CreateHtmlByFreemarker.java>>

package org.java.export.plugin.example;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;

public class CreateHtmlByFreemarker {


public static void main(String[] args) {
String str = getHtmlStr();
System.out.println(str);
}

public static String getHtmlStr(){
// step1 創建freeMarker配置實例
Configuration configuration = new Configuration();
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
try {
// step2 獲取模版路徑
String templatePath = Class.class.getResource("/ftl").getPath();
templatePath = java.net.URLDecoder.decode(templatePath,"utf-8");//這里我的路徑有空格添加此處理
configuration.setDirectoryForTemplateLoading(new File(templatePath));
StringBuilder sb = new StringBuilder();
sb.append("<div>");
sb.append("<img style='height:100px;width:200px;display:block;' src='https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2534506313,1688529724&fm=26&gp=0.jpg' />");
sb.append("</br><span>wesley 演示 導出富文本!@@#######¥¥%%%%………………&&&**~~~~~~&&&&&&&&、、、、、、、、</span>");
sb.append("</br><span>----多圖分割線---</span>");
sb.append("<img style='height:100px;width:200px;display:block;' src='https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2534506313,1688529724&fm=26&gp=0.jpg' />");
sb.append("</br><span>中國夢,幸福夢!</span>");
sb.append("</div>");
sb.append("<table style='border: 0.5px solid #000' border='0' cellspacing='0' cellpadding='0'>");
sb.append("<tr>");
sb.append(" <th style='border: 0.5px solid #000'>Month</th>");
sb.append("<th style='border: 0.5px solid #000'>Savings</th>");
sb.append("</tr>");
sb.append("<tr>");
sb.append("<td style='border: 0.5px solid #000'>January</td>");
sb.append("<td style='border: 0.5px solid #000'>$100</td>");
sb.append("</tr>");
sb.append("</table>");

// step3 創建數據模型
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("name", "wesley");
dataMap.put("datetime","2017-05-10");
dataMap.put("title","演示demo");
dataMap.put("context1", sb.toString());
dataMap.put("context2", sb.toString());
dataMap.put("context3", sb.toString());
dataMap.put("context4", sb.toString());
dataMap.put("context5", sb.toString());
dataMap.put("context6", sb.toString());
// step4 加載模版文件
Template template = configuration.getTemplate("title.ftl");
// step5 生成數據
template.process(dataMap, writer);
String htmlStr = stringWriter.toString();
System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^user.ftl 文件創建成功 !");
return htmlStr;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

}

 

  <<CreatePDFByHtml.java>>

  

package org.java.export.plugin.example;

import com.itextpdf.text.pdf.BaseFont;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.*;

public class CreatePDFByHtml {

public static void main(String[] args) throws IOException {
String pdfPath = "/Users/liqi/Desktop/1.pdf";
String htmlFilePath = "/Users/liqi/Desktop/1586671323385.html";
createPDFByHtml( pdfPath,htmlFilePath);
}

/**
* 該方法用來將指定的word文件轉換成pdf文件(使用flying saucer技術)
* @param pdfPath:生成后的pdf所在目錄,包括目錄+pdf名稱+.+pdf
* @param htmlFilePath:需要進行轉換的html文件所在目錄,包括目錄+html名稱+.+html
* */
public static boolean createPDFByHtml(String pdfPath, String htmlFilePath){
boolean result = false;
//1、判斷給定的文件是否是html文件:是htm格式結尾,或者以html格式結尾
if(htmlFilePath.toUpperCase().endsWith(".HTM") ||
htmlFilePath.toUpperCase().endsWith(".HTML")){//兩種格式都是掃描文件格式
try {
OutputStream os = new FileOutputStream(pdfPath);
ITextRenderer renderer = new ITextRenderer();
String str = CreateHtmlByFreemarker.getHtmlStr();
System.out.println(str);
System.out.println("--------------=============");
Document doc = Jsoup.parse(str);

System.out.println(doc.html());
Elements elements = doc.select("img");
int i=0;
for (Element element : elements){
element.attr("id",i+"");
i++;
}


String content=doc.html();
content = content.replace("&nbsp;","&#160;");
content = content.replace("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">","<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></meta>");
content = content.replace("(filtered)\">","(filtered)\"></meta>");
content = content.replaceAll("<br>","<br></br>");
for (Element element : elements){
String startStr = element.outerHtml();
String endStr = element.outerHtml()+"</img>";
content = content.replace(startStr,endStr);
}


ITextFontResolver fontResolver = renderer.getFontResolver();

// fontResolver.addFont("/Users/liqi/ideaWorkspace/java-export-word-plugin-master/PluginExample/src/main/resources/ftl/simhei.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//fontResolver.addFont("/Users/liqi/ideaWorkspace/java-export-word-plugin-master/PluginExample/src/main/resources/ftl/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);



/* // mac
fontResolver.addFont("/library/fonts/Arial Unicode.ttf",
BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

// linux:
fontResolver.addFont("/usr/share/fonts/TTF/ARIALUNI.TTF",
BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

// windows:
fontResolver.addFont("C:/Windows/Fonts/ARIALUNI.TTF",
BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); */


// 解決中文支持問題
fontResolver.addFont("/library/fonts/Arial Unicode.ttf",
BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

renderer.setDocumentFromString(content);

renderer.layout();
renderer.createPDF(os);
os.close();
result = true;
} catch (Exception e) {
result = false;
e.printStackTrace();
}
}else{
result = false;
}
return result ;
}
}

 

還有另一種方案也能實現該功能

使用Itext繪制pdf表格,前端使用canvas將富文本生成圖片,在使用IText將圖片插入到指定位置中,這也是可以實現的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM