JAVA替換PDF文字


前言:

  以下是通過網上查閱資料,東拼西湊實現的一個使用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:以上就是功能所有實現,不足之處和適應場景程序備注里面已詳細說明,每個方法的作用及參數說明也都在程序中備注說明。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM