根据pdf模板生成新的PDF文件

需求

当前有一堆pdf(word)文档。但是里面会有一些位置需要通过程序动态的将数据写入。

可以理解为 用户查看某一个pdf模板,然后填写自己的信息。提交之后,将这些信息动态的写入刚才那个模板之中,生成属于自己的一份PDF文件。

准备

这里采用的是 itext来实现

  1. 制作pdf模板(假设从word文件开始)

  1. 使用adobe acrobat dc 这个软件。对于当前word文件 另存为 adobe pdf。这样就成了 pdf文件

  1. 定制程序中需要使用到的 占位符。点击 准备表单。

实现

取当前模板,生成一份新的PDF文件,里面会涉及到 将占位符填充,增加水印,设置密码。

核心处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
private static void doCommon(String from, OutputStream out, Map<String, Object> data) {
PdfReader reader;
ByteArrayOutputStream bos;
PdfStamper stamper;
Document doc = null;
try {
reader = new PdfReader(from);//读取pdf模板
bos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, bos);
AcroFields form = stamper.getAcroFields();

// 遍历data 给pdf表单表格赋值
for (String key : data.keySet()) {
if (data.get(key) != null) {
form.setField(key, data.get(key).toString());
}
}

// 增加图片水印
BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
PdfGState gs = new PdfGState();
int total = reader.getNumberOfPages() + 1;
for (int i = 1; i < total; i++) {
PdfContentByte content = stamper.getOverContent(i);
gs.setFillOpacity(0.8f);
content.setGState(gs);
content.beginText();
content.setColorFill(Color.LIGHT_GRAY);
content.setFontAndSize(base, 40);
content.setTextMatrix(70, 200);
Rectangle pageRect = stamper.getReader().getPageSizeWithRotation(i);
// 计算水印X,Y坐标
float x = pageRect.getWidth() / 2;
float y = pageRect.getHeight() / 2;
content.showTextAligned(Element.ALIGN_CENTER, "非正式文件,请勿下载打印!", x, y, 40);
for (int j = 80; j <= 160; j += 80) {
content.showTextAligned(Element.ALIGN_CENTER, "非正式文件,请勿下载打印!", x, y - j, 40);
content.showTextAligned(Element.ALIGN_CENTER, "非正式文件,请勿下载打印!", x, y + j, 40);
}
content.endText();
}
stamper.setFormFlattening(true);
stamper.close();
doc = new Document();
PdfCopy copy = new PdfCopy(doc, out);
// 设置文件为只读模式
String md5 = "test111";
copy.setEncryption(null, md5.getBytes(), PdfWriter.ALLOW_PRINTING, PdfWriter.STANDARD_ENCRYPTION_128);
doc.open();
// 获取总页数
int pages = reader.getNumberOfPages();
// 这一句话 一定要在循环外面。否则生成的文件会是现有文件的【pages】倍
PdfReader pdfReader = new PdfReader(bos.toByteArray());
for (int i = 1; i <= pages; i++) {
PdfImportedPage importPage = copy.getImportedPage(pdfReader, i);
copy.addPage(importPage);
}
} catch (DocumentException | IOException e) {
e.printStackTrace();
} finally {
if (doc != null) {
doc.close();
}
}
}

对外提供方法

1
2
3
4
5
6
7
8
public static void fillDataToFile(String from, String out, UserInfo userInfo) {
try {
Map<String, Object> userInfoMap = buildUserInfo(userInfo);
doCommon(from, new FileOutputStream(out), userInfoMap);
} catch (IOException e) {
e.printStackTrace();
}
}

填充数据

1
2
3
4
5
6
7
8
9
10
private static Map<String, Object> buildUserInfo(UserInfo userInfo) {
Map<String, Object> map = Maps.newHashMap();
map.put("name",userInfo.getName());
map.put("age",userInfo.getAge());
map.put("sex",userInfo.getSex());
map.put("love",userInfo.getLove());
map.put("job",userInfo.getJob());
map.put("mobile",userInfo.getMobile());
return map;
}

测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void fillDataToFileTest() throws IOException {
UserInfo userInfo = UserInfo.builder()
.name("isbing")
.age(24)
.sex("man")
.love("coding")
.job("programmer")
.mobile("1761011")
.build();
String from = "D:\\pdf\\tpl\\test.pdf";
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String fileName = "test_"+df.format(new Date())+".pdf";
String out = "D:\\pdf" + File.separator + fileName;
PdfUtils.fillDataToFile(from,out,userInfo);
}

效果

注意:itext版本需要使用 2.1.7的。

5.0+的商用 需要收费。但是2.1.7的itextasian maven远程库没有。。。