freemarker生成html、html轉pdf、pdf根據關鍵字定位、pdf簽名
阿新 • • 發佈:2018-12-07
freemarker生成html、html轉pdf、pdf根據關鍵字定位、pdf簽名圖片
PdfUtil 類 (全部功能程式碼)
package com.wei.weitest.pdf; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; import java.nio.charset.Charset; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Security; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bouncycastle.jce.provider.BouncyCastleProvider; import com.itextpdf.awt.geom.Rectangle2D.Float; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Font; import com.itextpdf.text.Image; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.PdfSignatureAppearance; import com.itextpdf.text.pdf.PdfStamper; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode; import com.itextpdf.text.pdf.parser.ImageRenderInfo; import com.itextpdf.text.pdf.parser.PdfReaderContentParser; import com.itextpdf.text.pdf.parser.RenderListener; import com.itextpdf.text.pdf.parser.TextRenderInfo; import com.itextpdf.text.pdf.security.BouncyCastleDigest; import com.itextpdf.text.pdf.security.ExternalDigest; import com.itextpdf.text.pdf.security.ExternalSignature; import com.itextpdf.text.pdf.security.MakeSignature; import com.itextpdf.text.pdf.security.PrivateKeySignature; import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard; import com.itextpdf.tool.xml.XMLWorkerFontProvider; import com.itextpdf.tool.xml.XMLWorkerHelper; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; /** * <p>Title: PdfUtil</p> * <p>Description: freemarker生成html 並轉pdf 並根據關鍵字定位 並簽名 工具類</p> * @author weisibin * @date 2018年11月23日 */ public class PdfUtil { private static Configuration config = null; static{ try { /** * Static initialization. * Initialize the configuration of Freemarker. */ config = new Configuration(); //ftl模板檔案統一放至基於類路徑載入模板 config.setClassForTemplateLoading(PdfUtil.class, "/com/wei/weitest/template"); config.setTemplateUpdateDelay(0); // 設定編碼 config.setDefaultEncoding("UTF-8"); //簽名證書 基於BC實現的(JAVA版)SM2國密演算法簽名 //使用BouncyCastle Security.addProvider(new BouncyCastleProvider()); } catch (Exception e) { e.printStackTrace(); } } /** * <p>Title: createPdf</p> * <p>Description: 用freemarker模板 建立pdf</p> * @author weisibin * @date 2018年11月23日 * @param dataMap 傳入 模板引數 * @param templateName 模板檔名(帶字尾) * @param pdffile 建立的pdf存放路徑 * @throws IOException * @throws TemplateException * @throws DocumentException */ public static void createPdf(Map<String, Object> dataMap, String templateName, String pdffile) throws IOException, TemplateException, DocumentException { StringWriter stringWriter = null; BufferedWriter writer = null; // 建立配置例項 // 獲取模板 Template template = config.getTemplate(templateName); stringWriter = new StringWriter(); writer = new BufferedWriter(stringWriter); template.process(dataMap, writer); String htmlStr = stringWriter.toString(); writer.flush(); writer.close(); // html轉 pdf parseHTML2PDFFile2(pdffile, htmlStr); } /** * <p>Title: parseHTML2PDFFile2</p> * <p>Description: html字串轉為pdf檔案</p> * @author weisibin * @date 2018年11月23日 * @param pdfFile * @param html * @throws DocumentException * @throws IOException */ private static void parseHTML2PDFFile2(String pdfFile, String html) throws DocumentException, IOException { Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdfFile)); // HTML轉PDF document.open(); XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(html.getBytes()), Charset.forName("UTF-8"), new XMLWorkerFontProvider() { //pdf中文字型解決 public Font getFont(final String fontname, final String encoding, final boolean embedded, final float size, final int style, final BaseColor color) { BaseFont bf = null; try { bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); } catch (Exception e) { e.printStackTrace(); } Font font = new Font(bf, size, style, color); font.setColor(color); return font; } }); document.close(); } /** * <p>Title: signPdf</p> * <p>Description: pdf簽名證書</p> * @author weisibin * @date 2018年11月23日 * @param src 簽名pdf檔案位置 * @param os 簽名後pdf輸出流 * @param map 需要傳遞的引數 * @throws Exception */ public static void signPdf(String src, OutputStream os ,Map<String,Object> map) throws Exception { KeyStore ks = KeyStore.getInstance("pkcs12", "BC"); // 證書與證書密碼 ks.load(new FileInputStream(Content.PFX_PATH), Content.PFX_PASS.toCharArray()); String alias = (String) ks.aliases().nextElement(); PrivateKey pk = (PrivateKey) ks.getKey(alias, Content.PFX_PASS.toCharArray()); Certificate[] chain = ks.getCertificateChain(alias); // 讀取需要簽名的PDF原始檔 PdfReader reader = new PdfReader(src); // 建立簽章工具PdfStamper PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0'); // 獲取數字簽章屬性物件 PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); //簽名原因 if(null != map.get("reason")){ appearance.setReason(String.valueOf(map.get("reason"))); } //簽名位置 if(null != map.get("location")){ appearance.setLocation(String.valueOf(map.get("location"))); } // 獲取pdf的最後一頁頁數 int endPages = reader.getNumberOfPages(); // 獲得讀取pdf內容物件 PdfReaderContentParser parse = new PdfReaderContentParser(reader); // 獲得相關頁碼關鍵字具體位置 物件 //關鍵字 key String keyWords = "某某簽章:"; List<float[]> pl = getKeyWordPositionByPageNum(parse, endPages, keyWords); // 簽章位置 float[] p = null; // 獲取最後一個關鍵字定位物件 if (pl != null && pl.size() != 0) { p = pl.get(pl.size() - 1); } else { throw new Exception("pdf簽章沒有讀取到簽章關鍵字!"); } // 設定簽名簽名域圖章大小及位置 Rectangle positon = new Rectangle(p[0] + 85, p[1] - 80, p[0] + 165, p[1]); // 設定簽名的簽名域名稱 appearance.setVisibleSignature(positon, endPages, "sign"); // 讀取圖章圖片,這個image是itext包的image Image image = Image.getInstance(Content.LOGO_PATH); appearance.setSignatureGraphic(image); appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED); // 設定圖章的顯示方式,如下選擇的是隻顯示圖章(還有其他的模式,可以圖章和簽名描述一同顯示) appearance.setRenderingMode(RenderingMode.GRAPHIC); // 簽名演算法 ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", "BC"); // 摘要演算法 ExternalDigest digest = new BouncyCastleDigest(); // 呼叫itext簽名方法完成pdf簽章 MakeSignature.signDetached(appearance, digest, es, chain, null, null, null, 0, CryptoStandard.CMS); } /** * <p>Title: signPdf</p> * <p>Description: pdf簽名證書</p> * @author weisibin * @date 2018年11月23日 * @param src 簽名pdf檔案位置 * @param filepath 簽名後pdf檔案存放位置 * @param map 需要傳入的引數 * @throws Exception */ public static void signPdf(String src, String filepath ,Map<String,Object> map) throws Exception{ OutputStream os = new FileOutputStream(filepath); signPdf(src, os ,map); } /** * <p>Title: getKeyWordPosition</p> * <p>Description: 根據關鍵字和頁碼 獲取關鍵字的定位</p> * @author weisibin * @date 2018年11月23日 * @param parse 讀取pdf內容物件 * @param pageNumber 定位的頁碼 * @param keyword 關鍵字 * @return List<float[]> 定位集合 float[]:座標和頁數 float[0] >> X float[1] >> Y float[2] >> page * @throws Exception */ private static List<float []> getKeyWordPositionByPageNum(PdfReaderContentParser parse,Integer pageNumber, String keyword) throws Exception { List<float []> list = new ArrayList<float []>(); float [] resu = new float[3]; parse.processContent(pageNumber, new RenderListener() { @Override public void renderText(TextRenderInfo renderInfo) { String text = renderInfo.getText(); if (null != text && text.contains(keyword)) { Float boundingRectange = renderInfo.getBaseline().getBoundingRectange(); resu[0] = boundingRectange.x; resu[1] = boundingRectange.y; resu[2] = pageNumber; list.add(resu); } } @Override public void beginTextBlock() { } @Override public void endTextBlock() { } @Override public void renderImage(ImageRenderInfo renderInfo) { } }); return list; } /** * <p>Title: getKeyWordPositionByAllPages</p> * <p>Description: 根據keyword 全域性定位</p> * @author weisibin * @date 2018年11月23日 * @param parse 讀取pdf內容物件 * @param pages pdf總頁數 * @param keyword 關鍵字 * @return List<float[]> 定位集合 float[]:座標和頁數 float[0] >> X float[1] >> Y float[2] >> page * @throws Exception */ public static List<float[]> getKeyWordPositionByAllPages(PdfReaderContentParser parse, Integer pages, String keyword) throws Exception { List<float[]> List = new ArrayList<float[]>(); List<float[]> List1 = null; for (int pageNumber = 1; pageNumber <= pages; pageNumber++) { List1 = getKeyWordPositionByPageNum(parse,pageNumber, keyword); if (List1 != null && List1.size() != 0) { List.addAll(List1); } } return List; } }
Content 類 (自己建立相關檔案)
package com.wei.weitest.pdf; /** * <p>Title: Content</p> * <p>Description: pdf配置類</p> * @author weisibin * @date 2018年11月23日 */ public class Content { public static String BASE = "D:/dddd/pdfSign"; //檔案存放路徑 public static String PDF_NEW = BASE + "/haveSgin.pdf"; // 建立的PDF public static String PDF_SIGNED = BASE + "/haveSgin_1.pdf"; // 簽名的檔案 public static final String PFX_PATH = BASE + "/fake-credentials.pfx";// 證書,包含金鑰 public static final String PFX_PASS = "1111111"; // 這是簽名證書的密碼 public static final String LOGO_PATH = BASE + "/logo.png"; // 簽名的圖 }
相關依賴(基於spring-boot測試)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wei</groupId> <artifactId>weitest</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>weitest</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.SR1</spring-cloud.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-context</artifactId> </dependency> --> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> </dependency> <dependency> <artifactId>flying-saucer-pdf-itext5</artifactId> <groupId>org.xhtmlrenderer</groupId> <version>9.0.6</version> </dependency> <dependency> <artifactId>freemarker</artifactId> <groupId>freemarker</groupId> <version>2.3.8</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.10</version> </dependency> <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.10.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.56</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-pdfa</artifactId> <version>5.5.10</version> </dependency> <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-ext-jdk16 --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-ext-jdk16</artifactId> <version>1.46</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-xtra</artifactId> <version>5.5.10</version> </dependency> <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcmail-jdk16 --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcmail-jdk16</artifactId> <version>1.46</version> </dependency> <!-- https://mvnrepository.com/artifact/org.bouncycastle/bctsp-jdk16 --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bctsp-jdk16</artifactId> <version>1.46</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-scratchpad --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>3.16-beta2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.7-beta3</version> </dependency> <dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.10</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>