zoukankan      html  css  js  c++  java
  • java PDF批量替换关键词

    java PDF批量替换关键词

    要求:将要替换的字段封装到一个类中,并规定字段名与PDF中关键字的映射。

    准备:

    关键词信息类-----MatchItem

    package pdf.replace;
    
    import lombok.Data;
    
    @Data
    public class MatchItem {
    	//pdf页数
        private Integer pageNum;
        //关键词
        private String keyWord;
        //pdf扫描的单个字块
        private String content;
        //关键词的位置信息,字体大小等等
        private float x;
        private float y;
        private float fontHeight;
        private float fontWidth;
        private float pageHeight;
        private float pageWidth;
    }
    
    

    扫描pdf并组装成MatchItem类------KeyWordPositionListener

    package pdf.replace;
    
    import com.google.common.collect.Lists;
    import com.itextpdf.awt.geom.Rectangle2D;
    import com.itextpdf.text.Rectangle;
    import com.itextpdf.text.pdf.parser.ImageRenderInfo;
    import com.itextpdf.text.pdf.parser.RenderListener;
    import com.itextpdf.text.pdf.parser.TextRenderInfo;
    import lombok.Data;
    
    import java.util.List;
    @Data
    public class KeyWordPositionListener implements RenderListener {
        private String keyword;
        private Integer pageNumber;
        private Rectangle curPageSize;
        private List<MatchItem> allItems= Lists.newArrayList();
        private List<MatchItem> matches= Lists.newArrayList();
        @Override
        public void beginTextBlock() {
    
        }
    
        @Override
        public void renderText(TextRenderInfo textRenderInfo) {
            //获取字符
            String content = textRenderInfo.getText();
            Rectangle2D.Float textRectangle = textRenderInfo.getDescentLine().getBoundingRectange();
    
            MatchItem item = new MatchItem();
            item.setContent(content);
            item.setPageNum(pageNumber);
            item.setFontHeight(textRectangle.height == 0 ? 12:textRectangle.height);//默认12
            item.setFontWidth(textRectangle.width);
            item.setPageHeight(curPageSize.getHeight());
            item.setPageWidth(curPageSize.getWidth());
            item.setX((float)textRectangle.getX());
            item.setY((float)textRectangle.getY());
    
            //若keyword是单个字符,匹配上的情况
            if(content.equalsIgnoreCase(keyword)) {
                matches.add(item);
            }
            //保存所有的项
            allItems.add(item);
        }
    
        @Override
        public void endTextBlock() {
    
        }
    
        @Override
        public void renderImage(ImageRenderInfo imageRenderInfo) {
    
        }
    }
    
    

    类属性和pdf上关键词的映射枚举-----KeyEnum

    需要一直维护。因为入参类是会变化的

    package pdf.replace;
    public enum KeyEnum {
    
        //关键词后面+  & 只是我的这边方便,可以自行设计关键词
        姓名("name", "姓名&"),
        性别("sex", "性别&"),
        年龄("age", "年龄&"),
        学历("stu", "学历&");
    
        //属性
        private String property;
        //关键词
        private String keyWord;
        KeyEnum(String property, String keyWord) {
            this.property = property;
            this.keyWord = keyWord;
        }
    
        public static String value(String property) {
            for (KeyEnum e : values()) {
                if (e.getProperty().equals(property)) {
                    return e.getKeyWord();
                }
            }
            return null;
        }
    
        public String getProperty() {
            return property;
        }
    
        public void setProperty(String property) {
            this.property = property;
        }
    
        public String getKeyWord() {
            return keyWord;
        }
    
        public void setKeyWord(String keyWord) {
            this.keyWord = keyWord;
        }
    
    
    }
    
    

    测试类------Test

    package pdf.replace;
    
    import lombok.Data;
    
    
    @Data
    public class Test {
        private String name;
        private String sex;
        private Integer age;
        private String stu;
    }
    
    

    PDFUtils

    package pdf.replace;
    
    import com.itextpdf.text.BaseColor;
    import com.itextpdf.text.Font;
    import com.itextpdf.text.Rectangle;
    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 com.itextpdf.text.pdf.parser.PdfReaderContentParser;
    
    import java.io.FileOutputStream;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * pdf替换文字工具类
     *
     * 思路:
     * 1.逐页搜索关键字,逐页匹配
     * 2.先读取一页的所有字符信息,存放到allItems中
     * 3.把一页的字符拼接成字符串,然后匹配关键字,匹配上,记录匹配的第一个字符的MatchItem信息;匹配不是,继续下一页匹配
     * 4.根据匹配字符串的长度和字符的宽高信息画遮罩层,然后替换文字生成新的pdf文件
     *
     * 不足之处:
     * 1.目前只支持单字符串匹配
     * 2.替换之后的文字无法和原pdf中替换掉的文字信息一致(主要有:字体大小、样式等)
     * 3.某些情况下(主要是替换字体的大小)替换之后显示不是太整齐
     * 4.字体大小、样式无法把控
     * 5.无法匹配目标文字在两页中显示的情况(例如:目标文字:替换工具,第一页页尾有替换两字,第二页页首有工具二字)
     *
     */
    public class PdfUtils {
        /**
         * 根据关键字和pdf路径,全文搜索关键字
         * @param reader pdf目标路径
         * @param keyValue 关键字列表
         * @return
         * @throws Exception
         */
        public static Map<Integer,Map<MatchItem,String>> matchAll(PdfReader reader, Map<String,String> keyValue) throws Exception {
            Map<Integer,Map<MatchItem,String>> map=new HashMap<>();
            //获取pdf页数
            int pageSize = reader.getNumberOfPages();
            //逐页匹配关键字
            for(int page = 1;page <= pageSize;page++){
                Map<MatchItem,String> map1=new HashMap<>();
                for (String keyword:keyValue.keySet()) {
                    List<MatchItem> matchItems=matchPage(reader,page,keyword);
                    if (matchItems!=null&&matchItems.size()>0){
                        for (MatchItem item:matchItems){
                            item.setKeyWord(keyword);
                            map1.put(item,keyValue.get(keyword));
                        }
                    }
                }
                map.put(page,map1);
            }
            return map;
        }
    
        /**
         * 根据关键字、文档路径、pdf页数寻找特定的文件内容
         * @param reader
         * @param pageNumber 页数
         * @param keyword 关键字
         * @return
         * @throws Exception
         */
        public static List<MatchItem> matchPage(PdfReader reader, Integer pageNumber,String keyword) throws Exception {
            PdfReaderContentParser parse = new PdfReaderContentParser(reader);
            Rectangle rectangle = reader.getPageSize(pageNumber);
            //匹配监听
            KeyWordPositionListener renderListener = new KeyWordPositionListener();
            renderListener.setKeyword(keyword);
            renderListener.setPageNumber(pageNumber);
            renderListener.setCurPageSize(rectangle);
            parse.processContent(pageNumber, renderListener);
            return findKeywordItems(renderListener,keyword);
        }
    
    
        /**
         * 找到匹配的关键词块
         * @param renderListener
         * @param keyword
         * @return
         */
        public static List<MatchItem> findKeywordItems(KeyWordPositionListener renderListener,String keyword){
            //先判断本页中是否存在关键词
            List<MatchItem> allItems = renderListener.getAllItems();//所有块LIST
            StringBuffer sbtemp = new StringBuffer("");
    
            for(MatchItem item : allItems){//将一页中所有的块内容连接起来组成一个字符串。
                sbtemp.append(item.getContent());
            }
    
            List<MatchItem> matches = renderListener.getMatches();
    
            //一页组成的字符串没有关键词,直接return
            //第一种情况:关键词与块内容完全匹配的项,直接返回
            if(sbtemp.toString().indexOf(keyword) == -1 || matches.size() > 0){
                return matches;
            }
            //第二种情况:多个块内容拼成一个关键词,则一个一个来匹配,组装成一个关键词
            sbtemp = new StringBuffer("");
            List<MatchItem> tempItems = new ArrayList();
            for(MatchItem item : allItems){
                if(keyword.indexOf(item.getContent()) != -1 ){
                    tempItems.add(item);
                    sbtemp.append(item.getContent());
    
                    if(keyword.indexOf(sbtemp.toString()) == -1){//如果暂存的字符串和关键词 不再匹配时
                        sbtemp = new StringBuffer(item.getContent());
                        tempItems.clear();
                        tempItems.add(item);
                    }
    
                    if(sbtemp.toString().equalsIgnoreCase(keyword)){//暂存的字符串正好匹配到关键词时
                        matches.add(tempItems.get(0));//得到匹配的项
                        sbtemp = new StringBuffer("");//清空暂存的字符串
                        tempItems.clear();//清空暂存的LIST
                        continue;//继续查找
                    }
                }else{//如果找不到则清空
                    sbtemp = new StringBuffer("");
                    tempItems.clear();
                }
            }
            return matches;
        }
        /**
         * 自动生成替换键值对
         * @param
         */
        public static  Map<String,String> autoKeyValue(Class clazz,Object obj){
            Map<String,String> map=new HashMap<>();
            for(KeyEnum keyEnum:KeyEnum.values()){
                Field field= null;
                try {
                    field = clazz.getDeclaredField(keyEnum.getProperty());
                    if (null != field){
                        field.setAccessible(true);
                        Object o= field.get(obj);
                        if (null!=o){
                            map.put(keyEnum.getKeyWord(),o.toString());
                        }else {
                            map.put(keyEnum.getKeyWord(),"");
                        }
                    }
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            return map;
        }
        /**
         * 替换目标文字,生成新的pdf文件
         * @param src 目标pdf路径
         * @param dest 新pdf的路径
         * @param clazz 类
         * @param o 对象
         * @throws Exception
         */
        public static void batchReplace(String src,String dest,Class clazz,Object o) throws Exception{
            //根据入参类信息和对象获取键值对
            Map<String,String> keyValue=autoKeyValue(clazz,o);
            
            PdfReader reader = new PdfReader(src);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
            //获取类属性的所有关键词再pdf对应位置信息的map
            Map<Integer,Map<MatchItem,String>> matchItems=matchAll(reader,keyValue);
            //开始替换
            PdfContentByte canvas = null;
            for (Map.Entry<Integer,Map<MatchItem,String>> entry:matchItems.entrySet()){
                canvas = stamper.getOverContent(entry.getKey());
                for (Map.Entry<MatchItem,String> matchEntry:entry.getValue().entrySet()){
                    MatchItem item=matchEntry.getKey();
                    float x = item.getX();
                    float y = item.getY();
                    float fontWidth = item.getFontWidth();
                    float fontHeight = item.getFontHeight();
                    canvas.saveState();
                    canvas.setColorFill(BaseColor.WHITE);
                    canvas.rectangle(x, y,fontWidth*item.getKeyWord().length(),fontHeight+2);
                    canvas.fill();
                    canvas.restoreState();
                    //开始写入文本
                    canvas.beginText();
                    BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
                    Font font = new Font(bf,fontWidth,Font.UNDERLINE);
                    //设置字体和大小
                    canvas.setFontAndSize(font.getBaseFont(), fontWidth);
                    //设置字体的输出位置
                    canvas.setTextMatrix(x, y+fontWidth/10+0.5f);
                    //要输出的text
                    canvas.showText(matchEntry.getValue());
                    canvas.endText();
                }
            }
            stamper.close();
            reader.close();
            System.out.println("complete");
        }
    
    }
    

    测试

    测试代码

      public static void main(String[] args) throws Exception{
            String src = "C:\Users\steve\Desktop\test.pdf";
            String dest = "C:\Users\steve\Desktop\pdfTest.pdf";
    //    根据类来替换
            Test test=new Test();
            test.setName("张三");
            test.setSex("男");
            test.setStu("本科");
            batchReplace(src,dest,Test.class,test);
    
        }
    

    替换前pdf

    替换后pdf

  • 相关阅读:
    Centos7安装gitlab私服
    SpringBoot2.x开发WebSocket
    java中 DecimalFormat格式的定义
    Docker部署Reids实战单机、集群
    CCF地铁修建
    C题:A Water Problem(dp||搜索)
    A题:Common Substrings(KMP应用)
    HDU 1879 继续畅通工程(Prim||Kruscal模板题)
    魔兽世界之一:备战(模拟)
    F题:等差区间(RMQ||线段树)
  • 原文地址:https://www.cnblogs.com/zhuyan521/p/14340714.html
Copyright © 2011-2022 走看看