PDF模板查找关键字坐标

PDF模板查找关键字坐标

开发过程中会遇到很多给一个模板上赋值的任务,一般都是使用占位符等操作,对需要赋值的位置进行文本赋值,还有一种是找到关键字,然后获取其在文档中的位置,然后进行坐标偏移赋值。今天就来介绍第二种方式。

开始始终是我们的接口

    /**
     * PDF模板查找关键字坐标
     * @return 返回值
     */
    @GetMapping(value = "/testNewPdf")
    @ResponseBody
    void testNewPdf();

千变万变,接口不变

 @Override
    public void testNewPdf() {
    	testCreatePDF();
    }

还是接口 继续

    /**
     * .
     *
     * @Description: 作用:  测试pdf相关
     * @Author: LXT
     * @Date: 2022/1/7 14:11
     */
    public void testCreatePDF() {
        testQueryKeywordLastAddText();
    }

这里就要开始正常的代码逻辑了 , 先附上代码

 /**
     * .
     *
     * @Description: 作用:  pdf查找关键字后,加文字
     * @Author: LXT
     * @Date: 2022/1/7 14:12
     */
    private void testQueryKeywordLastAddText() {
    //初始化文件地址,或者其他位置的文件模板位置
        String initPdf = "D:\\Users\\User\\Desktop\\XXXX.pdf";
        logger.info("初始化之后的pdf地址" + initPdf);
        //关键字
        String keyword = "日期:";
        //调用工具类,查找关键字的位置信息
        float[] floats = QueryKeywordPositionUtil.queryKeywordPosition(initPdf, keyword);
        logger.info("关键字位置--" + JSON.toJSONString(floats));
        //要新加的文本
        String text = "2021 年 01 月 07 日";
        //调用工具类,进行文本赋值
        String endPath = KeywordLastAddTextUtil.keywordLastAddText(initPdf, floats, text);
        logger.info("pdf关键字后增加文字后地址--" + endPath);
        logger.info("pdf关键字后增加文字完成");
    }

代码简单明了 还是比较容易看懂的,
重点 来了 看下第一个工具类 查找关键字的

/**
 * .
 *
 * @ClassName: QueryKeywordPositionUtil
 * @Description: 查找关键字位置坐标工具类
 * @Author: LXT
 * @Date: 2022/1/6 16:11
 */
public class QueryKeywordPositionUtil {

    private static final Logger logger = LoggerFactory.getLogger(QueryKeywordPositionUtil.class);

    /**
     * .
     * @Description: 作用:  查找关键字位置坐标
     * @Author: LXT
     * @Date: 2022/1/7 14:40
     * @param pdfPath 入参 pdf地址
     * @param keyword 入参  关键字
     * @return float[] 分别为 float[0]所在页码  float[1]所在x轴 float[2]所在y轴
     */
    public static float[] queryKeywordPosition(String pdfPath, String keyword) {
        try {
            //1.给定文件
            File pdfFile = new File(pdfPath);
            //2.定义一个byte数组,长度为文件的长度
            byte[] pdfData = new byte[(int) pdfFile.length()];
            //3.IO流读取文件内容到byte数组
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(pdfFile);
                inputStream.read(pdfData);
            } catch (IOException e) {
                throw e;
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
            logger.info("读取pdf完成---" + pdfPath);
            //4.指定关键字
            // String keyword = "日期:";
            //5.调用方法,给定关键字和文件
            List<float[]> positions = findKeywordPostions(pdfData, keyword);
            logger.info("关键字" + keyword + "位置---" + JSON.toJSONString(positions));
            float page = 0;
//            float xxxx = 0;
//            float yyyy = 0;
            //6.返回值类型是  List<float[]> 每个list元素代表一个匹配的位置,分别为 float[0]所在页码  float[1]所在x轴 float[2]所在y轴
            System.out.println("total:" + positions.size());
            if (positions != null && positions.size() > 0) {
                for (float[] position : positions) {
                    page = position[0];
//                    xxxx = position[1];
//                    yyyy = position[2];
                    System.out.print("pageNum: " + (int) position[0]);
                    System.out.print("\tx: " + position[1]);
                    System.out.println("\ty: " + position[2]);
                    if (page == 1) {
                        logger.info("关键字" + keyword + "位置在第一页" + JSON.toJSONString(positions));
                        return position;
                    }
                }
            }
        } catch (Exception e) {
            logger.error("查找关键字位置坐标异常", e);
        }
        return null;
    }


    /**
     * findKeywordPostions 查找关键字位置
     *
     * @param pdfData 通过IO流 PDF文件转化的byte数组
     * @param keyword 关键字
     * @return List<float [ ]> : float[0]:pageNum float[1]:x float[2]:y
     * @throws IOException
     */
    private static List<float[]> findKeywordPostions(byte[] pdfData, String keyword) throws IOException {
        List<float[]> result = new ArrayList<>();
        List<PdfPageContentPositions> pdfPageContentPositions = getPdfContentPostionsList(pdfData);
        for (PdfPageContentPositions pdfPageContentPosition : pdfPageContentPositions) {
            List<float[]> charPositions = findPositions(keyword, pdfPageContentPosition);
            if (charPositions == null || charPositions.size() < 1) {
                continue;
            }
            result.addAll(charPositions);
        }
        return result;
    }

    /**
     * .
     *
     * @param pdfData 入参
     * @return java.util.List<com.utils.QueryKeywordPositionUtil.PdfPageContentPositions>
     * @Description: 作用:   查找关键字位置合集
     * @Author: LXT
     * @Date: 2022/1/7 10:37
     */
    private static List<PdfPageContentPositions> getPdfContentPostionsList(byte[] pdfData) throws IOException {
        PdfReader reader = new PdfReader(pdfData);
        List<PdfPageContentPositions> result = new ArrayList<>();
        int pages = reader.getNumberOfPages();
        for (int pageNum = 1; pageNum <= pages; pageNum++) {
            float width = reader.getPageSize(pageNum).getWidth();
            float height = reader.getPageSize(pageNum).getHeight();
            PdfRenderListener pdfRenderListener = new PdfRenderListener(pageNum, width, height);
            //解析pdf,定位位置
            PdfContentStreamProcessor processor = new PdfContentStreamProcessor(pdfRenderListener);
            PdfDictionary pageDic = reader.getPageN(pageNum);
            PdfDictionary resourcesDic = pageDic.getAsDict(PdfName.RESOURCES);
            try {
                processor.processContent(ContentByteUtils.getContentBytesForPage(reader, pageNum), resourcesDic);
            } catch (IOException e) {
                reader.close();
                throw e;
            }
            String content = pdfRenderListener.getContent();
            List<CharPosition> charPositions = pdfRenderListener.getcharPositions();
            List<float[]> positionsList = new ArrayList<>();
            for (CharPosition charPosition : charPositions) {
                float[] positions = new float[]{charPosition.getPageNum(), charPosition.getX(), charPosition.getY()};
                positionsList.add(positions);
            }
            PdfPageContentPositions pdfPageContentPositions = new PdfPageContentPositions();
            pdfPageContentPositions.setContent(content);
            pdfPageContentPositions.setPostions(positionsList);
            result.add(pdfPageContentPositions);
        }
        reader.close();
        return result;
    }


    private static List<float[]> findPositions(String keyword, PdfPageContentPositions pdfPageContentPositions) {
        List<float[]> result = new ArrayList<>();
        String content = pdfPageContentPositions.getContent();
        List<float[]> charPositions = pdfPageContentPositions.getPositions();
        for (int pos = 0; pos < content.length(); ) {
            int positionIndex = content.indexOf(keyword, pos);
            if (positionIndex == -1) {
                break;
            }
            float[] postions = charPositions.get(positionIndex);
            result.add(postions);
            pos = positionIndex + 1;
        }
        return result;
    }


    private static class PdfPageContentPositions {

        private String content;

        private List<float[]> positions;

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }

        public List<float[]> getPositions() {
            return positions;
        }

        public void setPostions(List<float[]> positions) {
            this.positions = positions;
        }
    }


    private static class PdfRenderListener implements RenderListener {

        private int pageNum;

        private float pageWidth;

        private float pageHeight;

        private StringBuilder contentBuilder = new StringBuilder();

        private List<CharPosition> charPositions = new ArrayList<>();

        public PdfRenderListener(int pageNum, float pageWidth, float pageHeight) {
            this.pageNum = pageNum;
            this.pageWidth = pageWidth;
            this.pageHeight = pageHeight;
        }

        public void beginTextBlock() {
        }

        public void renderText(TextRenderInfo renderInfo) {
            List<TextRenderInfo> characterRenderInfos = renderInfo.getCharacterRenderInfos();
            for (TextRenderInfo textRenderInfo : characterRenderInfos) {
                String word = textRenderInfo.getText();
                if (word.length() > 1) {
                    word = word.substring(word.length() - 1, word.length());
                }
                Rectangle2D.Float rectangle = textRenderInfo.getAscentLine().getBoundingRectange();
                float x = (float) rectangle.getX();
                float y = (float) rectangle.getY();
                //这两个是关键字在所在页面的XY轴的百分比
                float xPercent = Math.round(x / pageWidth * 10000) / 10000f;
                float yPercent = Math.round((1 - y / pageHeight) * 10000) / 10000f;
                CharPosition charPosition = new CharPosition(pageNum, (float) x, (float) y);
                charPositions.add(charPosition);
                contentBuilder.append(word);
            }
        }

        public void endTextBlock() {
        }

        public void renderImage(ImageRenderInfo renderInfo) {
        }

        public String getContent() {
            return contentBuilder.toString();
        }

        public List<CharPosition> getcharPositions() {
            return charPositions;
        }
    }

    private static class CharPosition {

        private int pageNum = 0;

        private float x = 0;

        private float y = 0;

        public CharPosition(int pageNum, float x, float y) {
            this.pageNum = pageNum;
            this.x = x;
            this.y = y;
        }

        public int getPageNum() {
            return pageNum;
        }

        public float getX() {
            return x;
        }

        public float getY() {
            return y;
        }

        @Override
        public String toString() {
            return "[pageNum=" + this.pageNum + ",x=" + this.x + ",y=" + this.y + "]";
        }
    }

}

主要是 queryKeywordPosition 这个 需要的参数就是pdf 位置 以及关键字
然后 开始读取pdf 找到文档中所有的关键字位置集合 重复的会反多个哦

我这里只需要操作第一页 所有就判断第一页就可以了 ,
具体是怎么查的关键字,还请高手一一解读,

然后回到 testQueryKeywordLastAddText 将唯一的位置信息返回,
然后进行关键字后增加文本操作
首先工具类 必不可少

/**
 * .
 *
 * @ClassName: KeywordLastAddTextUtil
 * @Description: 关键字后增加文本工具类
 * @Author: LXT
 * @Date: 2022/1/7 14:23
 */
public class KeywordLastAddTextUtil {

    private static final Logger logger = LoggerFactory.getLogger(KeywordLastAddTextUtil.class);

    /**
     * .
     *
     * @param templatePath 入参 pdf文件
     * @param floatNum     入参 关键字位置
     * @param text     入参 新增文本
     * @return String 新的pdf地址
     * @Description: 作用: 关键字后增加文本
     * @Author: LXT
     * @Date: 2022/1/7 14:47
     */
    public static String keywordLastAddText(String templatePath, float[] floatNum, String text) {
        logger.info("pdf--" + templatePath + "--增加文本---" + text);
        try {
            // 模板文件路径
            String newFileName = templatePath.replace(".pdf","addText.pdf");
            byte[] pdfBty = ClientUtil.readFileToByteArray(new File(templatePath));//pdf字节数组
            ClientUtil.writeByteArrayToFile(new File(newFileName), pdfBty);
            logger.info("输出pdf地址--" + newFileName);
            fileChannelCopy(new File(newFileName), new File(templatePath));
            PdfReader reader = new PdfReader(templatePath);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(newFileName));
            PdfContentByte overContent = stamper.getOverContent(1);
            //添加文字
            BaseFont font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            overContent.beginText();
            overContent.setFontAndSize(font, 10);
            overContent.setTextMatrix(200, 200);
            // 位置x 左到右 越来越大
            // 位置y 下到上 越来越大
            // x: 80.04 	y: 614.26263
            //字体大小为10  x 向右边偏移三个字 x+30
            //字体大小为10  y 向下边偏移一个字 y-10
            // float[1]所在x轴 float[2]所在y轴
            float xx = floatNum[1] + 30f;
            float yy = floatNum[2] - 10f;
            overContent.showTextAligned(Element.ALIGN_LEFT, text, xx, yy, 0);
            overContent.endText();
            stamper.close();
            return newFileName;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }
    }

    /**
     * .
     *
     * @param sources 入参 源文件
     * @param dest    入参 目标文件
     * @return void
     * @Description: 作用:  文件的输入输出流
     * @Author: LXT
     * @Date: 2022/1/7 14:41
     */
    private static void fileChannelCopy(File sources, File dest) {
        try {
            FileInputStream inputStream = new FileInputStream(sources);
            FileOutputStream outputStream = new FileOutputStream(dest);
            FileChannel fileChannelin = inputStream.getChannel();//得到对应的文件通道
            FileChannel fileChannelout = outputStream.getChannel();//得到对应的文件通道
            fileChannelin.transferTo(0, fileChannelin.size(), fileChannelout);//连接两个通道,并且从in通道读取,然后写入out通道
            inputStream.close();
            fileChannelin.close();
            outputStream.close();
            fileChannelout.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

这个其实也好理解

入参就是 pdf 位置 关键字坐标 以及要添加的文本
首先是将这个pdf 复制一份以免对源文件造成损坏
然后就是设置字体的一些信息 当然了 可以在里边放置像文件了 或者图片了 等等其他 就不一一介绍了

PdfContentByte overContent = stamper.getOverContent(1);
这个是设置我文本要在第几页

然后文本的xy轴说明 一看就明白了 应该不用介绍 看不明白的私信

rotation 是旋转量 也就是旋转多少度

将文本输出的新的pdf中 整个过程就完成了 随后返回新的pdf地址

好了 ,给定一个pdf 然后查找关键字坐标 并在后面添加文本数据 整个流程就算完成了 需要增加的其他的还请自行研究。

对了 附上 pdf模板的截图

在这里插入图片描述
这是第一页 需要在日期后增加文本 由于不确定因素 只能去查找关键字
下边是最终效果

在这里插入图片描述

本文完,码值不易,记得点赞。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值