Java生成支持中文的pdf
工具类使用背景
- Web项目为用户提供将报表、解决方案、文章等重要数据信息导出为PDF保存到本地电脑的功能,是必不可少的;所以需要一个通过freemark动态解析html,并将html转换为PDF和支持中文的工具类。
- 生成PDF需要使用到字体库,默认情况下linux是不支持中文的,所以当项目部署到linux的请记得先安装宋体字库,如何安装请百度“Linux添加宋体字库”;否则生成包含中文的PDF会展示乱码。
第三方依赖
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.2</version> </dependency> <dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.13.2</version> </dependency>
核心代码
1.freemark动态解析html工具
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
/**
* @author huangrusheng
* @version 1.0
* @date 2021/5/24 10:25
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class FreeMarkUtil {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static final Configuration configuration;
static {
configuration = new Configuration(Configuration.VERSION_2_3_30);
configuration.setDefaultEncoding(DEFAULT_CHARSET.name());
configuration.setClassLoaderForTemplateLoading(getDefaultClassLoader(), "ftl");
}
public static void parse(Object params, String templateName, Writer writer) throws IOException, TemplateException {
Template template = configuration.getTemplate(templateName);
template.process(params,writer);
}
public static byte[] parse(Object params,String templateName) throws IOException, TemplateException {
StringWriter stringWriter = new StringWriter();
parse(params,templateName,stringWriter);
System.out.println(stringWriter.toString());
return stringWriter.toString().getBytes(DEFAULT_CHARSET);
}
public static ClassLoader getDefaultClassLoader(){
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader != null){
return classLoader;
}
classLoader = FreeMarkUtil.class.getClassLoader();
if(classLoader != null){
return classLoader;
}
return ClassLoader.getSystemClassLoader();
}
}
2.html转换为pdf工具
import cn.hrs.tools.export.FreeMarkUtil;
import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
/**
* @author huangrusheng
* @version 1.0
* @date 2021/5/24 15:03
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class PDFUtil {
public static void exportByHtml(Object params, String templateName, OutputStream outputStream)throws Exception{
exportByHtml(params,templateName,outputStream,null);
}
public static void exportByHtml(Object params, String templateName, OutputStream outputStream, Rectangle rectangle) throws Exception{
Document document = null;
try{
document = new Document(rectangle == null ? PageSize.A4 : rectangle);
PdfWriter pdfWriter = PdfWriter.getInstance(document,outputStream);
document.open();
XMLWorkerHelper xmlWorkerHelper = XMLWorkerHelper.getInstance();
xmlWorkerHelper.parseXHtml(pdfWriter,document, new ByteArrayInputStream(FreeMarkUtil.parse(params,templateName)),FreeMarkUtil.DEFAULT_CHARSET ,new SongTiFontProvider());
}finally {
if (document != null){
document.close();
}
}
}
public static class SongTiFontProvider extends XMLWorkerFontProvider{
@Override
public Font getFont(final String fontName, String encoding, float size, final int style){
String fntName = fontName;
if(StrUtil.isBlank(fntName)){
fntName = "宋体";
}
float newSize = size;
if(newSize == 0){
newSize = 4;
}
return super.getFont(fntName,encoding,newSize,style);
}
}
}
单元测试用例
1.ftl模板
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<body>
<h1>${name}</h1>
<p>
<table border="1">
<tr>
<td style="width:80%"> ${introduce}</td>
<td style="width:20%"> <img style="width:100px;height:100px;float:right" src="${address}"/></td>
</tr>
</table>
</p>
</body>
</html>
2.代码
import cn.hrs.tools.export.pdf.PDFUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* @author huangrusheng
* @version 1.0
* @date 2021/5/24 15:47
*/
@RunWith(JUnit4.class)
public class PDFUtilTest {
@Test
public void exportPDF() throws Exception{
FileOutputStream outputStream = new FileOutputStream("D:\\hrs.pdf");
Map<String,Object> params = new HashMap<>(3);
params.put("name","智能安防方案");
params.put("introduce","利用了智能视频分析技术提供的报警信息的安防系统就称为智能安防系统 。" +
"而由于智能分析的图像识别特性,它与安防系统中的监控系统结合是最会紧密的,智能监控系统也是安防系统中智能分析应用最成熟的系统。也就是说智能分析技术在安防系统中的应用发展方向是两个,一是不断提高分析技术,提供更准确更丰富的报警结果;二是不断优化安防系统方案,充分利用智能分析技术的特点,将智能分析的报警结果智能地应用到安防体系中。");
params.put("address","https://profile.csdnimg.cn/A/5/D/1_m0_38138879");
PDFUtil.exportByHtml(params,"text.ftl",outputStream);
outputStream.close();
}
}
3.结果
模板位置: