前言:
以下是通過網上查閱資料,東拼西湊實現的一個使用java替換pdf文字的功能。使用的是itextpdf.jar
參考:
https://blog.csdn.net/sdizoea/article/details/75105798
https://blog.csdn.net/sishenkankan/article/details/53107195
具體實現:
1.引入jar包
1 <dependency> 2 <groupId>com.itextpdf</groupId> 3 <artifactId>itextpdf</artifactId> 4 <version>5.5.13</version> 5 </dependency> 6 <dependency> 7 <groupId>com.itextpdf</groupId> 8 <artifactId>itext-asian</artifactId> 9 <version>5.2.0</version> 10 </dependency>
2.編寫實現類
實現類主要有三個類。
一個是用來保存關鍵字信息的實體類MatchItem;
一個是匹配關鍵字的監聽類KeyWordPositionListener;
最后一個是查找關鍵字、關鍵字替換的實現類PdfUtils。
具體代碼如下:
MatchItem實體類
1 /** 2 * 用來保存關鍵字信息 3 */ 4 public class MatchItem { 5 6 //頁數 7 private Integer pageNum; 8 //x坐標 9 private Float x; 10 //y坐標 11 private Float y; 12 //頁寬 13 private Float pageWidth; 14 //頁高 15 private Float pageHeight; 16 //匹配字符 17 private String content; 18 //字體寬 19 private float fontWidth; 20 //字體高 21 private float fontHeight = 12; 22 23 public Integer getPageNum() { 24 return pageNum; 25 } 26 27 public void setPageNum(Integer pageNum) { 28 this.pageNum = pageNum; 29 } 30 31 public Float getX() { 32 return x; 33 } 34 35 public void setX(Float x) { 36 this.x = x; 37 } 38 39 public Float getY() { 40 return y; 41 } 42 43 public void setY(Float y) { 44 this.y = y; 45 } 46 47 public Float getPageWidth() { 48 return pageWidth; 49 } 50 51 public void setPageWidth(Float pageWidth) { 52 this.pageWidth = pageWidth; 53 } 54 55 public Float getPageHeight() { 56 return pageHeight; 57 } 58 59 public void setPageHeight(Float pageHeight) { 60 this.pageHeight = pageHeight; 61 } 62 63 public String getContent() { 64 return content; 65 } 66 67 public void setContent(String content) { 68 this.content = content; 69 } 70 71 public float getFontWidth() { 72 return fontWidth; 73 } 74 75 public void setFontWidth(float fontWidth) { 76 this.fontWidth = fontWidth; 77 } 78 79 public float getFontHeight() { 80 return fontHeight; 81 } 82 83 public void setFontHeight(float fontHeight) { 84 this.fontHeight = fontHeight; 85 } 86 87 @Override 88 public String toString() { 89 return "MatchItem{" + 90 "pageNum=" + pageNum + 91 ", x=" + x + 92 ", y=" + y + 93 ", pageWidth=" + pageWidth + 94 ", pageHeight=" + pageHeight + 95 ", content='" + content + '\'' + 96 '}'; 97 } 98 }
KeyWordPositionListener監聽類
1 import com.itextpdf.awt.geom.Rectangle2D; 2 import com.itextpdf.text.Rectangle; 3 import com.itextpdf.text.pdf.parser.ImageRenderInfo; 4 import com.itextpdf.text.pdf.parser.RenderListener; 5 import com.itextpdf.text.pdf.parser.TextRenderInfo; 6 7 import java.util.ArrayList; 8 import java.util.List; 9 10 /** 11 * 用來匹配pdf的關鍵詞 監聽類 12 */ 13 public class KeyWordPositionListener implements RenderListener { 14 15 //存放匹配上的字符信息 16 private List<MatchItem> matches = new ArrayList<MatchItem>(); 17 //存放所有的字符信息 18 private List<MatchItem> allItems = new ArrayList<MatchItem>(); 19 20 private Rectangle curPageSize; 21 22 /** 23 * 匹配的關鍵字 24 */ 25 private String keyword; 26 /** 27 * 匹配的當前頁 28 */ 29 private Integer pageNumber; 30 31 @Override 32 public void beginTextBlock() { 33 //do nothing 34 } 35 36 @Override 37 public void renderText(TextRenderInfo renderInfo) { 38 //獲取字符 39 String content = renderInfo.getText(); 40 Rectangle2D.Float textRectangle = renderInfo.getDescentLine().getBoundingRectange(); 41 42 MatchItem item = new MatchItem(); 43 item.setContent(content); 44 item.setPageNum(pageNumber); 45 item.setFontHeight(textRectangle.height == 0 ? 12:textRectangle.height);//默認12 46 item.setFontWidth(textRectangle.width); 47 item.setPageHeight(curPageSize.getHeight()); 48 item.setPageWidth(curPageSize.getWidth()); 49 item.setX((float)textRectangle.getX()); 50 item.setY((float)textRectangle.getY()); 51 52 //若keyword是單個字符,匹配上的情況 53 if(content.equalsIgnoreCase(keyword)) { 54 matches.add(item); 55 } 56 //保存所有的項 57 allItems.add(item); 58 } 59 60 @Override 61 public void endTextBlock() { 62 //do nothing 63 } 64 65 @Override 66 public void renderImage(ImageRenderInfo renderInfo) { 67 //do nothing 68 } 69 70 /** 71 * 設置需要匹配的當前頁 72 * @param pageNumber 73 */ 74 public void setPageNumber(Integer pageNumber) { 75 this.pageNumber = pageNumber; 76 } 77 78 /** 79 * 設置需要匹配的關鍵字,忽略大小寫 80 * @param keyword 81 */ 82 public void setKeyword(String keyword) { 83 this.keyword = keyword; 84 } 85 86 /** 87 * 返回匹配的結果列表 88 * @return 89 */ 90 public List<MatchItem> getMatches() { 91 return matches; 92 } 93 94 void setCurPageSize(Rectangle rect) { 95 this.curPageSize = rect; 96 } 97 98 public List<MatchItem> getAllItems() { 99 return allItems; 100 } 101 102 public void setAllItems(List<MatchItem> allItems) { 103 this.allItems = allItems; 104 } 105 106 }
PdfUtils核心功能實現類
1 import com.itextpdf.text.BaseColor; 2 import com.itextpdf.text.Font; 3 import com.itextpdf.text.Rectangle; 4 import com.itextpdf.text.pdf.BaseFont; 5 import com.itextpdf.text.pdf.PdfContentByte; 6 import com.itextpdf.text.pdf.PdfReader; 7 import com.itextpdf.text.pdf.PdfStamper; 8 import com.itextpdf.text.pdf.parser.PdfReaderContentParser; 9 10 import java.io.FileOutputStream; 11 import java.util.ArrayList; 12 import java.util.HashMap; 13 import java.util.List; 14 import java.util.Map; 15 16 /** 17 * pdf替換文字工具類 18 * 19 * 思路: 20 * 1.逐頁搜索關鍵字,逐頁匹配 21 * 2.先讀取一頁的所有字符信息,存放到allItems中 22 * 3.把一頁的字符拼接成字符串,然后匹配關鍵字,匹配上,記錄匹配的第一個字符的MatchItem信息;匹配不是,繼續下一頁匹配 23 * 4.根據匹配字符串的長度和字符的寬高信息畫遮罩層,然后替換文字生成新的pdf文件 24 * 25 * 不足之處: 26 * 1.目前只支持單字符串匹配 27 * 2.替換之后的文字無法和原pdf中替換掉的文字信息一致(主要有:字體大小、樣式等) 28 * 3.某些情況下(主要是替換字體的大小)替換之后顯示不是太整齊 29 * 4.字體大小、樣式無法把控 30 * 5.無法匹配目標文字在兩頁中顯示的情況(例如:目標文字:替換工具,第一頁頁尾有替換兩字,第二頁頁首有工具二字) 31 * 32 */ 33 public class PdfUtils { 34 35 public static void main(String[] args) throws Exception{ 36 // List<MatchItem> matchItems = matchPage("C:\\Users\\Desktop\\pdftest.pdf", "系統"); 37 // for(MatchItem m : matchItems){ 38 // System.out.println(m); 39 // } 40 // manipulatePdf("C:\\Users\\Desktop\\pdftest.pdf","C:\\Users\\Desktop\\pdftest_new.pdf",matchItems,"系統"); 41 String src = "C:\\\\Users\\\\Desktop\\\\pdftest.pdf"; 42 String dest = "C:\\\\Users\\\\Desktop\\\\pdftest_new.pdf"; 43 String keyWord = "登陸"; 44 String keyWordNew = "測試"; 45 pdfReplace(src,dest,keyWord,keyWordNew); 46 } 47 48 /** 49 * 根據關鍵字和pdf路徑,全文搜索關鍵字 50 * @param filePath pdf目標路徑 51 * @param keyword 關鍵字 52 * @return 53 * @throws Exception 54 */ 55 public static List<MatchItem> matchAll(String filePath, String keyword) throws Exception { 56 List<MatchItem> items = new ArrayList<MatchItem>(); 57 PdfReader reader = new PdfReader(filePath); 58 //獲取pdf頁數 59 int pageSize = reader.getNumberOfPages(); 60 //逐頁匹配關鍵字 61 for(int page = 1;page <= pageSize;page++){ 62 items.addAll(matchPage(reader,page,keyword)); 63 } 64 return items; 65 } 66 67 /** 68 * 根據關鍵字、文檔路徑、pdf頁數尋找特定的文件內容 69 * @param reader 70 * @param pageNumber 頁數 71 * @param keyword 關鍵字 72 * @return 73 * @throws Exception 74 */ 75 public static List<MatchItem> matchPage(PdfReader reader, Integer pageNumber,String keyword) throws Exception { 76 PdfReaderContentParser parse = new PdfReaderContentParser(reader); 77 Rectangle rectangle = reader.getPageSize(pageNumber); 78 //匹配監聽 79 KeyWordPositionListener renderListener = new KeyWordPositionListener(); 80 renderListener.setKeyword(keyword); 81 renderListener.setPageNumber(pageNumber); 82 renderListener.setCurPageSize(rectangle); 83 parse.processContent(pageNumber, renderListener); 84 return findKeywordItems(renderListener,keyword); 85 } 86 87 /** 88 * 找到匹配的關鍵詞塊 89 * @param renderListener 90 * @param keyword 91 * @return 92 */ 93 public static List<MatchItem> findKeywordItems(KeyWordPositionListener renderListener,String keyword){ 94 //先判斷本頁中是否存在關鍵詞 95 List<MatchItem> allItems = renderListener.getAllItems();//所有塊LIST 96 StringBuffer sbtemp = new StringBuffer(""); 97 98 for(MatchItem item : allItems){//將一頁中所有的塊內容連接起來組成一個字符串。 99 sbtemp.append(item.getContent()); 100 } 101 102 List<MatchItem> matches = renderListener.getMatches(); 103 104 //一頁組成的字符串沒有關鍵詞,直接return 105 //第一種情況:關鍵詞與塊內容完全匹配的項,直接返回 106 if(sbtemp.toString().indexOf(keyword) == -1 || matches.size() > 0){ 107 return matches; 108 } 109 //第二種情況:多個塊內容拼成一個關鍵詞,則一個一個來匹配,組裝成一個關鍵詞 110 sbtemp = new StringBuffer(""); 111 List<MatchItem> tempItems = new ArrayList(); 112 for(MatchItem item : allItems){ 113 if(keyword.indexOf(item.getContent()) != -1 ){ 114 tempItems.add(item); 115 sbtemp.append(item.getContent()); 116 117 if(keyword.indexOf(sbtemp.toString()) == -1){//如果暫存的字符串和關鍵詞 不再匹配時 118 sbtemp = new StringBuffer(item.getContent()); 119 tempItems.clear(); 120 tempItems.add(item); 121 } 122 123 if(sbtemp.toString().equalsIgnoreCase(keyword)){//暫存的字符串正好匹配到關鍵詞時 124 matches.add(tempItems.get(0));//得到匹配的項 125 sbtemp = new StringBuffer("");//清空暫存的字符串 126 tempItems.clear();//清空暫存的LIST 127 continue;//繼續查找 128 } 129 }else{//如果找不到則清空 130 sbtemp = new StringBuffer(""); 131 tempItems.clear(); 132 } 133 } 134 return matches; 135 } 136 137 /** 138 * 替換目標文字,生成新的pdf文件 139 * @param src 目標pdf路徑 140 * @param dest 新pdf的路徑 141 * @throws Exception 142 */ 143 public static void manipulatePdf(String src,String dest,List<MatchItem> matchItems,String keyWord,String keyWordNew) throws Exception{ 144 PdfReader reader = new PdfReader(src); 145 PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest)); 146 PdfContentByte canvas = null; 147 Map<Integer,List<MatchItem>> mapItem = new HashMap<Integer,List<MatchItem>>(); 148 List<MatchItem> itemList = null; 149 for(MatchItem item : matchItems){ 150 Integer pageNum = item.getPageNum(); 151 if(mapItem.containsKey(pageNum)){ 152 itemList = mapItem.get(pageNum); 153 itemList.add(item); 154 mapItem.put(pageNum,itemList); 155 }else{ 156 itemList = new ArrayList<MatchItem>(); 157 itemList.add(item); 158 mapItem.put(pageNum,itemList); 159 } 160 } 161 //遍歷每一頁去修改 162 for(Integer page : mapItem.keySet()){ 163 List<MatchItem> items = mapItem.get(page); 164 //遍歷每一頁中的匹配項 165 for(MatchItem item : items){ 166 canvas = stamper.getOverContent(page); 167 float x = item.getX(); 168 float y = item.getY(); 169 float fontWidth = item.getFontWidth(); 170 float fontHeight = item.getFontHeight(); 171 canvas.saveState(); 172 canvas.setColorFill(BaseColor.WHITE); 173 canvas.rectangle(x, y,fontWidth*keyWord.length(),fontWidth+2); 174 canvas.fill(); 175 canvas.restoreState(); 176 //開始寫入文本 177 canvas.beginText(); 178 BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); 179 Font font = new Font(bf,fontWidth,Font.BOLD); 180 //設置字體和大小 181 canvas.setFontAndSize(font.getBaseFont(), fontWidth); 182 //設置字體的輸出位置 183 canvas.setTextMatrix(x, y+fontWidth/10+0.5f); 184 //要輸出的text 185 canvas.showText(keyWordNew); 186 187 canvas.endText(); 188 } 189 } 190 stamper.close(); 191 reader.close(); 192 System.out.println("complete"); 193 } 194 195 /** 196 * 替換pdf中指定文字 197 * @param src 目標pdf路徑 198 * @param dest 新pdf的路徑 199 * @param keyWord 替換的文字 200 * @param keyWordNew 替換后的文字 201 * @throws Exception 202 */ 203 public static void pdfReplace(String src,String dest,String keyWord,String keyWordNew) throws Exception{ 204 manipulatePdf(src,dest,matchAll(src,keyWord),keyWord,keyWordNew); 205 } 206 }
PS:以上就是功能所有實現,不足之處和適應場景程序備注里面已詳細說明,每個方法的作用及參數說明也都在程序中備注說明。
