一、介绍
在实际的业务开发的时候,研发人员往往会碰到很多这样的一些场景,需要提供相关的电子凭证信息给用户,例如网银/支付宝/微信购物支付的电子发票、订单的库存打印单、各种电子签署合同等等,以方便用户查看、打印或者下载。
例如下图的电子发票!
熟悉这块业务的童鞋,一定特别清楚,目前最常用的解决方案是:把相关的数据信息,通过一些技术手段生成对应的 PDF 文件,然后返回给用户,以便预览、下载或者打印。
不太熟悉这项技术的童鞋,也不用着急,今天我们一起来详细了解一下在线生成 PDF 文件的技术实现手段!
二、案例实现
在介绍这个代码实践之前,我们先来了解一下这个第三方库:iText
,对,没错,它就是我们今天的主角。
iText
是著名的开放源码站点sourceforge
一个项目,是用于生成PDF
文档的一个java
类库,通过iText
不仅可以生成PDF
或rtf
的文档,而且还可以将XML
、Html
文件转化为PDF
文件。
iText
目前有两套版本,分别是iText5
和iText7
。iText5
应该是网上用的比较多的一个版本。iText5
因为是很多开发者参与贡献代码,因此在一些规范和设计上存在不合理的地方。iText7
是后来官方针对iText5
的重构,两个版本差别还是挺大的。不过在实际使用中,一般用到的都比较简单的 API,所以不用特别拘泥于使用哪个版本。
2.1、添加 iText 依赖包
在使用它之前,我们先引人相关的依赖包!
com.itextpdf
itextpdf
5.5.11
com.itextpdf.tool
xmlworker
5.5.11
com.itextpdf
itext-asian
5.2.0
org.xhtmlrenderer
flying-saucer-pdf-itext5
9.1.16
net.sf.jtidy
jtidy
r938
2.2、简单实现
老规矩,我们先来一个hello world
,代码如下:
public class CreatePDFMainTest {
public static void main(String[] args) throws Exception {
Document document = new Document(PageSize.A4);
//第二步,创建Writer实例
PdfWriter.getInstance(document, new FileOutputStream("hello.pdf"));
//创建中文字体
BaseFont bfchinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font fontChinese = new Font(bfchinese, 12, Font.NORMAL);
//第三步,打开文档
document.open();
//第四步,写入内容
Paragraph paragraph = new Paragraph("hello world", fontChinese);
document.add(paragraph);
//第五步,关闭文档
document.close();
}
}
打开hello.pdf
文件,内容如下!
2.3、复杂实现
在实际的业务开发中,因为业务场景非常复杂,而且变化快,我们往往不会采用上面介绍的写入内容方式来生成文件,而是采用HTML
文件转化为PDF
文件。
例如下面这张入库单!
我们应该如何快速实现呢?
首先,我们采用html
语言编写一个入库单页面,将其命令为printDemo.html
,源代码如下:
出库单
入库单
操作人:xxx
创建时间:2021-09-14 12:00:00
序号
商品
单位
数量
1
xxx沐浴露
箱
3
2
xxx洗发水
箱
4
3
xxx洗衣粉
箱
5
4
xxx洗面奶
箱
5
接着,我们将html
文件转成PDF
文件,源码如下:
public class CreatePDFMainTest {
/**
* 创建PDF文件
* @param htmlStr
* @throws Exception
*/
private static void writeToOutputStreamAsPDF(String htmlStr) throws Exception {
String targetFile = "pdfDemo.pdf";
File targeFile = new File(targetFile);
if(targeFile.exists()) {
targeFile.delete();
}
//定义pdf文件尺寸,采用A4横切
Document document = new Document(PageSize.A4, 25, 25, 15, 40);// 左、右、上、下间距
//定义输出路径
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(targetFile));
PdfReportHeaderFooter header = new PdfReportHeaderFooter("", 8, PageSize.A4);
writer.setPageEvent(header);
writer.addViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
document.open();
// CSS
CSSResolver cssResolver = new StyleAttrCSSResolver();
CssAppliers cssAppliers = new CssAppliersImpl(new XMLWorkerFontProvider(){
@Override
public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color) {
try {
//用于中文显示的Provider
BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
return new Font(bfChinese, size, style);
} catch (Exception e) {
return super.getFont(fontname, encoding, size, style);
}
}
});
//html
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
htmlContext.setImageProvider(new AbstractImageProvider() {
@Override
public Image retrieve(String src) {
//支持图片显示
int pos = src.indexOf("base64,");
try {
if (src.startsWith("data") && pos > 0) {
byte[] img = Base64.decode(src.substring(pos + 7));
return Image.getInstance(img);
} else if (src.startsWith("http")) {
return Image.getInstance(src);
}
} catch (BadElementException ex) {
return null;
} catch (IOException ex) {
return null;
}
return null;
}
@Override
public String getImageRootPath() {
return null;
}
});
// Pipelines
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new ByteArrayInputStream(htmlStr.getBytes()));
document.close();
}
/**
* 读取 HTML 文件
* @return
*/
private static String readHtmlFile() {
StringBuffer textHtml = new StringBuffer();
try {
File file = new File("printDemo.html");
BufferedReader reader = new BufferedReader(new FileReader(file));
String tempString = null;
// 一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null) {
textHtml.append(tempString);
}
reader.close();
} catch (IOException e) {
return null;
}
return textHtml.toString();
}
public static void main(String[] args) throws Exception {
//读取html文件
String htmlStr = readHtmlFile();
//将html文件转成PDF
writeToOutputStreamAsPDF(htmlStr);
}
}
运行程序,打开pdfDemo.pdf
,结果如下!
2.4、变量替换方式
上面的html
文件,是我们事先已经编辑好的,才能正常渲染。
但是在实际的业务开发的时候,例如下面的商品内容,完全是动态的,还是xxx-202109入库单
的名称,以及二维码,都是动态的。
这个时候,我们可以采用freemarker
模板引擎,通过定义变量来动态填充内容,直到转换出来的结果就是我们想要的html
页面。
当然,还有一种办法,例如下面这个,我们也可以在html
页面里面定义${name}
变量,然后在读取完文件之后,我们将其变量进行替换成我们想填充的任何值,这其实也是模板引擎最核心的一个玩法。
您好:${name}
欢迎,登录博客网站
三、总结
itext
框架是一个非常实用的第三方pdf
文件生成库,尤其是面对比较简单的pdf
文件内容渲染的时候,它完全满足我们的需求。
但是对于那种复杂的pdf
文档,可能需要我们自己单独进行适配开发。
如果想要获取源代码,关注下方公众号,并回复【cccc10】即可获取!
四、参考
1、JAVA使用ItextPDF