使用itext生成pdf
itext是用来处理pdf内容的开源工具(商业使用需要收费),可以用来根据模板生成pdf。
简介
- Github参见这里,官网参见这里,帮助文档上有一些例子,另外如果有疑问可以在知识库的搜索框里输入,有可能会找到对应的答案(知识库中的一些问题来自stackoverflow上的解答)。
- itext分为若干个组件,有的用于ocr,有的用于绘制pdf,有的用于html转换pdf,等等
- 这次要做的是利用itextpdf,利用调整好的html,转换成指定格式的pdf
上手代码
-
首先需要在pom中引入itextpdf的依赖如下
<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包或者引入中文字体;这里通过引入微软雅黑字体解决:
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转换为pdf,同时对于部分章节需要另起一页,所以使用了如下方法,其中PAGE_SIZE为定义好的页面大小,可以设置为PAGE_SIZE.A4等等;
//html转成元素 List<IElement> elements = HtmlConverter.convertToElements(htmlContent, props); PdfDocument pdf = new PdfDocument(new PdfWriter(dest)); Document document = new Document(pdf, PAGE_SIZE, false);
-
由于需要为每一页添加页眉页脚,参考这个问题,增加对应的handler,用于处理页眉页脚:
/** * 添加页眉页脚文字与分割线 * 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(); } } }
-
同理,可以为每一页添加背景图片:
/** * 添加背景图片 */ 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方法如下:
/** * 保存风险评述,生成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(); }
总结
调试下来,发现最有用的就是官网知识库还有源码,官网知识库能够解决怎么做的问题,对于一些细节的调整,可以查看源码并调试,就能得到想要的结果。