前言
我們之前使用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.7</itext7.version> </properties> <dependencies> <!-- 省略其他部分...--> <!-- itextpdf --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>kernel</artifactId> <version>${itext7.version}</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>io</artifactId> <version>${itext7.version}</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>layout</artifactId> <version>${itext7.version}</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>forms</artifactId> <version>${itext7.version}</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>pdfa</artifactId> <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.4</itext7.html2pdf.version> </properties> <dependencies> <!-- 省略其他部分...--> <!-- itextpdf html2pdf元件--> <dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <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-2018</p> <p>工齡:X年</p> <p>手機:XXXXXXXXXX</p> <p>郵箱:XXXXX@qq.com</p> <p>GitHub:<a href="https://github.com/huanzi-qch">https://github.com/huanzi-qch</a></p> <p>部落格園:<a href="https://www.cnblogs.com/huanzi-qch">https://www.cnblogs.com/huanzi-qch</a></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">專案1</h4> <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">專案2</h4> <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,能實現需求就是好技術!