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