zoukankan      html  css  js  c++  java
  • 使用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/

  • 相关阅读:
    符合Web标准的表格——CSS表格
    导航 Jquery
    【IOS学习】之三、图像视图&文本字段
    【VC++积累】之四、文件删除,复制
    Xcode 4.4中LLVM compiler 4.0带来的ObjectiveC新语法特性
    最快的存储过程分页 50W
    【网络编程】之十一、重叠IO Overlapped IO 完成例程
    OD使用教程15 调试篇15
    线性表12|循环链表 数据结构和算法17
    线性表13|约瑟夫问题 数据结构和算法18
  • 原文地址:https://www.cnblogs.com/Alex80/p/8116955.html
Copyright © 2011-2022 走看看