freemarker生成html、html转pdf、pdf根据关键字定位、pdf签名

版权声明:转载请注明出处 https://blog.csdn.net/ab52262879/article/details/84530950

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>

猜你喜欢

转载自blog.csdn.net/ab52262879/article/details/84530950