电子凭证 : Java 生成 Pdf
来源:蛙牛,
my.oschina.net/lujianing/blog/894365
1.背景
在某些业务场景中,需要提供相关的电子凭证,比如网银/支付宝中转账的电子回单,签约的电子合同等。方便用户查看,下载,打印。目前常用的解决方案是,把相关数据信息,生成对应的pdf文件返回给用户。
本文源码:http://git.oschina.net/lujianing/java_pdf_demo
2.iText
iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。
iText 官网:http://itextpdf.com/
iText 开发文档: http://developers.itextpdf.com/developers-home
iText目前有两套版本iText5和iText7。iText5应该是网上用的比较多的一个版本。iText5因为是很多开发者参与贡献代码,因此在一些规范和设计上存在不合理的地方。iText7是后来官方针对iText5的重构,两个版本差别还是挺大的。不过在实际使用中,一般用到的都比较简单,所以不用特别拘泥于使用哪个版本。比如我们在http://mvnrepository.com/中搜索iText,出来的都是iText5的依赖。
来个最简单的例子:
添加依赖:
<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
测试代码:JavaToPdf
package com.lujianing.test;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* Created by lujianing on 2017/5/7.
*/
public class JavaToPdf {
private static final String DEST = "target/HelloWorld.pdf";
public static void main(String[] args) throws FileNotFoundException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));
document.open();
document.add(new Paragraph("hello world"));
document.close();
writer.close();
}
}
运行结果:
3.iText-中文支持
iText默认是不支持中文的,因此需要添加对应的中文字体,比如黑体simhei.ttf
可参考文档:http://developers.itextpdf.com/examples/font-examples/using-fonts#1227-tengwarquenya1.java
测试代码:JavaToPdfCN
package com.lujianing.test;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* Created by lujianing on 2017/5/7.
*/
public class JavaToPdfCN {
private static final String DEST = "target/HelloWorld_CN.pdf";
private static final String FONT = "simhei.ttf";
public static void main(String[] args) throws FileNotFoundException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));
document.open();
Font f1 = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
document.add(new Paragraph("hello world,我是鲁家宁", f1));
document.close();
writer.close();
}
}
输出结果:
4.iText-Html渲染
在一些比较复杂的pdf布局中,我们可以通过html去生成pdf
可参考文档:http://developers.itextpdf.com/examples/xml-worker-itext5/xml-worker-examples
添加依赖:
<!-- https://mvnrepository.com/artifact/com.itextpdf.tool/xmlworker -->
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.11</version>
</dependency>
添加模板:template.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<style>
body{
font-family:SimHei;
}
.red{
color: red;
}
</style>
</head>
<body>
<div class="red">
你好,鲁家宁
</div>
</body>
</html>
测试代码:JavaToPdfHtml
package com.lujianing.test;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.lujianing.test.util.PathUtil;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* Created by lujianing on 2017/5/7.
*/
public class JavaToPdfHtml {
private static final String DEST = "target/HelloWorld_CN_HTML.pdf";
private static final String HTML = PathUtil.getCurrentPath()+"/template.html";
private static final String FONT = "simhei.ttf";
public static void main(String[] args) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));
// step 3
document.open();
// step 4
XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontImp.register(FONT);
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new FileInputStream(HTML), null, Charset.forName("UTF-8"), fontImp);
// step 5
document.close();
}
}
输出结果:
需要注意:
1.html中必须使用标准的语法,标签一定需要闭合
2.html中如果有中文,需要在样式中添加对应字体的样式
5.iText-Html-Freemarker渲染
在实际使用中,html内容都是动态渲染的,因此我们需要加入模板引擎支持,可以使用FreeMarker/Velocity,这里使用FreeMarker举例
添加FreeMarke依赖:
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.19</version>
</dependency>
添加模板:template_freemarker.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<style>
body{
font-family:SimHei;
}
.blue{
color: blue;
}
</style>
</head>
<body>
<div class="blue">
你好,${name}
</div>
</body>
</html>
测试代码:JavaToPdfHtmlFreeMarker
package com.lujianing.test;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.lujianing.test.util.PathUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
/**
* Created by lujianing on 2017/5/7.
*/
public class JavaToPdfHtmlFreeMarker {
private static final String DEST = "target/HelloWorld_CN_HTML_FREEMARKER.pdf";
private static final String HTML = "template_freemarker.html";
private static final String FONT = "simhei.ttf";
private static Configuration freemarkerCfg = null;
static {
freemarkerCfg =new Configuration();
//freemarker的模板目录
try {
freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException, DocumentException {
Map<String,Object> data = new HashMap();
data.put("name","鲁家宁");
String content = JavaToPdfHtmlFreeMarker.freeMarkerRender(data,HTML);
JavaToPdfHtmlFreeMarker.createPdf(content,DEST);
}
public static void createPdf(String content,String dest) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontImp.register(FONT);
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new ByteArrayInputStream(content.getBytes()), null, Charset.forName("UTF-8"), fontImp);
// step 5
document.close();
}
/**
* freemarker渲染html
*/
public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
Writer out = new StringWriter();
try {
// 获取模板,并设置编码方式
Template template = freemarkerCfg.getTemplate(htmlTmp);
template.setEncoding("UTF-8");
// 合并数据模型与模板
template.process(data, out); //将合并后的数据和模板写入到流中,这里使用的字符流
out.flush();
return out.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return null;
}
}
输出结果:
目前为止,我们已经实现了iText通过Html模板生成Pdf的功能,但是实际应用中,我们发现iText并不能对高级的CSS样式进行解析,比如CSS中的position属性等,因此我们要引入新的组件
6.Flying Saucer-CSS高级特性支持
Flying Saucer is a pure-Java library for rendering arbitrary well-formed XML (or XHTML) using CSS 2.1 for layout and formatting, output to Swing panels, PDF, and images.
Flying Saucer是基于iText的,支持对CSS高级特性的解析。
添加依赖:
<!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf-itext5 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.1.5</version>
</dependency>
添加模板:template_freemarker_fs.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<style>
body{
font-family:SimHei;
}
.color{
color: green;
}
.pos{
position:absolute;
left:200px;
top:5px;
width: 200px;
font-size: 10px;
}
</style>
</head>
<body>
<img src="logo.png" width="600px"/>
<div class="color pos">
你好,${name}
</div>
</body>
</html>
测试代码:JavaToPdfHtmlFreeMarker
package com.lujianing.test.flyingsaucer;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.BaseFont;
import com.lujianing.test.util.PathUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
/**
* Created by lujianing on 2017/5/7.
*/
public class JavaToPdfHtmlFreeMarker {
private static final String DEST = "target/HelloWorld_CN_HTML_FREEMARKER_FS.pdf";
private static final String HTML = "template_freemarker_fs.html";
private static final String FONT = "simhei.ttf";
private static final String LOGO_PATH = "file://"+PathUtil.getCurrentPath()+"/logo.png";
private static Configuration freemarkerCfg = null;
static {
freemarkerCfg =new Configuration();
//freemarker的模板目录
try {
freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException, DocumentException, com.lowagie.text.DocumentException {
Map<String,Object> data = new HashMap();
data.put("name","鲁家宁");
String content = JavaToPdfHtmlFreeMarker.freeMarkerRender(data,HTML);
JavaToPdfHtmlFreeMarker.createPdf(content,DEST);
}
/**
* freemarker渲染html
*/
public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
Writer out = new StringWriter();
try {
// 获取模板,并设置编码方式
Template template = freemarkerCfg.getTemplate(htmlTmp);
template.setEncoding("UTF-8");
// 合并数据模型与模板
template.process(data, out); //将合并后的数据和模板写入到流中,这里使用的字符流
out.flush();
return out.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return null;
}
public static void createPdf(String content,String dest) throws IOException, DocumentException, com.lowagie.text.DocumentException {
ITextRenderer render = new ITextRenderer();
ITextFontResolver fontResolver = render.getFontResolver();
fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 解析html生成pdf
render.setDocumentFromString(content);
//解决图片相对路径的问题
render.getSharedContext().setBaseURL(LOGO_PATH);
render.layout();
render.createPDF(new FileOutputStream(dest));
}
}
输出结果:
在某些场景下,html中的静态资源是在本地,我们可以使用render.getSharedContext().setBaseURL()加载文件资源,注意资源URL需要使用文件协议 “file://”。
对于生成的pdf页面大小,可以用css的@page属性设置。
7.PDF转图片
在某些场景中,我们可能只需要返回图片格式的电子凭证,我们可以使用Jpedal组件,把pdf转成图片
添加依赖:
<!-- https://mvnrepository.com/artifact/org.jpedal/jpedal-lgpl -->
<dependency>
<groupId>org.jpedal</groupId>
<artifactId>jpedal-lgpl</artifactId>
<version>4.74b27</version>
</dependency>
测试代码:JavaToPdfImgHtmlFreeMarker
package com.lujianing.test.flyingsaucer;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.BaseFont;
import com.lujianing.test.util.PathUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.jpedal.PdfDecoder;
import org.jpedal.exception.PdfException;
import org.jpedal.fonts.FontMappings;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
/**
* Created by lujianing on 2017/5/7.
*/
public class JavaToPdfImgHtmlFreeMarker {
private static final String DEST = "target/HelloWorld_CN_HTML_FREEMARKER_FS_IMG.png";
private static final String HTML = "template_freemarker_fs.html";
private static final String FONT = "simhei.ttf";
private static final String LOGO_PATH = "file://"+PathUtil.getCurrentPath()+"/logo.png";
private static final String IMG_EXT = "png";
private static Configuration freemarkerCfg = null;
static {
freemarkerCfg =new Configuration();
//freemarker的模板目录
try {
freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException, DocumentException, com.lowagie.text.DocumentException {
Map<String,Object> data = new HashMap();
data.put("name","鲁家宁");
String content = JavaToPdfImgHtmlFreeMarker.freeMarkerRender(data,HTML);
ByteArrayOutputStream pdfStream = JavaToPdfImgHtmlFreeMarker.createPdf(content);
ByteArrayOutputStream imgSteam = JavaToPdfImgHtmlFreeMarker.pdfToImg(pdfStream.toByteArray(),2,1,IMG_EXT);
FileOutputStream fileStream = new FileOutputStream(new File(DEST));
fileStream.write(imgSteam.toByteArray());
fileStream.close();
}
/**
* freemarker渲染html
*/
public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
Writer out = new StringWriter();
try {
// 获取模板,并设置编码方式
Template template = freemarkerCfg.getTemplate(htmlTmp);
template.setEncoding("UTF-8");
// 合并数据模型与模板
template.process(data, out); //将合并后的数据和模板写入到流中,这里使用的字符流
out.flush();
return out.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return null;
}
/**
* 根据模板生成pdf文件流
*/
public static ByteArrayOutputStream createPdf(String content) {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ITextRenderer render = new ITextRenderer();
ITextFontResolver fontResolver = render.getFontResolver();
try {
fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
} catch (com.lowagie.text.DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 解析html生成pdf
render.setDocumentFromString(content);
//解决图片相对路径的问题
render.getSharedContext().setBaseURL(LOGO_PATH);
render.layout();
try {
render.createPDF(outStream);
return outStream;
} catch (com.lowagie.text.DocumentException e) {
e.printStackTrace();
} finally {
try {
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 根据pdf二进制文件 生成图片文件
*
* @param bytes pdf二进制
* @param scaling 清晰度
* @param pageNum 页数
*/
public static ByteArrayOutputStream pdfToImg(byte[] bytes, float scaling, int pageNum,String formatName) {
//推荐的方法打开PdfDecoder
PdfDecoder pdfDecoder = new PdfDecoder(true);
FontMappings.setFontReplacements();
//修改图片的清晰度
pdfDecoder.scaling = scaling;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
//打开pdf文件,生成PdfDecoder对象
pdfDecoder.openPdfArray(bytes); //bytes is byte[] array with PDF
//获取第pageNum页的pdf
BufferedImage img = pdfDecoder.getPageAsImage(pageNum);
ImageIO.write(img, formatName, out);
} catch (PdfException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
return out;
}
}
输出结果:
Jpedal支持将指定页Pdf生成图片,pdfDecoder.scaling设置图片的分辨率(不同分辨率下文件大小不同) ,支持多种图片格式,具体更多可自行研究
8.总结
对于电子凭证的技术方案,总结如下:
1.html模板+model数据,通过freemarker进行渲染,便于维护和修改
2.渲染后的html流,可通过Flying Saucer组件生成pdf文件流,或者生成pdf后再转成jpg文件流
3.在Web项目中,对应的文件流,可以通过ContentType设置,在线查看/下载,不需通过附件服务
9.纯前端解决方案
还有一种解决方案是使用PhantomJS
git地址: https://github.com/ariya/phantomjs
PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。 PhantomJS 可以用于 页面自动化 , 网络监测 , 网页截屏 ,以及 无界面测试 等。
电子凭证 : Java 生成 Pdf的更多相关文章
- Java 生成pdf表格文档
最近在工作做一个泰国的项目,应供应商要求,需要将每天的交易生成pdf格式的报表上传到供应商的服务器,特此记录实现方法.废话不多说,直接上代码: THSarabunNew.ttf该文件是泰国字体自行网上 ...
- JAVA生成PDF文件
生成PDF文件是主要应用的是ITEXT插件 import java.awt.Color; import java.io.File; import java.io.FileOutputStream; i ...
- JAVA 生成PDF报表()
许多应用程序都要求动态生成 PDF 文档.这些应用程序涵盖从生成客户对帐单并通过电子邮件交付的银行到购买特定的图书章节并以 PDF 格式接收这些图书章节的读者.这个列表不胜枚举.在本文中,我们将使用 ...
- Java生成PDF报表
一.前言 前几天,做ASN条码收货模块,需要实现打印下载收货报表,经一番查找,选定iText--用于生成PDF文档的一个Java类库.废话不多说,进入正题. 二.iText简介 iText是著名的开放 ...
- java生成PDF文件(itext)
itextpdf-5.4.3.jar下载地址: http://www.kuaipan.cn/file/id_58980483773788178.htm 导入itextpdf-5.4.3.jar ToP ...
- java生成PDF,各种格式、样式、水印都有
代码中有两处需要图片,请自行替换. 一个是水印.一个是手指. 需要的JAR包链接:http://download.csdn.net/detail/justinytsoft/9688893 下面是预览: ...
- java生成pdf
介绍 本篇博客主要是为了介绍如何使用:flying-saucer+itext+freemark实现导出复杂点的pdf文件. 思路 先把pdf的内容以html形式准备好 使用freemarker将htm ...
- java生成PDF,并下载到本地
1.首先要写一个PDF工具类,以及相关工具 2.PDF所需jar包 iText是一种生成PDF报表的Java组件 freemarker是基于模板来生成文本输出 <dependency> & ...
- Java生成PDF文件(转)
原文地址:https://www.cnblogs.com/shuilangyizu/p/5760928.html 一.前言 前几天,做ASN条码收货模块,需要实现打印下载收货报表,经一番查找,选定iT ...
随机推荐
- win-zabbix_agent端配置解析
Zabbix agent 在windows上安装部署 部署windows服务器需要在agent服务器上添加到10.4.200.2的路由 蓝泰服务器需要添加10.0.90.5的网关,联通的机器需要添加到 ...
- Python内置函数(65)——staticmethod
英文文档: staticmethod(function) Return a static method for function. A static method does not receive a ...
- CTF中常见密码题解密网站总结
0x00.综合 网站中包含大多编码的解码. http://web2hack.org/xssee/ https://www.sojson.com/ http://web.chacuo.net/ 0x01 ...
- leetcode算法: Keyboard Row
Given a List of words, return the words that can be typed using letters of alphabet on only one row' ...
- 关于HTML
我的PHP学习之旅 学习PHP已经有一段时间了,今天才想好好的总结一下这一路走来的点点滴滴,也想把我的学习方法及经验分享给大家,希望能对你有所帮助. 首先学习的是PHP前端部分,这里需要学习的知识有H ...
- 南阳OJ-14-会场安排问题---区间不相交
题目链接: http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=14 题目描述: 学校的小礼堂每天都会有许多活动,有时间这些活动的计划时间会发生冲突 ...
- 浅析Java的Frok/Join框架
一丶Fork/Join框架产生背景: 随着并发需求的不断提高和硬件的不断发展,程序并行执行仿佛就提上日程上来了,伟大的毛主席就说过:"人多力量大",所以如果一件事可以分配给多个人同 ...
- this对象指向
this表示函数运行时,自动生成的一个内部对象,只能在函数内部运行 function test(){ this.x = 1; } 随着使用场景的变化,this的值会发生变化 原则:this指的值调用函 ...
- springCloud 微服务框架搭建入门(很简单的一个案例不喜勿扰)
Spring cloud 实现服务注册及发现 服务注册与发现对于微服务系统来说非常重要.有了服务发现与注册,你就不需要整天改服务调用的配置文件了,你只需要使用服务的标识符,就可以访问到服务. clou ...
- 【SQL.基础构建-第一节(1/4)】
-- Tips:数据库与sql-- 一.What's 数据库-- 1.数据库(Database,DB):将大量数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合.-- ...