itextpdf FormField 生成pdf 文件(包含中文以及图片处理)
很常见的一个功能,基于pdf 的AcroFields 提供的模版的能力,通过数据填充生成新的pdf 文档,对于图片的处理基于
PdfContentByte (一个强大的内容处理对象)
模版制作
一般大家的做法可以直接基于word 然后导出为pdf,然后通过pdf pro 工具,制作AcroFields ,但是pdf pro 比较贵,而且
很多时候我们不需要复杂的功能,pdfescape 是一个不错的选择,https://www.pdfescape.com 具体操作可以搜相关资料
比较简单
代码使用
使用了itextpdf 5 没有使用7 版本
- 模版说明
我已经制作好了一个测试模版,AcroFields 自己的信息为 year,makrs,d,logo (图片的)
模版内容
- 项目结构
- pom.xml
说明引入itext-asian 主要是解决字体语言的问题
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dalongdemoapp</groupId>
<artifactId>pdf</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<encoding>UTF-8</encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
</project>
- 代码说明
Application.java
package com.dalong;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
@author dalong
*/
public class Application {
public static void export(){
try {
String fileName = "src/main/resources/授课时间-image.pdf";
String imageFile = "src/main/resources/zeebe.png";
PdfReader reader = new PdfReader(fileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PdfStamper ps = new PdfStamper(reader, bos);
// 中文字体问题
BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
fontList.add(bf);
AcroFields fields = ps.getAcroFields();
fields.setSubstitutionFonts(fontList);
fillData(fields, data());
// 添加图片
addImage(ps,fields,"logo",imageFile);
ps.setFormFlattening(true);
ps.close();
//生成pdf路径存放的路径
OutputStream fos = new FileOutputStream("result.pdf");
fos.write(bos.toByteArray());
fos.flush();
fos.close();
bos.close();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 填充模板中的数据
*/
public static void fillData(AcroFields fields, Map<String, String> data) {
try {
for (String key : data.keySet()) {
String value = data.get(key);
fields.setField(key, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void addImage(PdfStamper stamper,AcroFields form,String field,String fieldValue){
try{
java.util.List<AcroFields.FieldPosition> photograph = form.getFieldPositions(field);
if(photograph!=null && photograph.size()>0){
Rectangle rect= photograph.get(0).position;
Image img = Image.getInstance(fieldValue);
img.scaleToFit(rect.getWidth(), rect.getHeight());
img.setBorder(2);
img.setAbsolutePosition(
photograph.get(0).position.getLeft() + (rect.getWidth() - img.getScaledWidth() )
, photograph.get(0).position.getTop() - (rect.getHeight()));
PdfContentByte cb = stamper.getOverContent((int)photograph.get(0).page);
cb.addImage(img);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 填充数据源
* 其中data存放的key值与pdf模板中的文本域值相对应
*/
public static Map<String, String> data() {
Map<String, String> data = new HashMap<String, String>();
data.put("year", "2020");
data.put("marks","摘要:这个本来属于s3 的特性,但是我们在实际使用的过程中肯定不想别人直接可以通过浏览器或者http就可以可以我们的文件内容 这个属于安全的控制,以下是一个实践以及一些安全控制 一些原则 不能直接暴露minio 访问到公网环境(可以基于nginx,以及反向代理工具解决) 配置合理的bucket 策略,可以 阅读全文\n");
data.put("d","因为当前大家主流的还是基于前后端分离的模式开发软件,组件+api 实现功能,但是很多时候好多租户对于功能有个性化需求,但是\n" +
"系统在设计的时候因为时间问题+早期设计问题造成业务扩展能力有点差,还需要支持个性化需求开发,所以我们可以拆分标准版本\n" +
"以及自定型版本,同时基于minio 提供的s3 管理模式,对于不同的租户创建不同的bucket,标准的使用标准版,这样客户化开发就很简单\n" +
"了(特殊场景需要个性化),此方案的缺点也比较明确:空间的占用,但是还好因为前后端分离的模式。每个租户占用的静态资源也不是\n" +
"很大,核心问题是在系统更新的时候,我们可能需要引导客户自己升级或者基于强大的ci/cd 系统进行所有租户的系统升级(构建包的处理)\n" +
"个人感觉好处也是很明显的,如果我们的api 以及website 已经做了比较好的版本管理,用户切换版本也就是静态资源的替换(直接基于s3 bucket)。\n" +
"saas 应用的功能主要基于单页面的模式开发的应用,在s3 中的存储就是css,js 以及静态html 页面。这样管理起来也是比较灵活的");
return data;
}
public static void main(String[] args) {
export();
}
}
- 生成的效果
模版字段替换
图片替换
说明
代码很简单,但是此功能还是一个比较常见的需求,还有有点用的
参考资料
https://www.pdfescape.com/
https://github.com/rongfengliang/itextpdf-image-learning