Java生成pdf,兼容富文本內容
使用技術,freemark + jsoup + flying saucer
使用freemark替換模板文件中指定的占位符,生成一個完整的的html字符串,
使用jsoup對html進行格式化,
使用flying saucer 將整個html進行pdf轉換(flying saucer對css的支持不是很完整,存在連續中文換行問題,需要在轉換的時候特殊處理)
- 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>
- 模板文件生成
- 先將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);
- 通過template替換ftl中的占位符,再將結果寫入字符流中,返回結果字符串
dataMap為Map格式,占位符為key,結果值為value
StringWriter stringWriter = new StringWriter();
BufferedWriter wirter = new BufferedWriter(stringWriter);
template.process(dataMap,writer);
return writer.toString();
- 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 修改所有的文字,在文字后面加上空格,這樣就能修復無法自動換行的問題
- 加載中文字體
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(" "," ");
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將圖片插入到指定位置中,這也是可以實現的