java itextpdf签章 根据关键字定位在pdf文件中的坐标

最近接到个需求,根据所传关键字,定位关键字所在pdf文件中的坐标,然后盖章,其中所传参数还有关键字所在页数,和获取第几次出现的关键字,会传多个印章和关键字,每个印章对应一个关键字。自己先写了个简单demo,和大家共享,还请多多指正。
所有java类
用到的jar包

package test.ne2.bean;
public class KeyWordBean implements Comparable<KeyWordBean> {
public KeyWordBean() {
	super();
}
public KeyWordBean(float x, float y, int page, String text) {
	super();
	this.x = x;
	this.y = y;
	this.page = page;
	this.text = text;
}
private float 	x;
private float 	y;
private int    	page;
private String 	text;
public float getX() {
	return x;
}
public void setX(float x) {
	this.x = x;
}
public float getY() {
	return y;
}
public void setY(float y) {
	this.y = y;
}
public int getPage() {
	return page;
}
public void setPage(int page) {
	this.page = page;
}

public String getText() {
	return text;
}
public void setText(String text) {
	this.text = text;
}
@Override
public String toString() {
	return "KeyWordBean [x=" + x + ", y=" + y + ", page=" + page
			+ ", text=" + text + "]";
}
@Override
public int compareTo(KeyWordBean o) {
	int i = (int) (o.getY() - this.getY());//先按照Y轴排序  
    if(i == 0){  
        return (int) (this.x - o.getX());//如果Y轴相等了再按X轴进行排序  
    }  
    return i;  
}

}

package test.ne2.bean;

public class SignPDFBean {
	private String keyWord; 			// 签章关键字
	private int	page; 					// 关键字所在页数
	private int num;					// 取第n次出现的关键字
	private String sealPath;			// 印章图片路径
	private String keyStorePath;		// 证书文件路径
	private String keyStorePass;		// 证书密码
	private String signReason;			// 设置签章原因,可以为空
	private String signLocation;		// 设置签章地点
	public String getKeyWord() {
		return keyWord;
	}
	public void setKeyWord(String keyWord) {
		this.keyWord = keyWord;
	}
	public int getPage() {
		return page;
	}
	public void setPage(int page) {
		this.page = page;
	}
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getSealPath() {
		return sealPath;
	}
	public void setSealPath(String sealPath) {
		this.sealPath = sealPath;
	}
	public String getKeyStorePath() {
		return keyStorePath;
	}
	public void setKeyStorePath(String keyStorePath) {
		this.keyStorePath = keyStorePath;
	}
	public String getKeyStorePass() {
		return keyStorePass;
	}
	public void setKeyStorePass(String keyStorePass) {
		this.keyStorePass = keyStorePass;
	}
	public String getSignReason() {
		return signReason;
	}
	public void setSignReason(String signReason) {
		this.signReason = signReason;
	}
	public String getSignLocation() {
		return signLocation;
	}
	public void setSignLocation(String signLocation) {
		this.signLocation = signLocation;
	}
	@Override
	public String toString() {
		return "SignPDFBean [keyWord=" + keyWord + ", page=" + page + ", num="
				+ num + ", sealPath=" + sealPath + ", keyStorePath="
				+ keyStorePath + ", keyStorePass=" + keyStorePass
				+ ", signReason=" + signReason + ", signLocation="
				+ signLocation + "]";
	}
	
}
package test.ne2.bean;
import java.util.List;
public class SignPDFRequestBean {
private String srcPDFPath; // 待签章pdf文件路径
private String outPDFPath;	// 签章后输出的pdf文件路径
private List<SignPDFBean> SignPDFBeans;
public String getSrcPDFPath() {
	return srcPDFPath;
}
public void setSrcPDFPath(String srcPDFPath) {
	this.srcPDFPath = srcPDFPath;
}
public String getOutPDFPath() {
	return outPDFPath;
}
public void setOutPDFPath(String outPDFPath) {
	this.outPDFPath = outPDFPath;
}
public List<SignPDFBean> getSignPDFBeans() {
	return SignPDFBeans;
}
public void setSignPDFBeans(List<SignPDFBean> signPDFBeans) {
	SignPDFBeans = signPDFBeans;
}
@Override
public String toString() {
	return "SignPDFRequestBean [srcPDFPath=" + srcPDFPath + ", outPDFPath="
			+ outPDFPath + ", SignPDFBeans=" + SignPDFBeans + "]";
}

}

package test.ne2;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import test.ne2.bean.KeyWordBean;

import com.google.common.collect.Lists;
import com.itextpdf.text.pdf.PdfReader;
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;

public class KeywordPDFUtils {
	 
	// 定义返回页码
	private static int i = 0;
	private static com.itextpdf.awt.geom.Rectangle2D.Float boundingRectange =null;
 
	private static List<KeyWordBean> lists = null;
//	private static KeyWordBean bean = new KeyWordBean();
	
	private static Map<Integer, List<KeyWordBean>> map = new HashMap<Integer, List<KeyWordBean>>();
	 
	public static Map<Integer, List<KeyWordBean>> getPDFText(String filePath) {
		try {
			PdfReader pdfReader = new PdfReader(filePath);
			int pageNum = pdfReader.getNumberOfPages();
			PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader);
	 
			for (i = 1; i <= pageNum; i++) {
				lists = Lists.newArrayList();
				boundingRectange =new com.itextpdf.awt.geom.Rectangle2D.Float();
				pdfReaderContentParser.processContent(i, new RenderListener() {
					@Override
					public void renderText(TextRenderInfo textRenderInfo) {
						String text = textRenderInfo.getText(); // 整页内容
						boundingRectange= textRenderInfo.getBaseline().getBoundingRectange();
						KeyWordBean bean = new KeyWordBean();
						bean.setX(boundingRectange.x);
						bean.setY(boundingRectange.y);
						bean.setPage(i);
						bean.setText(text);
						lists.add(bean);
					}
					@Override
					public void renderImage(ImageRenderInfo arg0) {
						// TODO Auto-generated method stub
					}
					@Override
					public void endTextBlock() {
						// TODO Auto-generated method stub
					}
					@Override
					public void beginTextBlock() {
						// TODO Auto-generated method stub
	 
					}
				});
				map.put(i, lists);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return map;
	}
	
	public static void main(String[] args) {
		Map<Integer, List<KeyWordBean>> keyWords = getPDFText("D:\\pdf\\src.pdf");
		System.out.println(keyWords.size());
	}

	public static KeyWordBean getKeyWordXY(Map<Integer, List<KeyWordBean>> map2, int page, int num,String keyWord) {
		int keyMatch = 1;
		StringBuilder content = new StringBuilder();
		 List<KeyWordBean> list = map.get(page);
		 Collections.sort(list);//正序比较
		StringBuilder builder = new StringBuilder();
		for(int i=0; i<list.size(); i++){
			KeyWordBean bean = list.get(i);
			String text = bean.getText();
			if(i+1 != list.size()){
				KeyWordBean beanNext = list.get(i+1);
				float x = beanNext.getX()-bean.getX();
				float y = beanNext.getY()-bean.getY();
				if(y == 0 && x <= 1){
//					System.out.print("去除因字体加粗产生的重复数字");
				}else{
					if (StringUtils.contains(content.toString(), keyWord) || StringUtils.contains(text, keyWord)) {
						if(keyMatch == num){
							return bean;
						}else{
							keyMatch ++;
						}
					}else if((!StringUtils.isEmpty(text) && keyWord.startsWith(text)) || content.length()>0){
						content.append(text);
						if(content.length() >= keyWord.length()){
							if(StringUtils.contains(content.toString(), keyWord)){
								if(keyMatch == num){
									return bean;
								}else{
									keyMatch ++;
								}
							}
							content = new StringBuilder();
						}
					}
				}
			}else{
				if (StringUtils.contains(content.toString(), keyWord) || StringUtils.contains(text, keyWord)) {
					if(keyMatch == num){
						return bean;
					}else{
						keyMatch ++;
					}
				}else if((!StringUtils.isEmpty(text) && keyWord.startsWith(text)) || content.length()>0){
					content.append(text);
					if(content.length() >= keyWord.length()){
						if(StringUtils.contains(content.toString(), keyWord)){
							if(keyMatch == num){
								return bean;
							}else{
								keyMatch ++;
							}
						}
						content = new StringBuilder();
					}
				}
			}
		}
		return null;
	}
	
	public static KeyWordBean getKeyWordBean(KeyWordBean bean, StringBuilder content, String keyWord, String text, int keyMatch, int num){
		if (StringUtils.contains(content.toString(), keyWord) || StringUtils.contains(text, keyWord)) {
			if(keyMatch == num){
				return bean;
			}else{
				keyMatch ++;
			}
		}else if((!StringUtils.isEmpty(text) && keyWord.startsWith(text)) || content.length()>0){
			content.append(text);
			if(content.length() >= keyWord.length()){
				if(StringUtils.contains(content.toString(), keyWord)){
					if(keyMatch == num){
						return bean;
					}else{
						keyMatch ++;
					}
				}
				content = new StringBuilder();
			}
		}
		return null;
	
	}

}

package test.ne2;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.UUID;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
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.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;

public class SignPDFUtils {
	
	/**     
	 *@param password     
	 *			秘钥密码    
	 *@param keyStorePath     
	 * 			秘钥文件路径    
	 *@param signPdfSrc     
	 *			签名的PDF文件     
	 *@param signImage     
	 **            签名图片文件     
	 ** @param x     
	 **            x坐标     
	 ** @param y     
	 **            y坐标     
	 ** @return     */    
	public static byte[] sign(String password, String keyStorePath, InputStream inputStream, String signImage, float x, float y, int page,String reason,String location) {
		PdfReader reader = null;        
		ByteArrayOutputStream signPDFData = null;        
		PdfStamper stp = null;        
		FileInputStream fos = null;        
		try {            
			BouncyCastleProvider provider = new BouncyCastleProvider();            
			Security.addProvider(provider);            
//			KeyStore ks = KeyStore.getInstance("JKS", new BouncyCastleProvider());            
			KeyStore ks = KeyStore.getInstance("JKS");
			fos = new FileInputStream(keyStorePath);            // 私钥密码 为Pkcs生成证书是的私钥密码 123456            
			ks.load(fos, password.toCharArray());             
			String alias = (String) ks.aliases().nextElement(); 
			// 获得私钥
			PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password.toCharArray());  
			// 获得证书链 
			Certificate[] chain = ks.getCertificateChain(alias);            
			reader = new PdfReader(inputStream);            
			signPDFData = new ByteArrayOutputStream();  
			//创建签章工具PdfStamper ,最后一个boolean参数 
	        //false的话,pdf文件只允许被签名一次,多次签名,最后一次有效
	        //true的话,pdf可以被追加签名,验签工具可以识别出每次签名之后文档是否被修改
			stp = PdfStamper.createSignature(reader, signPDFData, '\0', null, true);             
			stp.setFullCompression();   
			// 获取数字签章属性对象,设定数字签章的属性   
			PdfSignatureAppearance sap = stp.getSignatureAppearance();  
			// 设置签章原因
			sap.setReason(reason);
			// 设置签章地点                                                                                                                                                                                      			
			sap.setLocation(location);   
			// 使用png格式透明图片            
			Image image = Image.getInstance(signImage);            
			sap.setImageScale(0);            
			sap.setSignatureGraphic(image);  
			// 设置签章的显示方式,如下选择的是只显示图章(还有其他的模式,可以图章和签名描述一同显示)                                                                                                                                                			
			sap.setRenderingMode(RenderingMode.GRAPHIC);            
			//设置签名的位置,页码,签名域名称,多次追加签名的时候,签名预名称不能一样
	        //签名的位置,是图章相对于pdf页面的位置坐标,原点为pdf页面左下角
	        //四个参数的分别是,图章左下角x,图章左下角y,图章右上角x,图章右上角y
			sap.setVisibleSignature(new Rectangle(x, y, x + 185, y + 68), page,                    
			UUID.randomUUID().toString().replaceAll("-", ""));  
			
			stp.getWriter().setCompressionLevel(5);   
			// 摘要算法
			ExternalDigest digest = new BouncyCastleDigest();   
			// 签章算法 
			ExternalSignature signature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA512, provider.getName()); 
//			ExternalSignature signature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA512, provider.getName()); 
			// 进行盖章操作 CMS高级电子签名(CAdES)的长效签名规范   
			MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);            
			stp.close();            
			reader.close();            
			return signPDFData.toByteArray();        
			} catch (Exception e) {            
				e.printStackTrace();        
			} finally {             
				if (signPDFData != null) {               
					try {                    
						signPDFData.close();                
						} catch (IOException e) {                
							
						}            
					}             
				if (fos != null) {               
					try {                    
						fos.close();                
						} catch (IOException e) {                
							
						}            
					}        
				}        
		return null;    
		} 

}

package test.ne2;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import test.ne2.bean.KeyWordBean;
import test.ne2.bean.SignPDFBean;
import test.ne2.bean.SignPDFRequestBean;

public class Test {

	public static void main(String[] args) throws Exception {
		/*
		 * 签章参数
		 */
//		String keyWord 			= "85000315"; 					// 签章关键字
//		int page 				= 1; 					// 关键字所在页数
//		int num 				= 1;					// 取第n次出现的关键字
//		String srcPDFPath 		= "D:\\pdf\\test2.pdf"; // 待签章pdf文件路径
//		String outPDFPath 		= "D:\\pdf\\out.pdf";	// 签章后输出的pdf文件路径
//		String sealPath 		= "D:\\pdf\\印章.png";	// 印章图片路径
//		String keyStorePath 	= "D:\\pdf\\keystore";	// 证书文件路径
//		String keyStorePass 	= "Jitadmin001";		// 证书密码
//		String signReason		= "设置签章原因";		// 设置签章原因,可以为空
//		String signLocation		= "设置签章地点  ";		// 设置签章地点
		List<SignPDFBean> list = new ArrayList<SignPDFBean>();
		SignPDFBean bean1 = new SignPDFBean();
		bean1.setKeyStorePass("11111111");
		bean1.setKeyStorePath("D:\\pdf\\key\\keystore");
//		bean1.setKeyStorePass("Jitadmin001");
//		bean1.setKeyStorePath("D:\\pdf\\keystore");
		bean1.setKeyWord("85000315");
		bean1.setNum(1);
		bean1.setPage(1);
		bean1.setSealPath("D:\\pdf\\印章.png");
		bean1.setSignLocation("知春路1");
		bean1.setSignReason("孙测试1");
		
		SignPDFBean bean2 = new SignPDFBean();
		bean2.setKeyStorePass("Jitadmin001");
		bean2.setKeyStorePath("D:\\pdf\\keystore");
		bean2.setKeyWord("客户");
		bean2.setNum(1);
		bean2.setPage(1);
		bean2.setSealPath("D:\\pdf\\印章2.png");
		bean2.setSignLocation("知春路2");
		bean2.setSignReason("孙测试2");
		
		SignPDFBean bean3 = new SignPDFBean();
		bean3.setKeyStorePass("Jitadmin001");
		bean3.setKeyStorePath("D:\\pdf\\keystore");
		bean3.setKeyWord("五");
		bean3.setNum(1);
		bean3.setPage(1);
		bean3.setSealPath("D:\\pdf\\印章3.png");
		bean3.setSignLocation("知春路3");
		bean3.setSignReason("孙测试3");
		
		list.add(bean1);
		list.add(bean2);
		list.add(bean3);
		
		SignPDFRequestBean requestBean = new SignPDFRequestBean();
		requestBean.setSrcPDFPath("D:\\pdf\\test2.pdf");
		requestBean.setOutPDFPath("D:\\pdf\\out.pdf");
		requestBean.setSignPDFBeans(list);
		
		long startTime = System.currentTimeMillis();
		// 1.解析pdf文件
		Map<Integer, List<KeyWordBean>> map = KeywordPDFUtils.getPDFText(requestBean.getSrcPDFPath());
		// 2.获取关键字坐标
		List<SignPDFBean> beans = requestBean.getSignPDFBeans();
		byte[] fileData = null;
		InputStream in = null;
		for (int i = 0; i <beans.size(); i++){
			SignPDFBean pdfBean = beans.get(i);
			KeyWordBean bean = KeywordPDFUtils.getKeyWordXY(map, pdfBean.getPage(), pdfBean.getNum(), pdfBean.getKeyWord());
			if(null == bean){
				System.out.println("未查询到关键字。。。");
			}
			System.out.println(bean.toString());
			long keyTime = System.currentTimeMillis();
			if(i == 0){
				in = new FileInputStream(requestBean.getSrcPDFPath());
			}else{
				in = new ByteArrayInputStream(fileData);
			}
			// 3.进行盖章
			fileData = SignPDFUtils.sign(pdfBean.getKeyStorePass(), pdfBean.getKeyStorePath(), in, pdfBean.getSealPath(), bean.getX(), bean.getY(), bean.getPage(),pdfBean.getSignReason(),pdfBean.getSignLocation());
			long signTime = System.currentTimeMillis();
		}
		// 4.输出盖章后pdf文件
		FileOutputStream f = new FileOutputStream(new File(requestBean.getOutPDFPath()));        
		f.write(fileData);        
		f.close();   
		in.close();
		long endTime = System.currentTimeMillis();
//		System.out.println("关键字定位花费时间:"+(keyTime-startTime));
//		System.out.println("盖章消耗时间:"+(signTime-keyTime));
		System.out.println("总时间:"+(endTime-startTime));
	
	}
	
	
}

第一次写,排版很乱,大家见谅
参考文档:https://blog.csdn.net/javasun608/article/details/79307845
http://www.mamicode.com/info-detail-1942091.html
https://blog.csdn.net/modelsetget/article/details/80812550#comments

发布了9 篇原创文章 · 获赞 6 · 访问量 9837

猜你喜欢

转载自blog.csdn.net/weixin_43145779/article/details/83115766