Java动态生成PDF-itext

简述

因为项目需求-需要向特殊身份的用户展示其他用户填写的各个模块数据信息,为了快速开发且清晰便捷,选择生成PDF是一个比较好的方式,所以简单学习了一下怎么生成一份自定义的动态PDF。

选择了itext类库,因为比较出名,学习成本低,方便项目组快速开发。

找到了一篇博客进行入手练习:

http://rensanning.iteye.com/blog/1538689

推荐快速的码一遍所有功能代码,就可以着手自己写工具类了。

因为一些敏感信息的原因,所以删掉或隐藏了很多有可能暴露信息的代码部分,如果真的有人看到此博客且因此原因造成困扰的,博主在这里表示歉意。

可以通过我博客里的一些公开的个人信息联系我,本人菜鸟,欢迎讨论和指正

引入资源

pom.xml中引入如下资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- PDF -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.lowagie/itext -->
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>

编写工具类

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
import com.itextpdf.text.*;
import com.itextpdf.text.html.simpleparser.HTMLWorker;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.draw.DottedLineSeparator;
import com.itextpdf.text.pdf.draw.LineSeparator;
import com.itextpdf.text.pdf.draw.VerticalPositionMark;
import com.lai.counselorManagement.sqlserver.entity.*;
import com.lai.counselorManagement.sqlserver.entity.ado.*;
import org.apache.log4j.Logger;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.util.Map;
import java.util.TreeMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
* @program: XXX
* @description: PDF工具类
* @author: XXX
* @create: 2018-11-08
*/
public class PdfUtil {
private Logger logger = Logger.getLogger(PdfUtil.class);
private String path;
private Document document;
private Font textFont;
private Font boldFont;
private Font mainTitleFont;
private Font firstTitleFont;
private Font secondTitleFont;
private Font underlineFont;
private PdfWriter pdfWriter;
private PdfReader pdfReader;
private PdfStamper pdfStamper;
private OutputStream outputStream;

/**
* 初始化
* @throws IOException
*/
public void init(OutputStream outputStream,Document document)throws Exception{
logger.info("初始化工具类");
this.document = document;
this.outputStream = outputStream;
//添加中文字体
BaseFont bfChinese = BaseFont.createFont(DataUtil.getFontPathSimkai(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
//设置字体样式
this.textFont = new Font(bfChinese,12,Font.NORMAL);//正文
this.boldFont = new Font(bfChinese,11,Font.BOLD);//加粗
this.mainTitleFont = new Font(bfChinese,30,Font.BOLD);//总标题
this.firstTitleFont = new Font(bfChinese,22,Font.BOLD);//一级标题
this.secondTitleFont = new Font(bfChinese,15,Font.BOLD);//二级标题
this.underlineFont = new Font(bfChinese,11,Font.UNDERLINE);//下划线斜体

//配置输出流
this.pdfWriter = PdfWriter.getInstance(this.document,this.outputStream);
this.pdfWriter.setViewerPreferences(PdfWriter.PageModeUseThumbs);

//文档属性
this.document.addTitle("XXX");
this.document.addCreator("XXX");

//页边空白
this.document.setMargins(10, 20, 30, 40);
}


//创建空白内容PDF-当整体数据未填写时提示
public void createPDFToEmpty(String id)throws Exception{
this.document.open();
this.document.newPage();
drawLine(0);//画线
//提示内容
Paragraph paragraph = new Paragraph("XXX",this.mainTitleFont);
paragraph.setAlignment(Element.ALIGN_CENTER);
paragraph.setAlignment(Element.ALIGN_MIDDLE);
this.document.add(paragraph);
this.document.close();
turnToReadForChange(outputToInput(this.outputStream),outputStream);
addWaterMark(id);//添加水印
closeRead();
}

//创建错误提示PDF-当一些资源文件等缺失时提示
public void createPDFToError(String id)throws Exception{
this.document.open();
this.document.newPage();
drawLine(0);//画线
//提示内容
Paragraph paragraph = new Paragraph(" 抱歉,因为数据缺失等原因,未能正确的生成PDF,请联系管理员,我们会尽快为您解决问题。",this.mainTitleFont);
paragraph.setAlignment(Element.ALIGN_CENTER);
paragraph.setAlignment(Element.ALIGN_MIDDLE);
this.document.add(paragraph);
this.document.close();
turnToReadForChange(outputToInput(this.outputStream),outputStream);
addWaterMark(id);//添加水印
closeRead();
}

private ByteArrayInputStream outputToInput(OutputStream outputStream){
ByteArrayOutputStream byteArrayOutputStream = (ByteArrayOutputStream) outputStream;
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
}

public void createPDFToPlan(…省略数据)throws Exception{
this.document.open();
this.document.newPage();
//首页
drawLine(0);
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
addParagraph("XXX工作手册",4,this.mainTitleFont,"");
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
this.document.add(new Paragraph("\n"));
addParagraph("XXX: " + [XXX],4,this.secondTitleFont,"");
……

//模块A表
logger.info("A表");
this.document.newPage();
addParagraph("A表",4,this.firstTitleFont,"1");
for([A] a : as){
//数据处理
……
//附件-图片
String[] picPaths = new String[0];
if(a.getAccessoryPath() != null)
picPaths = a.getAccessoryPath().split(",");
for(int i = 0;i < picPaths.length;i++){
if(!picPaths[i].isEmpty())
picPaths[i] = DataUtil.getPathOfPicture() + "/" + picPaths[i];
}
add[A]Table(……,picPaths);
}

//B表
logger.info("B表");
this.document.newPage();
addParagraph("B表",4,this.firstTitleFont,"2");
for([B] b : bs){
//数据处理
//附件
String[] picPaths = new String[0];
if(b.getAccessoryPath() != null)
picPaths = b.getAccessoryPath().split(",");
for(int i = 0;i < picPaths.length;i++){
if(!picPaths[i].isEmpty())
picPaths[i] = DataUtil.getPathOfPicture() + "/" + picPaths[i];
}
add[B]Table(……,picPaths);
}

//总结
logger.info("XXX总结");
this.document.newPage();
addParagraph("XX总结",4,this.firstTitleFont,"18");
String[] picPaths = new String[0];
if(c != null){
//附件
if(c.getAccessoryPath() != null)
picPaths = c.getAccessoryPath().split(",");
for(int i = 0;i < picPaths.length;i++){
if(!picPaths[i].isEmpty())
picPaths[i] = DataUtil.getPathOfPicture() + "/" + picPaths[i];
}
add[C]Table(……, picPaths);
} else
add[C]Table(
"", "","", picPaths);
//添加目录
addCatalog();
this.document.close();
turnToReadForChange(outputToInput(this.outputStream),outputStream);
//添加水印
addWaterMark(instructorId);
closeRead();
}

//开启读取器,为了修改pdf
private void turnToReadForChange(InputStream inputStream,OutputStream outputStream)throws Exception{
this.pdfReader = new PdfReader(inputStream);
this.pdfStamper = new PdfStamper(this.pdfReader,outputStream);
}

//关闭读取
private void closeRead()throws Exception{
this.pdfStamper.close();
this.pdfReader.close();
}

//添加水印
private void addWaterMark(String id)throws Exception{
//配置图片水印
//addImageWaterMark(waterMarkPath);
//配置文字水印
addTextWaterMark(DataUtil.getWaterMarkName()+id);
//配置背景图
//addBackgroundImage(bgImgPath);
}

//添加Paragraph对象(段落)
private void addParagraph(String content,int code,Font font,String localDestination)throws Exception{
Paragraph paragraph = new Paragraph();
Chunk chunk = new Chunk(content,font);
if(!localDestination.isEmpty())
chunk.setLocalDestination(localDestination);//标记目录
paragraph.add(chunk);
paragraph.add(Chunk.NEWLINE);
switch (code){
case 0:
//默认
paragraph.setAlignment(Element.ALIGN_JUSTIFIED);
this.document.add(paragraph);
break;
case 1:
paragraph.setAlignment(Element.ALIGN_JUSTIFIED);
paragraph.setIndentationLeft(1 * 30f);
paragraph.setIndentationRight(1 * 30f);
this.document.add(paragraph);
break;
case 2:
//居右
paragraph.setAlignment(Element.ALIGN_RIGHT);
paragraph.setSpacingAfter(15f);
this.document.add(paragraph);
break;
case 3:
//居左
paragraph.setAlignment(Element.ALIGN_LEFT);
paragraph.setSpacingBefore(15f);
this.document.add(paragraph);
break;
case 4://居中
paragraph.setAlignment(Element.ALIGN_CENTER);
paragraph.setSpacingAfter(15f);
paragraph.setSpacingBefore(15f);
this.document.add(paragraph);
}
}

//画图
private void drawLine(int code)throws Exception{
switch (code){
case 0:
//直线
Paragraph p1 = new Paragraph(new Chunk(new LineSeparator()));
this.document.add(p1);
break;
case 1:
//点线
Paragraph p2 = new Paragraph(new Chunk(new DottedLineSeparator()));
this.document.add(p2);
break;
case 2:
//下滑线
LineSeparator UNDERLINE = new LineSeparator(1, 100, null, Element.ALIGN_CENTER, -2);
Paragraph p3 = new Paragraph("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN");
p3.add(UNDERLINE);
this.document.add(p3);
break;
}
}

//添加表格单元
private void addCellToTable(PdfPTable table,String value,Font font,int alignment,int colspan,int rowspan){
PdfPCell cell = new PdfPCell(new Phrase(value,font));
cell.setPaddingTop(10f);
cell.setPaddingBottom(10f);
if(alignment == 0){//单元格居中
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
}else if(alignment == 1){//默认
}
if(colspan > 0)
cell.setColspan(colspan);
if(rowspan > 0)
cell.setRowspan(rowspan);
table.addCell(cell);
}

//添加表格单元
private void addCellToTable(PdfPTable table,String value,Font font,int alignment){
PdfPCell cell = new PdfPCell(new Phrase(value,font));
cell.setPaddingTop(10f);
cell.setPaddingBottom(10f);
if(alignment == 0){
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
}
table.addCell(cell);
}

//添加图片到表格
private void addImgToTable(PdfPTable table,String[] picPaths,int colspan)throws Exception{
for(String picPath : picPaths){
if(!picPath.isEmpty()){
//插入图片
Image image = Image.getInstance(picPath);
PdfPCell cell = new PdfPCell(image,true);//调整图片大小
cell.setColspan(colspan);
cell.setPaddingTop(10f);
cell.setPaddingBottom(10f);
cell.setPaddingLeft(10f);
cell.setPaddingRight(10f);
table.addCell(cell);
}
}
}

//添加A表格
private void add[A]Table(……, String[] picPaths)throws Exception{
//按比例设置单元格宽度
float[] widths = {0.20f, 0.20f, 0.1f, 0.1f, 0.20f, 0.20f};
PdfPTable table = new PdfPTable(widths);
table.setPaddingTop(20f);
table.setHorizontalAlignment(Element.ALIGN_CENTER);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0,3,0);

addCellToTable(table,"XXX",boldFont,0,3,0);
addCellToTable(table,XXX,textFont,0,3,0);

addCellToTable(table,"XXX\n" + "XXX",boldFont,0);
addCellToTable(table,XXX,textFont,1,5,0);

addCellToTable(table,"附件",boldFont,0,0,picPaths.length);
addImgToTable(table,picPaths,5);
table.setSpacingAfter(20f);
document.add(table);
}

//添加B表
private void add[B]Table(……, String[] picPaths)throws Exception{
//按比例设置单元格宽度
float[] widths = {0.15f, 0.15f, 0.2f, 0.2f, 0.15f, 0.15f};
PdfPTable table = new PdfPTable(widths);
table.setHorizontalAlignment(Element.ALIGN_CENTER);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,0,3,0);

addCellToTable(table,"XXX\n" + "(XX)",boldFont,0);
addCellToTable(table,XXX,textFont,1,5,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,1,5,0);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,1,5,0);

addCellToTable(table,"附件",boldFont,0,0,picPaths.length);
addImgToTable(table,picPaths,5);
table.setSpacingAfter(20f);
document.add(table);
}

……

//XXX总结
private void add[C]Table(
……,String[] picPaths)throws Exception{
//按比例设置单元格宽度
float[] widths = {0.2f, 0.8f};
PdfPTable table = new PdfPTable(widths);
table.setHorizontalAlignment(Element.ALIGN_CENTER);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,1);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,1);

addCellToTable(table,"XXX",boldFont,0);
addCellToTable(table,XXX,textFont,1);

addCellToTable(table,"附件",boldFont,0,0,picPaths.length);
addImgToTable(table,picPaths,3);
table.setSpacingAfter(20f);
document.add(table);
}

//配置图片水印
private void addImageWaterMark(String waterMarkPath)throws Exception{
Image image = Image.getInstance(waterMarkPath);
image.setAbsolutePosition(200, 400);
PdfContentByte under = this.pdfStamper.getUnderContent(this.pdfReader.getNumberOfPages());
under.addImage(image);
}

//配置文字水印
private void addTextWaterMark(String waterMarkName)throws Exception{
PdfContentByte under;
int j = waterMarkName.length();
int total = this.pdfReader.getNumberOfPages()+1;
BaseFont base = BaseFont.createFont(DataUtil.getFontPathSimkai(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
float diaphaneityF = 0.2f;// 转换透明度(默认为20%)
PdfGState gs = new PdfGState();
for (int i = 1; i < total; i++) {
under = this.pdfStamper.getUnderContent(i);
under.beginText();
under.setFontAndSize(base, 66);
under.setColorFill(BaseColor.GRAY);
// 设置透明度
gs.setFillOpacity(diaphaneityF);
gs.setStrokeOpacity(diaphaneityF);
under.setGState(gs);
under.showTextAligned(Element.ALIGN_CENTER,waterMarkName,300,400,315);
// 添加水印文字
under.endText();
}
}

//添加目录
private void addCatalog()throws Exception{
PdfContentByte cb = this.pdfWriter.getDirectContent();
PdfOutline root = cb.getRootOutline();
PdfOutline oline1 = new PdfOutline(root, PdfAction.gotoLocalPage("1", false), "1.A表");
PdfOutline oline2 = new PdfOutline(root, PdfAction.gotoLocalPage("2", false), "2.B表");
……
PdfOutline oline18 = new PdfOutline(root, PdfAction.gotoLocalPage("18", false), "18.XXX总结");
}

//配置背景图
private void addBackgroundImage(String bgImgPath)throws Exception{
Image img = Image.getInstance(bgImgPath);
img.setAbsolutePosition(0, 0);
PdfContentByte under2 = this.pdfStamper.getUnderContent(this.pdfReader.getNumberOfPages());
under2.addImage(img);
}

……

}