PDF组件调研报告

330 阅读6分钟

1. 调研背景

客户要求大比武系统,比武评比活动结束后,需要给老师发布证书,要根据不同模板设置不同格式证书,并下载为pdf打印,存档。考虑前端页面上使用VUE渲染HTML,点击下载导出PDF方式的实现。

2.调研目的

满足客户需求,完成动态设置证书,下载pdf。

3.实施方案

3.1PDF组件对比

pdfbox组件

功能简介:Apache PDFbox是一个开源的、基于Java的、支持PDF文档生成的工具库,它可以用于创建新的PDF文档,修改现有的PDF文档,还可以从PDF文档中提取所需的内容。Apache PDFBox还包含了数个命令行工具。

使用场景:在读取和解析PDF的时候使用PDFBox,较为简单。PDFBox主要有以下特征:
1.提取文本,包括Unicode字符。
2.集成Lucene搜索引擎:Lucene搜索引擎与PDF索引相集成。
3.加密/解密PDF文档。
4.从PDF和XFDF格式中导入或导出表单数据。
5.向已有PDF文档中追加内容。
6.将一个PDF文档切分为多个文档。
7.覆盖PDF文档。

测试结果

测试环境堆内存消耗读取时间写入/转换时间文件大小
Win64 4核8gjdk1.8W:70.77MB R:61.86MB1561ms2560msW:836BR:852KB

itext组件

功能简介:iText是著名的开放项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。
使用场景:修改PDF的时候使用Itext,支持粒度较细,比如插入文字可以自定义字体,使用字库文件(ttf)。

测试结果

测试环境堆内存消耗读取时间写入/转换时间文件大小
Win64 4核8gjdk1.8W:22.60MBR:53.97MB165ms54msW:836BR:852KB

jodconverter(包含jodconverter-core、jodconverter-local等)组件

功能简介:Jodconvert , 是一个java的文档转换器, 利用 openoffice 或 libreOffice 来进行文档格式转换。jodconverter 4.1.0版本后,改进了api的结构,同时新增了local以及online的模块,local方式是本地启动libreoffice,然后使用uno进行编程,操作本地的office。

 使用场景:JODConverter主要的功能是用来做各种档案的转换,springboot集成了jodconverter 。libreOffice比OpenOffice转换稳定,需安装对应系统的libreOffice/OpenOffice软件。 

测试结果

测试环境堆内存消耗读取时间写入/转换时间文件大小
Win64 4核8gjdk1.8libreOffice:3.31MBOpenOffice:3.07MB#libreOffice:18363msOpenOffice:8124mslibreOffic:807KB->2.09MBOpenOffice:807KB->525KB

3.2 iText介绍

iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDFrtf的文档,而且可以将XMLHtml文件转化为PDF文件。 
iText 官网:http://itextpdf.com/
iText 开发文档: http://developers.itextpdf.com/developers-home

4. 开发集成

经过各个组件对比,选用Itext来实现,因为HTML编写对项目成员要求不高,而且无需安装客户端等。

4.1 iText常用类

com.itextpdf.text.Document:这是iText库中最常用的类,它代表了一个pdf实例。如果你需要从零开始生成一个PDF文件,你需要使用这个Document类。首先创建(new)该实例,然后打开(open)它,并添加(add)内容,最后关闭(close)该实例,即可生成一个pdf文件。
com.itextpdf.text.Paragraph:表示一个缩进的文本段落,在段落中,你可以设置对齐方式,缩进,段落前后间隔等。
com.itextpdf.text.Chapter:表示PDF的一个章节,他通过一个Paragraph类型的标题和整形章数创建。
com.itextpdf.text.Font:这个类包含了所有规范好的字体,包括family of font,大小,样式和颜色,所有这些字体都被声明为静态常量。
com.itextpdf.text.List:表示一个列表;
cocom.itextpdf.text.List:表示一个列表;
com.itextpdf.text.Anchor:表示一个锚,类似于HTML页面的链接。
com.itextpdf.text.pdf.PdfWriter:当这个PdfWriter被添加到PdfDocument后,所有添加到Document的内容将会写入到与文件或网络关联的输出流中。
com.itextpdf.text.pdf.PdfReader:用于读取PDF文件。

4.2 iText生成PDF步骤

用iText生成PDF文档需要5个步骤:
①建立Document对象的实例。
Document document = new Document();
②建立一个书写器(Writer)与document对象关联,通过书写器(Writer)可以将文档写入到磁盘中。
PDFWriter.getInstance(document,new FileOutputStream(“D:\Helloworld.PDF”));
③打开文档。
document.open();
④向文档中添加内容。
document.add(new Paragraph(“Hello World”));
⑤关闭文档。
document.close();
通过上面的5个步骤,就能产生一个Helloworld.PDF的文件,文件内容为”Hello World”。

4.3 中文支持

通过下面的代码就可以在文档中使用中文了,字体大小和粗细可以调整Font()方法中的12Font.NORMAL,
切记:Font不是AWT.Font,是我们jar包中的
BaseFont bfChinese = BaseFont.createFont(“STSong-Light”, “UniGB-UCS2-H”, BaseFont.NOT_EMBEDDED);
Font FontChinese = new Font(bfChinese, 12, Font.NORMAL);
Paragraph pragraph=new Paragraph(“你好”, FontChinese);

4.4 用itext布局加元素生成pdf

Paragraph,排版布局。
Itext的com.itextpdf.text.Paragraph 类是段落的处理。在一个段落中,你可以设置段落的对齐方式,缩进和间距。
PdfWriter.getInstance(document, response.getOutputStream());  
document.open();  
//设置字体  
BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);     
com.itextpdf.text.Font FontChinese20 = new com.itextpdf.text.Font(bfChinese, 20, com.itextpdf.text.Font.BOLD);  
com.itextpdf.text.Font FontChinese18 = new com.itextpdf.text.Font(bfChinese, 18, com.itextpdf.text.Font.BOLD);   
com.itextpdf.text.Font FontChinese16 = new com.itextpdf.text.Font(bfChinese, 16, com.itextpdf.text.Font.BOLD);  
com.itextpdf.text.Font FontChinese12 = new com.itextpdf.text.Font(bfChinese, 12, com.itextpdf.text.Font.NORMAL);  
com.itextpdf.text.Font FontChinese11Bold = new com.itextpdf.text.Font(bfChinese, 11, com.itextpdf.text.Font.BOLD);  
com.itextpdf.text.Font FontChinese9 = new com.itextpdf.text.Font(bfChinese, 9, com.itextpdf.text.Font.ITALIC);  
com.itextpdf.text.Font FontChinese9Normal = new com.itextpdf.text.Font(bfChinese, 9, com.itextpdf.text.Font.NORMAL);  
//table1  
PdfPTable table1 = new PdfPTable(3);  
PdfPCell cell11 = new PdfPCell(new Paragraph("大家好",FontChinese20));  
//垂直居中  
cell11.setVerticalAlignment(Element.ALIGN_MIDDLE);  
//水平居中  
cell11.setHorizontalAlignment(Element.ALIGN_CENTER);  
//去掉边框  
cell11.setBorder(0);  
Image image1 = Image.getInstance("d:/xxx1.jpg");   
Image image2 = Image.getInstance("d:/xxx2.jpg");  
//设置每列宽度比例     
int width11[] = {35,40,25};  
table1.setWidths(width11);   
table1.getDefaultCell().setBorder(0);  
table1.addCell(image1);    
table1.addCell(cell11);    
table1.addCell(image2);  
//把table加入document  
document.add(table1);  
//加入空行  
Paragraph blankRow1 = new Paragraph(18f, " ", FontChinese18);   
document.add(blankRow1);  
//可以继续增加其他table和空行来布局,很好编辑  
document.close();  

4.5集成依赖jar包

    <groupId>com.itextpdf</groupId>  
    <artifactId>itextpdf</artifactId>  
    <version>5.5.10</version>  
</dependency>  
<dependency>  
    <groupId>com.itextpdf</groupId>  
    <artifactId>itext-asian</artifactId>  
    <version>5.2.0</version>  
</dependency>  
<dependency>  
    <groupId>com.itextpdf</groupId>  
    <artifactId>kernel</artifactId>  
    <version>7.1.2</version>  
</dependency>  
<dependency>  
    <groupId>com.itextpdf</groupId>  
    <artifactId>layout</artifactId>  
    <version>7.1.2</version>  
</dependency>  

4.6核心代码

	   * @return 
	   * @throws IOException 
	   */  
private void creatProductCert(CertificateQueryVo certQueryVo,File templateFile,PdfWriter writer,String qrTempUrl){  
  
      //证书抬头内容  
      String headContent = getHeadContent(certQueryVo);  
      //证书主体内容  
      String bodyContent = getBodyContent(certQueryVo);  
      //证书内容最后一句话  
      String footContent = getFootContent(certQueryVo);  
  
      Document document = null;  
      try (PdfDocument pdfDoc = new PdfDocument(writer)) {  
          PageSize p = new PageSize(PageSize.A4.getHeight(), PageSize.A4.getWidth());  
          pdfDoc.setDefaultPageSize(p);  
          document = new Document(pdfDoc);  
          File fontFile = getFondTempFile();  
          PdfFont font = PdfFontFactory.createFont(  
                  fontFile.getPath() + ",1",  
                  BaseFont.IDENTITY_H,  
                  BaseFont.NOT_EMBEDDED);  
  
          Image tImgCover = new Image(  
                  ImageDataFactory.create(templateFile.getPath()));  
          tImgCover.scaleAbsolute(PageSize.A4.getHeight(), PageSize.A4.getWidth());  
          document.add(tImgCover.setFixedPosition(0, 0));  
  
  
          Paragraph paragraph= new Paragraph(headContent);  
          paragraph.setFirstLineIndent(0);  
          paragraph.setFont(font);  
          paragraph.setFontSize(20);  
          paragraph.setBold();  
          paragraph.setFixedPosition(65, 370, 720);  
          document.add(paragraph);  
  
          Paragraph paragraphBody= new Paragraph(bodyContent);  
          paragraphBody.setFirstLineIndent(45);  
          paragraphBody.setFont(font);  
          paragraphBody.setFontSize(20);  
          paragraphBody.setBold();  
          paragraphBody.setWidth(720);  
          paragraphBody.setRelativePosition(30, 190, 0, 0);  
          document.add(paragraphBody);  
  
          Paragraph paragraphFoot= new Paragraph(footContent);  
          paragraphFoot.setFirstLineIndent(45);  
          paragraphFoot.setFont(font);  
          paragraphFoot.setFontSize(20);  
          paragraphFoot.setBold();  
          paragraphFoot.setWidth(720);  
          paragraphFoot.setFixedPosition(65, 210, 720);  
          document.add(paragraphFoot);  
  
          //二维码图片目录  
          mkTempDir(qrPath);  
          //二维码中的链接  
          String qrTempContent = qrContent.concat(certQueryVo.getCertNum());  
          //生成二维码  
          CodeUtil.getBarCode(qrTempContent, qrTempUrl);  
          Image qrcode = new Image(  
                  ImageDataFactory.create(qrTempUrl));  
          qrcode.scaleAbsolute(80, 80);  
          document.add(qrcode.setFixedPosition(110, 125));  
  
          Paragraph paragraphCert= new Paragraph("证书编号:" + certQueryVo.getCertNum());  
          paragraphCert.setFirstLineIndent(45);  
          paragraphCert.setFont(font);  
          paragraphCert.setFontSize(12);  
          paragraphCert.setBold();  
          paragraphCert.setWidth(720);  
          paragraphCert.setFixedPosition(65, 100, 720);  
          document.add(paragraphCert);  
  
          Paragraph paragraphCertifi= new Paragraph("验证网站:" + certificateWebUrl);  
          paragraphCertifi.setFirstLineIndent(45);  
          paragraphCertifi.setFont(font);  
          paragraphCertifi.setFontSize(12);  
          paragraphCertifi.setBold();  
          paragraphCertifi.setWidth(720);  
          paragraphCertifi.setFixedPosition(65, 80, 720);  
          document.add(paragraphCertifi);  
  
          Image stampImg = new Image(  
                  ImageDataFactory.create(new URL(certQueryVo.getCertStamp())));  
          stampImg.scaleAbsolute(120, 120);  
          document.add(stampImg.setFixedPosition(600, 80));  
  
          Paragraph paragraphCertQuer= new Paragraph(certQueryVo.getOrgName());  
          paragraphCertQuer.setFirstLineIndent(45);  
          paragraphCertQuer.setFont(font);  
          paragraphCertQuer.setFontSize(16);  
          paragraphCertQuer.setBold();  
          paragraphCertQuer.setTextAlignment(TextAlignment.CENTER);  
          paragraphCertQuer.setFixedPosition(490, 100, 300);  
          document.add(paragraphCertQuer);  
  
          String date = DateUtils.numDateTurnToChinese(certQueryVo.getAwardTime());  
	          Paragraph paragraphDate = new Paragraph(date);  
	          paragraphDate.setFirstLineIndent(45);  
	          paragraphDate.setFont(font);  
	          paragraphDate.setFontSize(16);  
	          paragraphDate.setBold();  
	          paragraphDate.setTextAlignment(TextAlignment.CENTER);  
	          paragraphDate.setFixedPosition(490, 80, 300);  
	          document.add(paragraphDate);  
	  
	          document.close();  
	      } catch (Exception e) {  
	          logger.error("生成证书时异常,{}", e);  
	      } finally {  
	          try {  
	              if (writer != null) {  
	                  writer.close();  
	              }  
	              if (document != null) {  
	                  document.close();  
	              }  
	              handDeleteFile(qrTempUrl);  
	          } catch (Exception e) {  
	              logger.error("生成证书关闭writer异常,{}", e);  
	          }  
	  
	      }  
	  }