itext是用来处理pdf内容的开源工具(商业使用需要收费),可以用来根据模板生成pdf。
简介
- Github参见这里,官网参见这里,帮助文档上有一些例子,另外如果有疑问可以在知识库的搜索框里输入,有可能会找到对应的答案(知识库中的一些问题来自stackoverflow上的解答)。
- itext分为若干个组件,有的用于ocr,有的用于绘制pdf,有的用于html转换pdf,等等
- 这次要做的是利用itextpdf,利用调整好的html,转换成指定格式的pdf
上手代码
首先需要在pom中引入itextpdf的依赖如下
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<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>${com.itextpdf.html2pdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>forms</artifactId>
<version>${com.itextpdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>svg</artifactId>
<version>${com.itextpdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>styled-xml-parser</artifactId>
<version>${com.itextpdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>${com.itextpdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>${com.itextpdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>${com.itextpdf.version}</version>
</dependency>
itext对于中文的支持,需要引入itext-asian包或者引入中文字体;这里通过引入微软雅黑字体解决:
1
2
3
4
5
6
7ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.addStandardPdfFonts();
// .ttf 字体所在目录
String resources = Html2PdfUtil.class.getResource(FONT_RESOURCE_DIR).getPath();
fp.addDirectory(resources);
props.setFontProvider(fp);由于需要使用html转换为pdf,同时对于部分章节需要另起一页,所以使用了如下方法,其中PAGE_SIZE为定义好的页面大小,可以设置为PAGE_SIZE.A4等等;
1
2
3
4//html转成元素
List<IElement> elements = HtmlConverter.convertToElements(htmlContent, props);
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
Document document = new Document(pdf, PAGE_SIZE, false);由于需要为每一页添加页眉页脚,参考这个问题,增加对应的handler,用于处理页眉页脚:
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/**
* 添加页眉页脚文字与分割线
* pdf绘制时坐标可以参考平面直角坐标系的第一象限
*/
private static class HeaderFooterHandler implements IEventHandler {
protected String info;
public void setInfo(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfDocument pdfDoc = ((PdfDocumentEvent) event).getDocument();
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(),page.getResources(), pdfDoc);
// 画页脚分割线
// MARGIN_LEFT为左侧留白,FOOTER_LINE_BOTTOM_DISTANCE为页脚线到底部距离,MARGIN_RIGHT为右侧留白
pdfCanvas.setStrokeColor(ColorConstants.GRAY) .moveTo(MARGIN_LEFT, FOOTER_LINE_BOTTOM_DISTANCE).lineTo(PAGE_SIZE.getWidth()- MARGIN_RIGHT, FOOTER_LINE_BOTTOM_DISTANCE).closePathStroke();
// 画页眉分割线
// HEADER_LINE_TOP_DISTANCE为页眉线到顶部距离
pdfCanvas.setStrokeColor(ColorConstants.GRAY).moveTo(MARGIN_LEFT, pageSize.getHeight() - HEADER_LINE_TOP_DISTANCE).lineTo(PAGE_SIZE.getWidth()- MARGIN_RIGHT, pageSize.getHeight() - HEADER_LINE_TOP_DISTANCE).closePathStroke();
try {
// 加载字体
PdfFont YaHeiFont = PdfFontFactory.createFont("/font/WeiRuanYaHei-1.ttf", PdfEncodings.IDENTITY_H,true);
//header
float headerFontSize = 14;
String headerTextContent = "页眉文字";
int headerTextLength = headerTextContent.length();
// 这个Rectangle用于定位,定位点在矩形左下角
// x坐标:长方形左侧边线
// y坐标:长方形底部边线
// MARGIN_RIGHT为右侧留白
// HEADER_PADDING_BOTTOM为页眉文字到页眉线留白
Rectangle headerRightPosition = new Rectangle((float) (PAGE_SIZE.getWidth() - MARGIN_RIGHT - Math.ceil(headerFontSize * headerTextLength)),
pageSize.getHeight() - (HEADER_LINE_TOP_DISTANCE- (0.5f * headerFontSize)) + HEADER_PADDING_BOTTOM , headerFontSize * headerTextLength, headerFontSize);
Canvas headerRightCanvas = new Canvas(pdfCanvas, pdfDoc, headerRightPosition);
// 设置下字体
Text headerText = new Text(headerTextContent).setFont(YaHeiFont).setFontSize(headerFontSize).setFontColor(ColorConstants.GRAY);
Paragraph headerParagraph = new Paragraph().add(headerText).setMarginRight(0);
headerRightCanvas.add(headerParagraph);
//footer
// 页码
float footerFontSize = 12;
String pageNumTextContent = "P" + pdfDoc.getPageNumber(page);
int pageNumTextLength = pageNumTextContent.length();
// x坐标:左侧margin
// y坐标:页脚线到页面底部-字体占位高度
Rectangle pageNumPosition =
new Rectangle(MARGIN_LEFT, FOOTER_LINE_BOTTOM_DISTANCE - footerFontSize,footerFontSize * pageNumTextLength, footerFontSize);
Canvas pageNumCanvas = new Canvas(pdfCanvas, pdfDoc, pageNumPosition);
Text pageNumText = new Text(pageNumTextContent).setFont(YaHeiFont).setFontSize(footerFontSize).setFontColor(ColorConstants.GRAY);
Paragraph pageNumParagraph = new Paragraph().add(pageNumText).setMarginLeft(0);
pageNumCanvas.add(pageNumParagraph);
// 页脚文字
int footerTextLength = info.length();
// x坐标:页面宽度-右侧margin-字体占位宽度
// y坐标:页脚线到页面底部-字体占位高度
Rectangle footerRightPosition = new Rectangle(PAGE_SIZE.getWidth() - MARGIN_RIGHT - footerFontSize*footerTextLength,
FOOTER_LINE_BOTTOM_DISTANCE - footerFontSize, footerTextLength * footerFontSize, footerFontSize);
Canvas footerRightCanvas = new Canvas(pdfCanvas, pdfDoc, footerRightPosition);
footerRightCanvas.setFont(YaHeiFont);
Text footerText = new Text(info).setFont(YaHeiFont).setFontColor(ColorConstants.GRAY).setFontSize(footerFontSize);
Paragraph footerTextParagraph = new Paragraph().add(footerText).setMarginRight(0).setTextAlignment(TextAlignment.RIGHT);
footerRightCanvas.add(footerTextParagraph);
}
catch (Exception e){
e.printStackTrace();
}
}
}同理,可以为每一页添加背景图片:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* 添加背景图片
*/
private static class BackgroundEventHandler implements IEventHandler {
public static final String BACKGROUND_IMAGE = Html2PdfUtil.class.getResource("/pdfTemplate").getPath() + "/background.png";
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdfDoc = docEvent.getDocument();
PdfPage page = docEvent.getPage();
try {
// 添加背景图片
Image backgroundImage = new Image(ImageDataFactory.create(BACKGROUND_IMAGE));
PdfCanvas background = new PdfCanvas(page.newContentStreamBefore(),
page.getResources(), pdfDoc);
Rectangle areaBackground = page.getPageSize();
new Canvas(background, pdfDoc, areaBackground)
.add(backgroundImage);
} catch (Exception e){
e.printStackTrace();
}
}
}完整的生成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/**
* 保存风险评述,生成pdf方法
* @param htmlContent html文本
* @param dest 目的文件路径,如 /xxx/xxx.pdf
* @throws IOException IO异常
*/
public static void createPdf(String htmlContent, String dest) throws IOException {
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.addStandardPdfFonts();
// .ttf 字体所在目录
String resources = Html2PdfUtil.class.getResource(FONT_RESOURCE_DIR).getPath();
fp.addDirectory(resources);
props.setFontProvider(fp);
//html转成元素
List<IElement> elements = HtmlConverter.convertToElements(htmlContent, props);
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
Document document = new Document(pdf, PAGE_SIZE, false);
//设置下留白
document.setMargins(MARGIN_TOP, MARGIN_LEFT, MARGIN_BOTTOM, MARGIN_RIGHT);
//背景图片处理器
BackgroundEventHandler backgroundEventHandler = new BackgroundEventHandler();
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, backgroundEventHandler);
//页眉页脚分割线与文字处理器
HeaderFooterHandler handler = new HeaderFooterHandler();
handler.setInfo("页脚文字");
pdf.addEventHandler(PdfDocumentEvent.START_PAGE, handler);
//添加分页符,此处用于在指定的元素位置添加分页符,非必须
for (IElement element : elements) {
if (element instanceof Div) {
Div div = (Div) element;
List<IElement> children = div.getChildren();
children.add(2,new AreaBreak());
}
}
for (IElement element : elements) {
if (element instanceof HtmlPageBreak) {
// 分页符
document.add((HtmlPageBreak) element);
} else if (element instanceof Paragraph) {
//段落,这里可以统一设置文字对齐
Paragraph p = (Paragraph) element;
p.setTextAlignment(TextAlignment.JUSTIFIED);
document.add(p);
} else if (element instanceof Div) {
//块
Div div = (Div) element;
document.add(div);
} else if (element instanceof com.itextpdf.layout.element.List) {
//列表
com.itextpdf.layout.element.List list = (com.itextpdf.layout.element.List) element;
document.add(list);
} else {
document.add((IBlockElement) element);
}
}
document.close();
}
总结
调试下来,发现最有用的就是官网知识库还有源码,官网知识库能够解决怎么做的问题,对于一些细节的调整,可以查看源码并调试,就能得到想要的结果。