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:以上就是功能所有实现,不足之处和适应场景程序备注里面已详细说明,每个方法的作用及参数说明也都在程序中备注说明。

posted @ 2019-06-12 11:34  北漂追梦人  阅读(11254)  评论(5编辑  收藏  举报