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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章