前言
我们之前使用js库html2canvas + jspdf实现html转PDF、图片,并下载(详情请戳:html页面转PDF、图片操作记录),大致原理是将页面塞到画布里,以图片的方式放到PDF中,生成的文件比较大,文本记录Java使用iText7生成PDF
iText 7是iText强大的PDF工具包的最新版本,用于PDF生成,PDF编程,处理和操作,如数字签名等
官方文档:https://kb.itextpdf.com/home/it7kb/ebooks
简单生成PDF
官方文档:https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-jump-start-tutorial-for-java
根据文档说明,我们引入依赖
<properties> <itext7.version>7.1.7itext7.version> properties> <dependencies> <dependency> <groupId>com.itextpdfgroupId> <artifactId>kernelartifactId> <version>${itext7.version}version> dependency> <dependency> <groupId>com.itextpdfgroupId> <artifactId>ioartifactId> <version>${itext7.version}version> dependency> <dependency> <groupId>com.itextpdfgroupId> <artifactId>layoutartifactId> <version>${itext7.version}version> dependency> <dependency> <groupId>com.itextpdfgroupId> <artifactId>formsartifactId> <version>${itext7.version}version> dependency> <dependency> <groupId>com.itextpdfgroupId> <artifactId>pdfaartifactId> <version>${itext7.version}version> dependency> dependencies>
代码
package cn.huanzi.qch.util; import com.itextpdf.html2pdf.ConverterProperties; import com.itextpdf.html2pdf.HtmlConverter; import com.itextpdf.io.font.PdfEncodings; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.colors.Color; import com.itextpdf.kernel.colors.DeviceRgb; import com.itextpdf.kernel.events.Event; import com.itextpdf.kernel.events.IEventHandler; import com.itextpdf.kernel.events.PdfDocumentEvent; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfPage; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.action.PdfAction; import com.itextpdf.kernel.pdf.annot.PdfLinkAnnotation; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import com.itextpdf.layout.Canvas; import com.itextpdf.layout.Document; import com.itextpdf.layout.Style; import com.itextpdf.layout.element.*; import com.itextpdf.layout.font.FontProvider; import com.itextpdf.layout.property.TextAlignment; import com.itextpdf.layout.property.VerticalAlignment; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; /** * itextpdf 工具类 * iText 7是iText强大的PDF工具包的最新版本,用于PDF生成,PDF编程,处理和操作,如数字签名等。 * https://kb.itextpdf.com/home/it7kb/ebooks */ public class ITextPdfUtil { //字体,我这里使用系统自带的simhei黑体 private static final String FONT = "C:/Windows/Fonts/simhei.ttf"; //html转pdf public static void html2pdf(){ String html = ""; //从html文件读取内容 StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new FileReader("E:\\Java\\html2pdf.html"));){ for (Object o : reader.lines().toArray()) { stringBuilder.append(o); } }catch (Exception e){ e.printStackTrace(); } html = stringBuilder.toString(); try (PdfWriter writer = new PdfWriter("E:\\Java\\html2pdf.pdf"); PdfDocument pdf = new PdfDocument(writer); ){ //转换器属性设置 ConverterProperties props = new ConverterProperties(); //字体 props.setFontProvider(new FontProvider()); props.getFontProvider().addFont(ITextPdfUtil.FONT); //为img图片配置基础路径 props.setBaseUri("D:\\XFT User\\Pictures\\"); //HtmlConverter.convertToDocument Document document = HtmlConverter.convertToDocument(html, pdf, props); //设置文档属性 pdf.getDocumentInfo().setAuthor("huanzi-qch"); pdf.getDocumentInfo().setTitle("IText测试html2pdf"); pdf.getDocumentInfo().setSubject("XXX公司"); pdf.getDocumentInfo().setMoreInfo("1","111"); pdf.getDocumentInfo().setCreator("huanzi"); pdf.getDocumentInfo().setKeywords("IText"); //注册事件监听 pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new MyEventHandler()); //设置字体 document.setFont(ITextPdfUtil.getPdfFont()); //页边距 document.setMargins(0, 0, 0, 0); document.close(); System.out.println("操作完成!"); }catch (IOException e){ e.printStackTrace(); System.err.println("操作异常..."); } } //生成简单PDF public static void test(){ //语法糖 try (PdfWriter writer = new PdfWriter("E:\\Java\\test.pdf"); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf, PageSize.A4.rotate()); ){ //设置文档属性 pdf.getDocumentInfo().setAuthor("huanzi-qch"); pdf.getDocumentInfo().setTitle("IText测试PDF"); pdf.getDocumentInfo().setSubject("XXX公司"); pdf.getDocumentInfo().setMoreInfo("1","111"); pdf.getDocumentInfo().setCreator("huanzi"); pdf.getDocumentInfo().setKeywords("IText"); //注册事件监听 pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new MyEventHandler()); //设置字体 document.setFont(ITextPdfUtil.getPdfFont()); //页边距 document.setMargins(20, 20, 20, 20); //简单文字 document.add(new Paragraph("简单文字")); document.add(new Paragraph("Hello Word!").add(new Tab()).add(new Text("你好!").addStyle(new Style().setFontSize(24)))); //简单图片 document.add(new Paragraph("简单图片")); document.add(new Image(ImageDataFactory.create("D:\\XFT User\\Pictures\\logo.png"))); //简单表格 document.add(new Paragraph("简单表格")); Table table = new Table(new float[]{3, 3, 4}); PdfFont font = ITextPdfUtil.getPdfFont(); //标题、内容 process(table, "姓名;年龄;电话号码", font, true); for (int i = 0; i < 5; i++) { process(table, "张三"+i+";"+(18+i)+";1500000000"+i, font, false); } document.add(table); //超链接 document.add(new Paragraph("超链接")); PdfLinkAnnotation annotation = new PdfLinkAnnotation(new Rectangle(0, 0)); annotation.setAction(PdfAction.createURI("https://itextpdf.com/")); Paragraph p = new Paragraph("更多精彩内容,猛戳:").add(new Link("这里", annotation)); document.add(p); //换一页 //document.add(new AreaBreak(AreaBreakType.NEXT_PAGE)); document.close(); System.out.println("操作完成!"); } catch (IOException e) { e.printStackTrace(); System.err.println("操作异常..."); } } //获取统一字体 public static PdfFont getPdfFont(){ PdfFont pdfFont = null; try { pdfFont = PdfFontFactory.createFont(ITextPdfUtil.FONT, PdfEncodings.IDENTITY_H,true); } catch (IOException e) { e.printStackTrace(); } return pdfFont; } //设置表格内容 public static void process(Table table, String line, PdfFont font, boolean isHeader) { String[] split = line.split(";"); for (String s : split) { Cell cell = new Cell().add(new Paragraph(s).setFont(font)); if (isHeader) { table.addHeaderCell(cell); } else { table.addCell(cell); } } } /** * 自定义事件监听 * * 背景颜色 * 页脚页眉 * 文字水印 * * 也可以分成多个EventHandler */ protected static class MyEventHandler implements IEventHandler { @Override public void handleEvent(Event event) { PdfDocumentEvent docEvent = (PdfDocumentEvent) event; PdfDocument pdfDoc = docEvent.getDocument(); PdfPage page = docEvent.getPage(); int pageNumber = pdfDoc.getPageNumber(page); Rectangle pageSize = page.getPageSize(); PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc); //背景颜色 Color backgroundColor = new DeviceRgb(245, 245, 245);; pdfCanvas.saveState() .setFillColor(backgroundColor) .rectangle(pageSize.getLeft(), pageSize.getBottom(),pageSize.getWidth(), pageSize.getHeight()) .fill().restoreState(); //页脚页眉 PdfFont pdfFont = ITextPdfUtil.getPdfFont(); String header = "我是页眉"; String footer = "第 "+pageNumber+" 页"; pdfCanvas.beginText() .setFontAndSize(pdfFont, 9) .moveText((pageSize.getWidth() / 2) - (pdfFont.getWidth(header) / 200), pageSize.getTop() - 20) .showText(header) .moveText((pdfFont.getWidth(header) / 200) - (pdfFont.getWidth(footer) / 200), -pageSize.getTop() + 30) .showText(footer) .endText(); //文字水印 Canvas canvas = new Canvas(pdfCanvas, pdfDoc, page.getPageSize()); canvas.setFontColor(new DeviceRgb(200, 200, 200)); canvas.setProperty(20, 20); canvas.setFont(pdfFont); for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { canvas.showTextAligned(new Paragraph("我是文字水印").setOpacity(0.8f),(150 + i * 300), (160 + j * 150), pdfDoc.getPageNumber(page), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45); } } pdfCanvas.release(); } } //测试 public static void main(String[] args) { test(); //html2pdf(); } }
效果
生成的PDF
文档属性
HTML转PDF
官方文档:https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-converting-html-to-pdf-with-pdfhtml
pdfHTML是iText 7的一个附加组件,需要添加依赖
<properties> <itext7.html2pdf.version>2.1.4itext7.html2pdf.version> properties> <dependencies> <dependency> <groupId>com.itextpdfgroupId> <artifactId>html2pdfartifactId> <version>${itext7.html2pdf.version}version> dependency> dependencies>
代码
代码同上,仅main测试函数不同!
//测试 public static void main(String[] args) { //test(); html2pdf(); }
PS:html内容往下拉
效果
html页面
DOCTYPE html>
<html>
<head>
<title>简单简历title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate"/>
<meta http-equiv="pragma" content="no-cache"/>
<meta http-equiv="expires" content="0"/>
<style>
*{
font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
}
body {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
padding: 0;
margin: 5px 0;
color: black;
}
a:hover {
color: #5c8dff;
}
b{
margin: 0 10px;
}
/* 主体 */
.main {
/*margin: 0 auto;*/
/*width: 770px;*/
/*box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);*/
/*border: 1px solid #dad8d8;*/
/*background: #fbfbfb;*/
}
/* 1级标题 */
h3.list1 {
color: #409EFF;
border-bottom: 1px solid #409EFF;
padding: 5px;
margin: 50px 0 0 0;
}
/* 大模块 */
.block,.block1 {
/*background: #efefef;*/
/*border: 1px solid #dad8d8;*/
margin: 0 0 20px 0;
padding: 0 10px;
}
.block,.block1 p {
text-indent:2em;
}
.block:hover {
background: #eeeeee;
}
.block1:hover {
background: #cecece;
}
.button-list{
text-align: center;
margin: 20px auto;
padding: 10px;
width: 1024px;
}
style>
head>
<body>
<div id="body" class="main">
<div class="section">
<div class="module">
<h3 class="list1" style="margin: 0;">基本信息h3>
<div class="block">
<p>XXX<b>/b>男<b>/b>25岁p>
<p>本科<b>/b>XX学校<b>/b>XX专业<b>/b>2014-2018p>
<p>工龄:X年p>
<p>手机:XXXXXXXXXXp>
<p>邮箱:XXXXX@qq.comp>
<p>GitHub:<a href="https://github.com/huanzi-qch">https://github.com/huanzi-qcha>p>
<p>博客园:<a href="https://www.cnblogs.com/huanzi-qch">https://www.cnblogs.com/huanzi-qcha>p>
<br/>
<p>求职岗位:Java开发<b>/b>目标城市:南宁市<b>/b>期望薪资:面议p>
<p>注:已离职,一个月可到岗p>
<img style="position: relative;top: -350px;left: 500px;width: 100px;height: 130px;" src="logo.png"/>
div>
div>
<div class="module">
<h3 class="list1">技能清单h3>
<div class="block">
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
<p>熟悉XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。p>
div>
div>
<div class="module">
<h3 class="list1">工作经历h3>
<div class="block">
<p>XXX技术有限公司<b>/b>Java开发工程师<b>/b>2018.01 - 至今p>
<p>参与多个项目开发、测试、部署等工作,包括:p>
<p>1、XXX。p>
<p>2、XXX。p>
<p>3、XXX。p>
div>
div>
<div class="module">
<h3 class="list1">项目经历h3>
<div class="block">
<h4 class="list2">项目1h4>
<div class="block1">
<p>项目名称:XXX(2020-11 —2021-11)p>
<p>项目介绍:XXXXXXXXXXXXXXX。p>
<p>技术架构:XXX + XXX + XXX。p>
<p>职责描述:p>
<p>1、XXXX。p>
<p>2、XXXX。p>
<p>3、XXXX。p>
<p>4、XXXX。p>
div>
<h4 class="list2">项目2h4>
<div class="block1">
<p>项目名称:XXX(2020-11 —2021-11)p>
<p>项目介绍:XXXXXXXXXXXXXXX。p>
<p>技术架构:SpringBoot + Vue + Element-UI + ECharts。p>
<p>职责描述:p>
<p>1、XXXX。p>
<p>2、XXXX。p>
<p>3、XXXX。p>
<p>4、XXXX。p>
div>
div>
div>
<div class="module">
<h3 class="list1">自我评价h3>
<div class="block">
<p>1、XXXX;p>
<p>2、XXXX;p>
<p>3、XXXX;p>
div>
div>
div>
div>
body>
html>
生成的PDF
后记
不管是前端生成PDF,还是后端生成PDF,能实现需求就是好技术!