• java itext替换PDF中的文本


    itext没有提供直接替换PDF文本的接口,我们可以通过在原有的文本区域覆盖一个遮挡层,再在上面加上文本来实现。

    所需jar包:

    1.先在PDF需要替换的位置覆盖一个白色遮挡层(颜色可根据PDF文字背景色自行定义)

    import com.itextpdf.text.BaseColor;  
    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.pdf.PdfContentByte;  
    import com.itextpdf.text.pdf.PdfReader;  
    import com.itextpdf.text.pdf.PdfStamper;  
    import java.io.File;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
       
    /**
     * <p>PDF增加遮盖层<p>
     * @version 1.0
     * @author li_hao
     * @date 2018年3月14日
     */
    public class HighLightByAddingContent {
         
        public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
            PdfReader reader = new PdfReader(src);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
            PdfContentByte canvas = stamper.getUnderContent(1);  //不可遮挡文字
    //        PdfContentByte canvas = stamper.getOverContent(1);  //可以遮挡文字
            canvas.saveState();
            canvas.setColorFill(BaseColor.YELLOW);  //黄色遮挡层
    //        canvas.setColorFill(BaseColor.WHITE);  //白色遮挡层
            canvas.rectangle(116, 726, 66, 16);
            canvas.fill();
            canvas.restoreState();
            stamper.close();
            reader.close();
        }
        
        /**
         * 测试
         */
        public static void main(String[] args) throws IOException, DocumentException {
            
            String SRC = "D:/testpdf/test.pdf";
            String DEST = "D:/testpdf/test2.pdf";
            
            File file = new File(DEST);
            file.getParentFile().mkdirs();
            new HighLightByAddingContent().manipulatePdf(SRC, DEST);
        }
    }

    测试结果(不遮挡文字+黄色背景):

               

              

    测试结果(遮挡文字+白色背景):

             

    2. PDF增加遮盖层+文字

    import com.itextpdf.text.BaseColor;  
    import com.itextpdf.text.DocumentException;
    import com.itextpdf.text.Font;
    import com.itextpdf.text.pdf.BaseFont;
    import com.itextpdf.text.pdf.PdfContentByte;  
    import com.itextpdf.text.pdf.PdfReader;  
    import com.itextpdf.text.pdf.PdfStamper;  
    import java.io.File;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
       
    /**
     * <p>PDF增加遮盖层+文字<p>
     * @version 1.0
     * @author li_hao
     * @date 2018年3月13日
     */
    public class HighLightByAddingContent {  
       
        
        public void manipulatePdf(String src, String dest) throws IOException, DocumentException {  
            PdfReader reader = new PdfReader(src);  
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));  
            //添加一个遮挡处,可以把原内容遮住,后面在上面写入内容
    //        PdfContentByte canvas = stamper.getUnderContent(1);  //不可以遮挡文字
            PdfContentByte canvas = stamper.getOverContent(1);  //可以遮挡文字
            
            float height=780;  
            System.out.println(canvas.getHorizontalScaling());  
            float x,y;  
            x= 116;  
            y = height -49.09F;  
            canvas.saveState();  
            canvas.setColorFill(BaseColor.YELLOW);  //遮挡层颜色:黄色
    //      canvas.setColorFill(BaseColor.WHITE);  //遮挡层颜色:白色
            canvas.rectangle(x, y-5, 43, 15);  
            
            canvas.fill();  
            canvas.restoreState();  
            //开始写入文本   
            canvas.beginText();   
            //BaseFont bf = BaseFont.createFont(URLDecoder.decode(CutAndPaste.class.getResource("/AdobeSongStd-Light.otf").getFile()), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);  
            BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);  
            Font font = new Font(bf,10,Font.BOLD);   
            //设置字体和大小   
            canvas.setFontAndSize(font.getBaseFont(), 10);    
            //设置字体的输出位置   
            canvas.setTextMatrix(x, y);    
            //要输出的text   
            canvas.showText("我是替换" );     
              
            canvas.endText();  
            stamper.close();  
            reader.close();  
            System.out.println("complete");   
        }  
        
        /**
         * 测试
         */
        public static void main(String[] args) throws IOException, DocumentException {  
            String SRC = "D:/testpdf/test.pdf";
            String DEST = "D:/testpdf/test_a.pdf";
            File file = new File(DEST);  
            file.getParentFile().mkdirs();  
            new HighLightByAddingContent().manipulatePdf(SRC, DEST);  
        }  
    }  

    测试结果:

                    


    下面是自动查找旧文字替换新文字完整代码:

    1. 需要替换的区域

    /**
     * <p>需要替换的区域 <p>
     * @version 1.0
     * @author li_hao
     * @date 2018年3月14日
     */
    public class ReplaceRegion {  
      
        private String aliasName;  
        private Float x;  
        private Float y;  
        private Float w;  
        private Float h;  
          
        public ReplaceRegion(String aliasName){  
            this.aliasName = aliasName;  
        }  
          
        /**
         * 替换区域的别名 
         * @return
         */
        public String getAliasName() {  
            return aliasName;  
        }  
        public void setAliasName(String aliasName) {  
            this.aliasName = aliasName;  
        }  
        public Float getX() {  
            return x;  
        }  
        public void setX(Float x) {  
            this.x = x;  
        }  
        public Float getY() {  
            return y;  
        }  
        public void setY(Float y) {  
            this.y = y;  
        }  
        public Float getW() {  
            return w;  
        }  
        public void setW(Float w) {  
            this.w = w;  
        }  
        public Float getH() {  
            return h;  
        }  
        public void setH(Float h) {  
            this.h = h;  
        }  
    }  

    2. 解析PDF中文本的x,y位置

    import java.io.FileInputStream;  
    import java.io.IOException;  
    import java.util.ArrayList;  
    import java.util.List;  
    import java.util.Map;  
    import com.itextpdf.text.pdf.PdfReader;  
    import com.itextpdf.text.pdf.parser.PdfReaderContentParser;  
      
    /**
     * <p>解析PDF中文本的x,y位置<p>
     * @version 1.0
     * @author li_hao
     * @date 2018年3月14日
     */
    public class PdfPositionParse {  
      
        private PdfReader reader;  
        private List<String> findText = new ArrayList<String>();    //需要查找的文本  
        private PdfReaderContentParser parser;  
      
        public PdfPositionParse(String fileName) throws IOException{  
            FileInputStream in = null;  
            try{  
                in =new FileInputStream(fileName);  
                byte[] bytes = new byte[in.available()];  
                in.read(bytes);  
                init(bytes);  
            }finally{  
                in.close();  
            }  
        }  
          
        public PdfPositionParse(byte[] bytes) throws IOException{  
            init(bytes);  
        }  
          
        private boolean needClose = true;  
        /**
         * 传递进来的reader不会在PdfPositionParse结束时关闭  
         * @param reader
         */
        public PdfPositionParse(PdfReader reader){  
            this.reader = reader;  
            parser = new PdfReaderContentParser(reader);  
            needClose = false;  
        }  
      
        public void addFindText(String text){  
            this.findText.add(text);  
        }  
          
        private void init(byte[] bytes) throws IOException {  
            reader = new PdfReader(bytes);  
            parser = new PdfReaderContentParser(reader);  
        }  
          
        /**
         * 解析文本  
         * @return
         * @throws IOException
         */
        public Map<String, ReplaceRegion> parse() throws IOException{  
            try{  
                if(this.findText.size() == 0){  
                    throw new NullPointerException("没有需要查找的文本");  
                }  
                PositionRenderListener listener = new PositionRenderListener(this.findText);  
                parser.processContent(1, listener);  
                return listener.getResult();  
            }finally{  
                if(reader != null && needClose){  
                    reader.close();  
                }  
            }  
        }  
    }  

    3. PDF渲染监听,当找到渲染的文本时,得到文本的坐标x,y,w,h

    import java.util.HashMap;  
    import java.util.List;  
    import java.util.Map;  
    import com.itextpdf.awt.geom.Rectangle2D.Float;  
    import com.itextpdf.text.pdf.parser.ImageRenderInfo;  
    import com.itextpdf.text.pdf.parser.RenderListener;  
    import com.itextpdf.text.pdf.parser.TextRenderInfo;  
      
    /**
     * <p>pdf渲染监听,当找到渲染的文本时,得到文本的坐标x,y,w,h<p>
     * @version 1.0
     * @author li_hao
     * @date 2018年3月14日
     */
    public class PositionRenderListener implements RenderListener{  
          
        private List<String> findText;  
        private float defaultH;     ///出现无法取到值的情况,默认为12  
        private float fixHeight;    //可能出现无法完全覆盖的情况,提供修正的参数,默认为2  
        public PositionRenderListener(List<String> findText, float defaultH,float fixHeight) {  
            this.findText = findText;  
            this.defaultH = defaultH;  
            this.fixHeight = fixHeight;  
        }  
      
        public PositionRenderListener(List<String> findText) {  
            this.findText = findText;  
            this.defaultH = 12;  
            this.fixHeight = 2;  
        }  
          
        @Override  
        public void beginTextBlock() {  
              
        }  
      
        @Override  
        public void endTextBlock() {  
              
        }  
      
        @Override  
        public void renderImage(ImageRenderInfo imageInfo) {  
        }  
      
        private Map<String, ReplaceRegion> result = new HashMap<String, ReplaceRegion>();  
        @Override  
        public void renderText(TextRenderInfo textInfo) {  
            String text = textInfo.getText();  
            for (String keyWord : findText) {  
                if (null != text && text.equals(keyWord)){  
                    Float bound = textInfo.getBaseline().getBoundingRectange();  
                    ReplaceRegion region = new ReplaceRegion(keyWord);  
                    region.setH(bound.height == 0 ? defaultH : bound.height);  
                    region.setW(bound.width);  
                    region.setX(bound.x);  
                    region.setY(bound.y-this.fixHeight);  
                    result.put(keyWord, region);  
                }  
            }  
        }  
      
        public Map<String, ReplaceRegion> getResult() {  
            for (String key : findText) {   //补充没有找到的数据  
                if(this.result.get(key) == null){  
                    this.result.put(key, null);  
                }  
            }  
            return this.result;  
        }  
    }  

    4. 替换PDF文件某个区域内的文本

    import java.io.ByteArrayOutputStream;  
    import java.io.FileInputStream;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.util.HashMap;  
    import java.util.Map;  
    import java.util.Map.Entry;  
    import java.util.Set;  
    import com.itextpdf.text.BaseColor;  
    import com.itextpdf.text.DocumentException;  
    import com.itextpdf.text.Font;  
    import com.itextpdf.text.log.Logger;  
    import com.itextpdf.text.log.LoggerFactory;  
    import com.itextpdf.text.pdf.BaseFont;  
    import com.itextpdf.text.pdf.PdfContentByte;  
    import com.itextpdf.text.pdf.PdfReader;  
    import com.itextpdf.text.pdf.PdfStamper;  
      
    /**
     * <p>替换PDF文件某个区域内的文本 <p>
     * @version 1.0
     * @author li_hao
     * @date 2018年3月14日
     */
    public class PdfReplacer {  
        private static final Logger logger = LoggerFactory.getLogger(PdfReplacer.class);  
          
        private int fontSize;  
        private Map<String, ReplaceRegion> replaceRegionMap = new HashMap<String, ReplaceRegion>();  
        private Map<String, Object> replaceTextMap =new HashMap<String, Object>();  
        private ByteArrayOutputStream output;  
        private PdfReader reader;  
        private PdfStamper stamper;  
        private PdfContentByte canvas;  
        private Font font;  
          
        public PdfReplacer(byte[] pdfBytes) throws DocumentException, IOException{  
            init(pdfBytes);  
        }  
          
        public PdfReplacer(String fileName) throws IOException, DocumentException{  
            FileInputStream in = null;  
            try{  
                in =new FileInputStream(fileName);  
                byte[] pdfBytes = new byte[in.available()];  
                in.read(pdfBytes);  
                init(pdfBytes);  
            }finally{  
                in.close();  
            }  
        }  
          
        private void init(byte[] pdfBytes) throws DocumentException, IOException{  
            logger.info("初始化开始");  
            reader = new PdfReader(pdfBytes);  
            output = new ByteArrayOutputStream();  
            stamper = new PdfStamper(reader, output);  
            canvas = stamper.getOverContent(1);  
            setFont(10);  
            logger.info("初始化成功");  
        }  
          
        private void close() throws DocumentException, IOException{  
            if(reader != null){  
                reader.close();  
            }  
            if(output != null){  
                output.close();  
            }  
              
            output=null;  
            replaceRegionMap=null;  
            replaceTextMap=null;  
        }  
          
        public void replaceText(float x, float y, float w,float h, String text){  
            ReplaceRegion region = new ReplaceRegion(text);     //用文本作为别名  
            region.setH(h);  
            region.setW(w);  
            region.setX(x);  
            region.setY(y);  
            addReplaceRegion(region);  
            this.replaceText(text, text);  
        }  
          
        public void replaceText(String name, String text){  
            this.replaceTextMap.put(name, text);  
        }  
          
        /**
         * 替换文本 
         * @throws DocumentException
         * @throws IOException
         */
        private void process() throws DocumentException, IOException{  
            try{  
                parseReplaceText();  
                canvas.saveState();  
                Set<Entry<String, ReplaceRegion>> entrys = replaceRegionMap.entrySet();  
                for (Entry<String, ReplaceRegion> entry : entrys) {  
                    ReplaceRegion value = entry.getValue();  
                    canvas.setColorFill(BaseColor.WHITE);
                    canvas.rectangle(value.getX(),value.getY(),value.getW(),value.getH());  
                }  
                canvas.fill();  
                canvas.restoreState();  
                //开始写入文本   
                canvas.beginText();   
                for (Entry<String, ReplaceRegion> entry : entrys) {  
                    ReplaceRegion value = entry.getValue();  
                    //设置字体  
                    canvas.setFontAndSize(font.getBaseFont(), getFontSize());    
                    canvas.setTextMatrix(value.getX(),value.getY()+2/*修正背景与文本的相对位置*/);    
                    canvas.showText((String) replaceTextMap.get(value.getAliasName()));     
                }  
                canvas.endText();  
            }finally{  
                if(stamper != null){  
                    stamper.close();  
                }  
            }  
        }  
          
        /**
         * 未指定具体的替换位置时,系统自动查找位置   
         */
        private void parseReplaceText() {  
            PdfPositionParse parse = new PdfPositionParse(reader);  
            Set<Entry<String, Object>> entrys = this.replaceTextMap.entrySet();  
            for (Entry<String, Object> entry : entrys) {  
                if(this.replaceRegionMap.get(entry.getKey()) == null){  
                    parse.addFindText(entry.getKey());  
                }  
            }  
              
            try {  
                Map<String, ReplaceRegion> parseResult = parse.parse();  
                Set<Entry<String, ReplaceRegion>> parseEntrys = parseResult.entrySet();  
                for (Entry<String, ReplaceRegion> entry : parseEntrys) {  
                    if(entry.getValue() != null){  
                        this.replaceRegionMap.put(entry.getKey(), entry.getValue());  
                    }  
                }  
            } catch (IOException e) {  
                logger.error(e.getMessage(), e);  
            }  
              
        }  
      
        /**
         * 生成新的PDF文件  
         * @param fileName
         * @throws DocumentException
         * @throws IOException
         */
        public void toPdf(String fileName) throws DocumentException, IOException{  
            FileOutputStream fileOutputStream = null;  
            try{  
                process();  
                fileOutputStream = new FileOutputStream(fileName);  
                fileOutputStream.write(output.toByteArray());  
                fileOutputStream.flush();  
            }catch(IOException e){  
                logger.error(e.getMessage(), e);  
                throw e;  
            }finally{  
                if(fileOutputStream != null){  
                    fileOutputStream.close();  
                }  
                close();  
            }  
            logger.info("文件生成成功");  
        }  
          
        /**
         * 将生成的PDF文件转换成二进制数组  
         * @return
         * @throws DocumentException
         * @throws IOException
         */
        public byte[] toBytes() throws DocumentException, IOException{  
            try{  
                process();  
                logger.info("二进制数据生成成功");  
                return output.toByteArray();  
            }finally{  
                close();  
            }  
        }  
          
        /**
         * 添加替换区域  
         * @param replaceRegion
         */
        public void addReplaceRegion(ReplaceRegion replaceRegion){  
            this.replaceRegionMap.put(replaceRegion.getAliasName(), replaceRegion);  
        }  
          
        /**
         * 通过别名得到替换区域   
         * @param aliasName
         * @return
         */
        public ReplaceRegion getReplaceRegion(String aliasName){  
            return this.replaceRegionMap.get(aliasName);  
        }  
      
        public int getFontSize() {  
            return fontSize;  
        }  
      
        /**
         * 设置字体大小 
         * @param fontSize
         * @throws DocumentException
         * @throws IOException
         */
        public void setFont(int fontSize) throws DocumentException, IOException{  
            if(fontSize != this.fontSize){  
                this.fontSize = fontSize;  
              BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); //使用iTextAsian.jar中的字体 
    //          BaseFont bf = BaseFont.createFont("C:/WINDOWS/Fonts/STSONG.TTF", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);  //使用Windows系统字体(TrueType)
    //          BaseFont bf = BaseFont.createFont("/STSONG.TTF", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);  //使用资源字体(ClassPath)
              
                font = new Font(bf,this.fontSize,Font.BOLD);  
            }  
        }  
          
        public void setFont(Font font){  
            if(font == null){  
                throw new NullPointerException("font is null");  
            }  
            this.font = font;  
        }  
        
        /**
         * 测试
         */
        public static void main(String[] args) throws IOException, DocumentException {  
            PdfReplacer textReplacer = new PdfReplacer("D:/testpdf/test.pdf");
            textReplacer.replaceText("我是测试名称", "李大锤李大锤李大锤");
            textReplacer.replaceText("我是测试内容", "张三丰张三丰张三丰");
            textReplacer.toPdf("D:/testpdf/test3.pdf");  
        }  
    }  

    测试结果:

          

           

     以上就是java使用itext替换PDF中的文本的代码,在替换文本时可以自动查找文本的内容,当然也可以自定义坐标区域替换。

    注:如果不好确定替换区域的坐标位置,而PDF对应区域又是空白的话,可以在生成PDF的时候,在需要替换的区域先写入“特定文字内容”,预设的文字颜色设置和背景色相同,这样PDF看起来似乎是空的,但是却可以用查找文本的方式替换,替换后的就能显示出替换后的内容了,这样有时候可以方便很多。

    附:替换后的文本在PC端、Android端、iOS端都是可以打开和使用,在HTML5开发时使用PDF.js插件时发现替换后的字体不显示的情况,这是由于PDF.js插件不识别iTextAsign.jar中的字体,把:BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED)做下修改,可以使用Windows中的字体:BaseFont bf = BaseFont.createFont("C:/WINDOWS/Fonts/STSONG.TTF", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED),字体和路径路径可以自行设置,linux中先安装中文字体库,再使用相应字体的路径即可。替换字体后,网页中使用PDF.js插件也没问题了。


    源码:https://github.com/piaoliudelaoyaoguai/pdfReplaceWordsDemo.git

  • 相关阅读:
    付出给人一种美好的感觉
    表连接查询 条件在On与Where后区别
    Json与实体类 转化
    算法的时间复杂度和空间复杂度详解
    真的理解同步和异步了吗?
    花生壳申请域名并进行内网穿透
    XML 反序列化
    .Net 发送邮件
    BootStrap DataTable 时间日期列排序
    SQL Server 动态SQL拼接
  • 原文地址:https://www.cnblogs.com/hooly/p/8569734.html
Copyright © 2020-2023  润新知