使用itext直接替換PDF中的文本


直接說問題,itext沒有直接提供替換PDF中文本的接口(查看資料得到的結論是PDF不支持這種操作),不過存在解決思路:在需要替換的文本上覆蓋新的文本。按照這個思路我們需要解決以下幾個問題:

 

  • itext怎樣增加白色底的覆蓋層
  • 找到覆蓋層的位置(左頂點的位置)和高度與寬帶
這樣做的目的是什么了?也告訴下大家,比如:現在要你將業務數據導出成PDF存檔,且PDF的模板有現成的。對我們寫程序的來說,變化的只是部分數據,假如我們可以直接替換里面的數據,是不是可以節省我們的開發時間。

1、itext怎樣增加覆蓋層?

itext在自己的Demo中提供了很多案例代碼,從中我們可以看到高亮的案例
查看itext代碼
[java]  view plain  copy
 
  1. /* 
  2.  * This example was written in answer to the question: 
  3.  * http://stackoverflow.com/questions/33952183 
  4.  */  
  5. package sandbox.stamper;  
  6.    
  7. import com.itextpdf.text.BaseColor;  
  8. import com.itextpdf.text.DocumentException;  
  9. import com.itextpdf.text.pdf.PdfContentByte;  
  10. import com.itextpdf.text.pdf.PdfReader;  
  11. import com.itextpdf.text.pdf.PdfStamper;  
  12. import java.io.File;  
  13. import java.io.FileOutputStream;  
  14. import java.io.IOException;  
  15.    
  16. /** 
  17.  * 
  18.  * @author Bruno Lowagie (iText Software) 
  19.  */  
  20. public class HighLightByAddingContent {  
  21.    
  22.     public static final String SRC = "resources/pdfs/hello.pdf";  
  23.     public static final String DEST = "results/stamper/hello_highlighted.pdf";  
  24.     public static void main(String[] args) throws IOException, DocumentException {  
  25.         File file = new File(DEST);  
  26.         file.getParentFile().mkdirs();  
  27.         new HighLightByAddingContent().manipulatePdf(SRC, DEST);  
  28.     }  
  29.    
  30.     public void manipulatePdf(String src, String dest) throws IOException, DocumentException {  
  31.         PdfReader reader = new PdfReader(src);  
  32.         PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));  
  33.         PdfContentByte canvas = stamper.getUnderContent(1);  
  34.         canvas.saveState();  
  35.         canvas.setColorFill(BaseColor.YELLOW);  
  36.         canvas.rectangle(36, 786, 66, 16);  
  37.         canvas.fill();  
  38.         canvas.restoreState();  
  39.         stamper.close();  
  40.         reader.close();  
  41.     }  
  42. }  
 這里可以在任意位置產生一個層,符合我們的“遮蓋層”的要求,不過,通過測試發現此段代碼存在一個問題點,它無法遮擋住文字,只是添加了一個背景層。為了達到我們的要求,我們只需要修改一處地方:
[java]  view plain  copy
 
  1. PdfContentByte canvas = stamper.getUnderContent(1);  //變成 PdfContentByte canvas = stamper.getOverContent(1);  
到目前為止,我們的遮蓋層已添加,后面我們還需要的就是在新的遮蓋層上寫上自己的文字,代碼如下:
[java]  view plain  copy
 
  1. /********************************************************************** 
  2.  * <pre> 
  3.  * FILE : HighLightByAddingContent.java 
  4.  * CLASS : HighLightByAddingContent 
  5.  * 
  6.  * 
  7.  * FUNCTION : TODO 
  8.  * 
  9.  * 
  10.  *====================================================================== 
  11.  * CHANGE HISTORY LOG 
  12.  *---------------------------------------------------------------------- 
  13.  * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ. 
  14.  *---------------------------------------------------------------------- 
  15.  *       
  16.  * DESCRIPTION: 
  17.  * </pre> 
  18.  ***********************************************************************/  
  19.   
  20. package com.cx.itext;  
  21.   
  22. import java.io.File;  
  23. import java.io.FileOutputStream;  
  24. import java.io.IOException;  
  25. import java.net.URLDecoder;  
  26.   
  27. import com.itextpdf.text.BaseColor;  
  28. import com.itextpdf.text.DocumentException;  
  29. import com.itextpdf.text.Font;  
  30. import com.itextpdf.text.pdf.BaseFont;  
  31. import com.itextpdf.text.pdf.PdfContentByte;  
  32. import com.itextpdf.text.pdf.PdfReader;  
  33. import com.itextpdf.text.pdf.PdfStamper;  
  34.   
  35. public class HighLightByAddingContent {  
  36.   
  37.    @SuppressWarnings("deprecation")  
  38.    public static final String SRC = URLDecoder.decode(HighLightByAddingContent.class.getResource("ticket.pdf").getFile());  
  39.    public static final String DEST = "I://ticket.pdf";  
  40.    public static void main(String[] args) throws IOException, DocumentException {  
  41.        File file = new File(DEST);  
  42.        file.getParentFile().mkdirs();  
  43.        new HighLightByAddingContent().manipulatePdf(SRC, DEST);  
  44.    }  
  45.   
  46.    public void manipulatePdf(String src, String dest) throws IOException, DocumentException {  
  47.        PdfReader reader = new PdfReader(src);  
  48.        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));  
  49.        PdfContentByte canvas = stamper.getOverContent(1);  
  50.        float height=595;  
  51.        System.out.println(canvas.getHorizontalScaling());  
  52.        float x,y;  
  53.        x= 216;  
  54.        y = height -49.09F;  
  55.        canvas.saveState();  
  56.        canvas.setColorFill(BaseColor.WHITE);  
  57.        canvas.rectangle(x, y-5, 43, 15);  
  58.          
  59.        canvas.fill();  
  60.        canvas.restoreState();  
  61.      //開始寫入文本   
  62.        canvas.beginText();   
  63.        //BaseFont bf = BaseFont.createFont(URLDecoder.decode(CutAndPaste.class.getResource("/AdobeSongStd-Light.otf").getFile()), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);  
  64.        BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);  
  65.        Font font = new Font(bf,10,Font.BOLD);   
  66.        //設置字體和大小   
  67.        canvas.setFontAndSize(font.getBaseFont(), 10);    
  68.        //設置字體的輸出位置   
  69.        canvas.setTextMatrix(x, y);    
  70.        //要輸出的text   
  71.        canvas.showText("多退少補" );     
  72.          
  73.        //設置字體的輸出位置   
  74.        canvas.setFontAndSize(font.getBaseFont(), 20);    
  75.        canvas.setTextMatrix(x, y-90);    
  76.        //要輸出的text   
  77.        canvas.showText("多退少補" );     
  78.          
  79.        canvas.endText();  
  80.        stamper.close();  
  81.        reader.close();  
  82.        System.out.println("complete");  
  83.    }  
  84. }  

2、找到覆蓋層的位置(左頂點的位置)和高度與寬帶

我的第一個想法是通過工具得到替換文本的具體位置,雖然這個方法不怎么好,不過確實可行。使用到的工具是常用的Adobe Reader,以下是正常頁面(PDF是網上搜的,百度key:“申請 filetype:pdf”):
 
Adobe提供了測量工具,我們可以通過“編輯-->分析-->測量工具”看到如下頁面:
此時,我們雖然可以直接測量,但是測量默認顯示的厘米,與itext需要設置的單位不一致,我們需要手工換算下(1英寸=72點)。不過,adobe可以幫我們省掉換算的工作,右鍵點擊,出現以下選項(需要在測量功能下右鍵):
“更改比例”可以幫助我們完成換算工作。(ps:“顯示標尺”是一個不錯的選項)。最后的畫面如下:
最后,需要提醒下,itext的Y是從下往上算的。
 
這樣得到位置是不是太不方便了。那我們是否可以通過itext自動計算出我們需要的位置?代碼如下(從網上COPY,不記得具體來源,支持作者)
[java]  view plain  copy
 
  1. /********************************************************************** 
  2.  * <pre> 
  3.  * FILE : Demo.java 
  4.  * CLASS : Demo 
  5.  * 
  6.  * AUTHOR : caoxu-yiyang@qq.com 
  7.  * 
  8.  * FUNCTION : TODO 
  9.  * 
  10.  * 
  11.  *====================================================================== 
  12.  * CHANGE HISTORY LOG 
  13.  *---------------------------------------------------------------------- 
  14.  * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ. 
  15.  *---------------------------------------------------------------------- 
  16.  *          |2016年11月9日|caoxu-yiyang@qq.com| Created | 
  17.  * DESCRIPTION: 
  18.  * </pre> 
  19.  ***********************************************************************/  
  20.   
  21. package com.cx.itext;  
  22.   
  23. import java.io.IOException;  
  24. import com.itextpdf.awt.geom.Rectangle2D.Float;  
  25. import com.itextpdf.text.pdf.PdfReader;  
  26. import com.itextpdf.text.pdf.parser.ImageRenderInfo;  
  27. import com.itextpdf.text.pdf.parser.PdfReaderContentParser;  
  28. import com.itextpdf.text.pdf.parser.RenderListener;  
  29. import com.itextpdf.text.pdf.parser.TextRenderInfo;  
  30.   
  31. public class Demo  
  32. {  
  33.     // 定義關鍵字  
  34.     private static String KEY_WORD = "結算區分";  
  35.     // 定義返回值  
  36.     private static float[] resu = null;  
  37.     // 定義返回頁碼  
  38.     private static int i = 0;  
  39.   
  40.     public static void main(String[] args) {  
  41.         float[] point = getKeyWords("I://ticket_in.pdf");  
  42.     }  
  43.     /* 
  44.      * 返回關鍵字所在的坐標和頁數 float[0] >> X float[1] >> Y float[2] >> page 
  45.      */  
  46.     private static float[] getKeyWords(String filePath)  
  47.     {  
  48.         try  
  49.         {  
  50.             PdfReader pdfReader = new PdfReader(filePath);  
  51.             int pageNum = pdfReader.getNumberOfPages();  
  52.             PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(  
  53.                     pdfReader);  
  54.   
  55.             // 下標從1開始  
  56.             for (i = 1; i <= pageNum; i++)  
  57.             {  
  58.                 pdfReaderContentParser.processContent(i, new RenderListener()  
  59.                 {  
  60.   
  61.                     @Override  
  62.                     public void renderText(TextRenderInfo textRenderInfo)  
  63.                     {  
  64.                         String text = textRenderInfo.getText();  
  65.                         if (null != text && text.contains(KEY_WORD))  
  66.                         {  
  67.                             Float boundingRectange = textRenderInfo  
  68.                                     .getBaseline().getBoundingRectange();  
  69.                             resu = new float[3];  
  70.                             System.out.println("======="+text);  
  71.                             System.out.println("h:"+boundingRectange.getHeight());  
  72.                             System.out.println("w:"+boundingRectange.width);  
  73.                             System.out.println("centerX:"+boundingRectange.getCenterX());  
  74.                             System.out.println("centerY:"+boundingRectange.getCenterY());  
  75.                             System.out.println("x:"+boundingRectange.getX());  
  76.                             System.out.println("y:"+boundingRectange.getY());  
  77.                             System.out.println("maxX:"+boundingRectange.getMaxX());  
  78.                             System.out.println("maxY:"+boundingRectange.getMaxY());  
  79.                             System.out.println("minX:"+boundingRectange.getMinX());  
  80.                             System.out.println("minY:"+boundingRectange.getMinY());  
  81.                             resu[0] = boundingRectange.x;  
  82.                             resu[1] = boundingRectange.y;  
  83.                             resu[2] = i;  
  84.                         }  
  85.                     }  
  86.   
  87.                     @Override  
  88.                     public void renderImage(ImageRenderInfo arg0)  
  89.                     {  
  90.                     }  
  91.   
  92.                     @Override  
  93.                     public void endTextBlock()  
  94.                     {  
  95.   
  96.                     }  
  97.   
  98.                     @Override  
  99.                     public void beginTextBlock()  
  100.                     {  
  101.                     }  
  102.                 });  
  103.             }  
  104.         } catch (IOException e)  
  105.         {  
  106.             e.printStackTrace();  
  107.         }  
  108.         return resu;  
  109.     }  
  110.   
  111. }  
結合以上的,我們就可以寫一個自動替換PDF文本的類,具體使用如下:
[java]  view plain  copy
 
  1. public static void main(String[] args) throws IOException, DocumentException {  
  2.         PdfReplacer textReplacer = new PdfReplacer("I://test.pdf");  
  3.         textReplacer.replaceText("陳坤", "小白");  
  4.         textReplacer.replaceText("本科", "社會大學");  
  5.         textReplacer.replaceText("0755-29493863", "15112345678");  
  6.         textReplacer.toPdf("I://ticket_out.pdf");  
  7.     }  

原始PDF:
未替換
替換之后的(紅色背景只是方便大家看到差別):
(第一次認真寫博客,感覺感覺好花時間了,佩服那些堅持寫博客的人~~)

 

 

補上相關代碼(還在完善中),總共4個類

代碼中有幾個地方要說明下:

1、由於自動計算得到的高度都是0,所有我這邊默認的都是12,大家要根據實際情況來設

2、除了可以讓代碼自己計算位置之外,也可以通過replaceText的重載方法強制指定替換區域。

 

[java]  view plain  copy
 
  1. /********************************************************************** 
  2.  * <pre> 
  3.  * FILE : PdfTextReplacer.java 
  4.  * CLASS : PdfTextReplacer 
  5.  * 
  6.  * AUTHOR : caoxu-yiyang@qq.com 
  7.  * 
  8.  * FUNCTION : TODO 
  9.  * 
  10.  * 
  11.  *====================================================================== 
  12.  * CHANGE HISTORY LOG 
  13.  *---------------------------------------------------------------------- 
  14.  * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ. 
  15.  *---------------------------------------------------------------------- 
  16.  *          |2016年11月8日|caoxu-yiyang@qq.com| Created | 
  17.  * DESCRIPTION: 
  18.  * </pre> 
  19.  ***********************************************************************/  
  20.   
  21. package com.cx.itext;  
  22.   
  23. import java.io.ByteArrayOutputStream;  
  24. import java.io.FileInputStream;  
  25. import java.io.FileOutputStream;  
  26. import java.io.IOException;  
  27. import java.util.HashMap;  
  28. import java.util.Map;  
  29. import java.util.Map.Entry;  
  30. import java.util.Set;  
  31.   
  32. import com.itextpdf.text.BaseColor;  
  33. import com.itextpdf.text.DocumentException;  
  34. import com.itextpdf.text.Font;  
  35. import com.itextpdf.text.log.Logger;  
  36. import com.itextpdf.text.log.LoggerFactory;  
  37. import com.itextpdf.text.pdf.BaseFont;  
  38. import com.itextpdf.text.pdf.PdfContentByte;  
  39. import com.itextpdf.text.pdf.PdfReader;  
  40. import com.itextpdf.text.pdf.PdfStamper;  
  41.   
  42. /** 
  43.  * 替換PDF文件某個區域內的文本 
  44.  * @user : caoxu-yiyang@qq.com 
  45.  * @date : 2016年11月8日 
  46.  */  
  47. public class PdfReplacer {  
  48.     private static final Logger logger = LoggerFactory.getLogger(PdfReplacer.class);  
  49.       
  50.     private int fontSize;  
  51.     private Map<String, ReplaceRegion> replaceRegionMap = new HashMap<String, ReplaceRegion>();  
  52.     private Map<String, Object> replaceTextMap =new HashMap<String, Object>();  
  53.     private ByteArrayOutputStream output;  
  54.     private PdfReader reader;  
  55.     private PdfStamper stamper;  
  56.     private PdfContentByte canvas;  
  57.     private Font font;  
  58.       
  59.     public PdfReplacer(byte[] pdfBytes) throws DocumentException, IOException{  
  60.         init(pdfBytes);  
  61.     }  
  62.       
  63.     public PdfReplacer(String fileName) throws IOException, DocumentException{  
  64.         FileInputStream in = null;  
  65.         try{  
  66.             in =new FileInputStream(fileName);  
  67.             byte[] pdfBytes = new byte[in.available()];  
  68.             in.read(pdfBytes);  
  69.             init(pdfBytes);  
  70.         }finally{  
  71.             in.close();  
  72.         }  
  73.     }  
  74.       
  75.     private void init(byte[] pdfBytes) throws DocumentException, IOException{  
  76.         logger.info("初始化開始");  
  77.         reader = new PdfReader(pdfBytes);  
  78.         output = new ByteArrayOutputStream();  
  79.         stamper = new PdfStamper(reader, output);  
  80.         canvas = stamper.getOverContent(1);  
  81.         setFont(10);  
  82.         logger.info("初始化成功");  
  83.     }  
  84.       
  85.     private void close() throws DocumentException, IOException{  
  86.         if(reader != null){  
  87.             reader.close();  
  88.         }  
  89.         if(output != null){  
  90.             output.close();  
  91.         }  
  92.           
  93.         output=null;  
  94.         replaceRegionMap=null;  
  95.         replaceTextMap=null;  
  96.     }  
  97.       
  98.     public void replaceText(float x, float y, float w,float h, String text){  
  99.         ReplaceRegion region = new ReplaceRegion(text);     //用文本作為別名  
  100.         region.setH(h);  
  101.         region.setW(w);  
  102.         region.setX(x);  
  103.         region.setY(y);  
  104.         addReplaceRegion(region);  
  105.         this.replaceText(text, text);  
  106.     }  
  107.       
  108.     public void replaceText(String name, String text){  
  109.         this.replaceTextMap.put(name, text);  
  110.     }  
  111.       
  112.     /** 
  113.      * 替換文本 
  114.      * @throws IOException  
  115.      * @throws DocumentException  
  116.      * @user : caoxu-yiyang@qq.com 
  117.      * @date : 2016年11月9日 
  118.      */  
  119.     private void process() throws DocumentException, IOException{  
  120.         try{  
  121.             parseReplaceText();  
  122.             canvas.saveState();  
  123.             Set<Entry<String, ReplaceRegion>> entrys = replaceRegionMap.entrySet();  
  124.             for (Entry<String, ReplaceRegion> entry : entrys) {  
  125.                 ReplaceRegion value = entry.getValue();  
  126.                 canvas.setColorFill(BaseColor.RED);  
  127.                 canvas.rectangle(value.getX(),value.getY(),value.getW(),value.getH());  
  128.             }  
  129.             canvas.fill();  
  130.             canvas.restoreState();  
  131.             //開始寫入文本   
  132.             canvas.beginText();   
  133.             for (Entry<String, ReplaceRegion> entry : entrys) {  
  134.                 ReplaceRegion value = entry.getValue();  
  135.                 //設置字體  
  136.                 canvas.setFontAndSize(font.getBaseFont(), getFontSize());    
  137.                 canvas.setTextMatrix(value.getX(),value.getY()+2/*修正背景與文本的相對位置*/);    
  138.                 canvas.showText((String) replaceTextMap.get(value.getAliasName()));     
  139.             }  
  140.             canvas.endText();  
  141.         }finally{  
  142.             if(stamper != null){  
  143.                 stamper.close();  
  144.             }  
  145.         }  
  146.     }  
  147.       
  148.     /** 
  149.      * 未指定具體的替換位置時,系統自動查找位置 
  150.      * @user : caoxu-yiyang@qq.com 
  151.      * @date : 2016年11月9日 
  152.      */  
  153.     private void parseReplaceText() {  
  154.         PdfPositionParse parse = new PdfPositionParse(reader);  
  155.         Set<Entry<String, Object>> entrys = this.replaceTextMap.entrySet();  
  156.         for (Entry<String, Object> entry : entrys) {  
  157.             if(this.replaceRegionMap.get(entry.getKey()) == null){  
  158.                 parse.addFindText(entry.getKey());  
  159.             }  
  160.         }  
  161.           
  162.         try {  
  163.             Map<String, ReplaceRegion> parseResult = parse.parse();  
  164.             Set<Entry<String, ReplaceRegion>> parseEntrys = parseResult.entrySet();  
  165.             for (Entry<String, ReplaceRegion> entry : parseEntrys) {  
  166.                 if(entry.getValue() != null){  
  167.                     this.replaceRegionMap.put(entry.getKey(), entry.getValue());  
  168.                 }  
  169.             }  
  170.         } catch (IOException e) {  
  171.             logger.error(e.getMessage(), e);  
  172.         }  
  173.           
  174.     }  
  175.   
  176.     /** 
  177.      * 生成新的PDF文件 
  178.      * @user : caoxu-yiyang@qq.com 
  179.      * @date : 2016年11月9日 
  180.      * @param fileName 
  181.      * @throws DocumentException 
  182.      * @throws IOException 
  183.      */  
  184.     public void toPdf(String fileName) throws DocumentException, IOException{  
  185.         FileOutputStream fileOutputStream = null;  
  186.         try{  
  187.             process();  
  188.             fileOutputStream = new FileOutputStream(fileName);  
  189.             fileOutputStream.write(output.toByteArray());  
  190.             fileOutputStream.flush();  
  191.         }catch(IOException e){  
  192.             logger.error(e.getMessage(), e);  
  193.             throw e;  
  194.         }finally{  
  195.             if(fileOutputStream != null){  
  196.                 fileOutputStream.close();  
  197.             }  
  198.             close();  
  199.         }  
  200.         logger.info("文件生成成功");  
  201.     }  
  202.       
  203.     /** 
  204.      * 將生成的PDF文件轉換成二進制數組 
  205.      * @user : caoxu-yiyang@qq.com 
  206.      * @date : 2016年11月9日 
  207.      * @return 
  208.      * @throws DocumentException 
  209.      * @throws IOException 
  210.      */  
  211.     public byte[] toBytes() throws DocumentException, IOException{  
  212.         try{  
  213.             process();  
  214.             logger.info("二進制數據生成成功");  
  215.             return output.toByteArray();  
  216.         }finally{  
  217.             close();  
  218.         }  
  219.     }  
  220.       
  221.     /** 
  222.      * 添加替換區域 
  223.      * @user : caoxu-yiyang@qq.com 
  224.      * @date : 2016年11月9日 
  225.      * @param replaceRegion 
  226.      */  
  227.     public void addReplaceRegion(ReplaceRegion replaceRegion){  
  228.         this.replaceRegionMap.put(replaceRegion.getAliasName(), replaceRegion);  
  229.     }  
  230.       
  231.     /** 
  232.      * 通過別名得到替換區域 
  233.      * @user : caoxu-yiyang@qq.com 
  234.      * @date : 2016年11月9日 
  235.      * @param aliasName 
  236.      * @return 
  237.      */  
  238.     public ReplaceRegion getReplaceRegion(String aliasName){  
  239.         return this.replaceRegionMap.get(aliasName);  
  240.     }  
  241.   
  242.     public int getFontSize() {  
  243.         return fontSize;  
  244.     }  
  245.   
  246.     /** 
  247.      * 設置字體大小 
  248.      * @user : caoxu-yiyang@qq.com 
  249.      * @date : 2016年11月9日 
  250.      * @param fontSize 
  251.      * @throws DocumentException 
  252.      * @throws IOException 
  253.      */  
  254.     public void setFont(int fontSize) throws DocumentException, IOException{  
  255.         if(fontSize != this.fontSize){  
  256.             this.fontSize = fontSize;  
  257.             BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);  
  258.             font = new Font(bf,this.fontSize,Font.BOLD);  
  259.         }  
  260.     }  
  261.       
  262.     public void setFont(Font font){  
  263.         if(font == null){  
  264.             throw new NullPointerException("font is null");  
  265.         }  
  266.         this.font = font;  
  267.     }  
  268.       
  269.     public static void main(String[] args) throws IOException, DocumentException {  
  270.         PdfReplacer textReplacer = new PdfReplacer("I://test.pdf");  
  271.         textReplacer.replaceText("陳坤", "小白");  
  272.         textReplacer.replaceText("本科", "社會大學");  
  273.         textReplacer.replaceText("0755-29493863", "15112345678");  
  274.         textReplacer.toPdf("I://ticket_out.pdf");  
  275.     }  
  276. }  

 

[java]  view plain  copy
 
  1. /********************************************************************** 
  2.  * <pre> 
  3.  * FILE : ReplaceRegion.java 
  4.  * CLASS : ReplaceRegion 
  5.  * 
  6.  * AUTHOR : caoxu-yiyang@qq.com 
  7.  * 
  8.  * FUNCTION : TODO 
  9.  * 
  10.  * 
  11.  *====================================================================== 
  12.  * CHANGE HISTORY LOG 
  13.  *---------------------------------------------------------------------- 
  14.  * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ. 
  15.  *---------------------------------------------------------------------- 
  16.  *          |2016年11月9日|caoxu-yiyang@qq.com| Created | 
  17.  * DESCRIPTION: 
  18.  * </pre> 
  19.  ***********************************************************************/  
  20.   
  21. package com.cx.itext;  
  22.   
  23. /** 
  24.  * 需要替換的區域 
  25.  * @user : caoxu-yiyang@qq.com 
  26.  * @date : 2016年11月9日 
  27.  */  
  28. public class ReplaceRegion {  
  29.   
  30.     private String aliasName;  
  31.     private Float x;  
  32.     private Float y;  
  33.     private Float w;  
  34.     private Float h;  
  35.       
  36.     public ReplaceRegion(String aliasName){  
  37.         this.aliasName = aliasName;  
  38.     }  
  39.       
  40.     /** 
  41.      * 替換區域的別名 
  42.      * @user : caoxu-yiyang@qq.com 
  43.      * @date : 2016年11月9日 
  44.      * @return 
  45.      */  
  46.     public String getAliasName() {  
  47.         return aliasName;  
  48.     }  
  49.     public void setAliasName(String aliasName) {  
  50.         this.aliasName = aliasName;  
  51.     }  
  52.     public Float getX() {  
  53.         return x;  
  54.     }  
  55.     public void setX(Float x) {  
  56.         this.x = x;  
  57.     }  
  58.     public Float getY() {  
  59.         return y;  
  60.     }  
  61.     public void setY(Float y) {  
  62.         this.y = y;  
  63.     }  
  64.     public Float getW() {  
  65.         return w;  
  66.     }  
  67.     public void setW(Float w) {  
  68.         this.w = w;  
  69.     }  
  70.     public Float getH() {  
  71.         return h;  
  72.     }  
  73.     public void setH(Float h) {  
  74.         this.h = h;  
  75.     }  
  76. }  

 

[java]  view plain  copy
 
  1. /********************************************************************** 
  2.  * <pre> 
  3.  * FILE : PdfPositionParse.java 
  4.  * CLASS : PdfPositionParse 
  5.  * 
  6.  * AUTHOR : caoxu-yiyang@qq.com 
  7.  * 
  8.  * FUNCTION : TODO 
  9.  * 
  10.  * 
  11.  *====================================================================== 
  12.  * CHANGE HISTORY LOG 
  13.  *---------------------------------------------------------------------- 
  14.  * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ. 
  15.  *---------------------------------------------------------------------- 
  16.  *          |2016年11月9日|caoxu-yiyang@qq.com| Created | 
  17.  * DESCRIPTION: 
  18.  * </pre> 
  19.  ***********************************************************************/  
  20.   
  21. package com.cx.itext;  
  22.   
  23. import java.io.FileInputStream;  
  24. import java.io.IOException;  
  25. import java.util.ArrayList;  
  26. import java.util.List;  
  27. import java.util.Map;  
  28.   
  29. import com.cx.itext.listener.PositionRenderListener;  
  30. import com.itextpdf.text.pdf.PdfReader;  
  31. import com.itextpdf.text.pdf.parser.PdfReaderContentParser;  
  32.   
  33. /** 
  34.  * 解析PDF中文本的x,y位置 
  35.  * @user : caoxu-yiyang@qq.com 
  36.  * @date : 2016年11月9日 
  37.  */  
  38. public class PdfPositionParse {  
  39.   
  40.     private PdfReader reader;  
  41.     private List<String> findText = new ArrayList<String>();    //需要查找的文本  
  42.     private PdfReaderContentParser parser;  
  43.   
  44.     public PdfPositionParse(String fileName) throws IOException{  
  45.         FileInputStream in = null;  
  46.         try{  
  47.             in =new FileInputStream(fileName);  
  48.             byte[] bytes = new byte[in.available()];  
  49.             in.read(bytes);  
  50.             init(bytes);  
  51.         }finally{  
  52.             in.close();  
  53.         }  
  54.     }  
  55.       
  56.     public PdfPositionParse(byte[] bytes) throws IOException{  
  57.         init(bytes);  
  58.     }  
  59.       
  60.     private boolean needClose = true;  
  61.     /** 
  62.      * 傳遞進來的reader不會在PdfPositionParse結束時關閉 
  63.      * @user : caoxu-yiyang@qq.com 
  64.      * @date : 2016年11月9日 
  65.      * @param reader 
  66.      */  
  67.     public PdfPositionParse(PdfReader reader){  
  68.         this.reader = reader;  
  69.         parser = new PdfReaderContentParser(reader);  
  70.         needClose = false;  
  71.     }  
  72.   
  73.     public void addFindText(String text){  
  74.         this.findText.add(text);  
  75.     }  
  76.       
  77.     private void init(byte[] bytes) throws IOException {  
  78.         reader = new PdfReader(bytes);  
  79.         parser = new PdfReaderContentParser(reader);  
  80.     }  
  81.       
  82.     /** 
  83.      * 解析文本 
  84.      * @user : caoxu-yiyang@qq.com 
  85.      * @date : 2016年11月9日 
  86.      * @throws IOException 
  87.      */  
  88.     public Map<String, ReplaceRegion> parse() throws IOException{  
  89.         try{  
  90.             if(this.findText.size() == 0){  
  91.                 throw new NullPointerException("沒有需要查找的文本");  
  92.             }  
  93.             PositionRenderListener listener = new PositionRenderListener(this.findText);  
  94.             parser.processContent(1, listener);  
  95.             return listener.getResult();  
  96.         }finally{  
  97.             if(reader != null && needClose){  
  98.                 reader.close();  
  99.             }  
  100.         }  
  101.     }  
  102. }  

 

[java]  view plain  copy
 
  1. /********************************************************************** 
  2.  * <pre> 
  3.  * FILE : PositionRenderListener.java 
  4.  * CLASS : PositionRenderListener 
  5.  * 
  6.  * AUTHOR : caoxu-yiyang@qq.com 
  7.  * 
  8.  * FUNCTION : TODO 
  9.  * 
  10.  * 
  11.  *====================================================================== 
  12.  * CHANGE HISTORY LOG 
  13.  *---------------------------------------------------------------------- 
  14.  * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ. 
  15.  *---------------------------------------------------------------------- 
  16.  *          |2016年11月9日|caoxu-yiyang@qq.com| Created | 
  17.  * DESCRIPTION: 
  18.  * </pre> 
  19.  ***********************************************************************/  
  20.   
  21. package com.cx.itext.listener;  
  22.   
  23. import java.util.HashMap;  
  24. import java.util.List;  
  25. import java.util.Map;  
  26.   
  27. import com.cx.itext.ReplaceRegion;  
  28. import com.itextpdf.awt.geom.Rectangle2D.Float;  
  29. import com.itextpdf.text.pdf.parser.ImageRenderInfo;  
  30. import com.itextpdf.text.pdf.parser.RenderListener;  
  31. import com.itextpdf.text.pdf.parser.TextRenderInfo;  
  32.   
  33. /** 
  34.  * pdf渲染監聽,當找到渲染的文本時,得到文本的坐標x,y,w,h 
  35.  * @user : caoxu-yiyang@qq.com 
  36.  * @date : 2016年11月9日 
  37.  */  
  38. public class PositionRenderListener implements RenderListener{  
  39.       
  40.     private List<String> findText;  
  41.     private float defaultH;     ///出現無法取到值的情況,默認為12  
  42.     private float fixHeight;    //可能出現無法完全覆蓋的情況,提供修正的參數,默認為2  
  43.     public PositionRenderListener(List<String> findText, float defaultH,float fixHeight) {  
  44.         this.findText = findText;  
  45.         this.defaultH = defaultH;  
  46.         this.fixHeight = fixHeight;  
  47.     }  
  48.   
  49.     public PositionRenderListener(List<String> findText) {  
  50.         this.findText = findText;  
  51.         this.defaultH = 12;  
  52.         this.fixHeight = 2;  
  53.     }  
  54.       
  55.     @Override  
  56.     public void beginTextBlock() {  
  57.           
  58.     }  
  59.   
  60.     @Override  
  61.     public void endTextBlock() {  
  62.           
  63.     }  
  64.   
  65.     @Override  
  66.     public void renderImage(ImageRenderInfo imageInfo) {  
  67.     }  
  68.   
  69.     private Map<String, ReplaceRegion> result = new HashMap<String, ReplaceRegion>();  
  70.     @Override  
  71.     public void renderText(TextRenderInfo textInfo) {  
  72.         String text = textInfo.getText();  
  73.         for (String keyWord : findText) {  
  74.             if (null != text && text.equals(keyWord)){  
  75.                 Float bound = textInfo.getBaseline().getBoundingRectange();  
  76.                 ReplaceRegion region = new ReplaceRegion(keyWord);  
  77.                 region.setH(bound.height == 0 ? defaultH : bound.height);  
  78.                 region.setW(bound.width);  
  79.                 region.setX(bound.x);  
  80.                 region.setY(bound.y-this.fixHeight);  
  81.                 result.put(keyWord, region);  
  82.             }  
  83.         }  
  84.     }  
  85.   
  86.     public Map<String, ReplaceRegion> getResult() {  
  87.         for (String key : findText) {   //補充沒有找到的數據  
  88.             if(this.result.get(key) == null){  
  89.                 this.result.put(key, null);  
  90.             }  
  91.         }  
  92.         return this.result;  
  93.     }  
  94. }  

我用到的jar包如下:

 


大家可以從官網下載,可以構建maven項目省去自己找包的麻煩。如果沒有用maven又想下載具體的jar包,可以直接訪問maven倉庫下載:http://mvnrepository.com/


免責聲明!

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



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