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;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;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将图片插入到指定位置中,这也是可以实现的

原文地址:https://www.cnblogs.com/LeachBlog/p/12738642.html