【PDFBox】PDFBox操作PDF文档之读取指定页面文本内容、读取所有页面文本内容、根据模板文件生成PDF文档

news2024/12/24 2:34:12

这篇文章,主要介绍PDFBox操作PDF文档之读取指定页面文本内容、读取所有页面文本内容、根据模板文件生成PDF文档。

目录

一、PDFBox操作文本

1.1、读取所有页面文本内容

1.2、读取指定页面文本内容

1.3、写入文本内容

1.4、替换文本内容

(1)自定义PDTextStripper类

(2)创建KeyWordEntity实体类

(3)下载字体文件

(4)创建PDFUtil工具类

(5)运行效果

(6)不足之处


一、PDFBox操作文本

PDFBox操作文本内容,需要使用文本提取器PDTextStripper对象实现,这个PDTextStripper类提供了对文本内容操作的方法,例如:getText()获取文本,writeString()写入字符串等等,下面介绍PDFBox操作文本的几种情况。

1.1、读取所有页面文本内容

一个PDF文档是由多个页面组成的,某一个页面中都可能会包含文本内容,PDTextStripper类提供的【getText()】方法,可以获取到整个PDF文档的文本内容,案例代码如下所示:

package pdfbox.demo.text;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.File;
import java.io.IOException;

/**
 * @version 1.0.0
 * @Date: 2023/7/18 9:03
 * @Author ZhuYouBin
 * @Description: 读取PDF文档中所有纯文本内容
 */
public class ReadAllText {
    public static void main(String[] args) throws IOException {
        // 1、加载指定PDF文档
        PDDocument document = PDDocument.load(new File("D:\\demo.pdf"));
        // 2、创建文本提取对象
        PDFTextStripper stripper = new PDFTextStripper();
        // 3、获取指定页面的文本内容
        String text = stripper.getText(document);
        System.out.println("获取文本内容: " + text);
        // 4、关闭
        document.close();
    }
}

1.2、读取指定页面文本内容

有些情况下,我们可能是需要获取某一个页面中的文本内容,这个时候可以通过PDTextStripper类设置页面边界,也就是设置提取哪些页面中的文本内容,只需要调用【setStartPage()】和【setEndPage()】方法即可,案例代码如下所示:

package pdfbox.demo.text;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.File;
import java.io.IOException;

/**
 * @version 1.0.0
 * @Date: 2023/7/18 9:03
 * @Author ZhuYouBin
 * @Description: 读取PDF文档中所有纯文本内容
 */
public class ReadPageText {
    public static void main(String[] args) throws IOException {
        // 1、加载指定PDF文档
        PDDocument document = PDDocument.load(new File("D:\\demo.pdf"));
        // 2、创建文本提取对象
        PDFTextStripper stripper = new PDFTextStripper();
        // 指定页面读取内容
        stripper.setStartPage(0); // 设置起始页面,这里设置成0,就表示读取第一个页面
        stripper.setEndPage(0); // 设置结束页面,这里设置成0,就表示读取第一个页面
        // 3、获取指定页面的文本内容
        String text = stripper.getText(document);
        System.out.println("获取文本内容: " + text);
        // 4、关闭
        document.close();
    }
}

1.3、写入文本内容

前几篇文章已经介绍过了如何使用PDFBox写入纯文本内容到PDF文档里面,写入内容可以写入单行内容,也可以写入多行文本内容,可以参考文章:

【【PDFBox】PDFBox操作PDF文档之创建PDF文档、加载PDF文档、添加空白页面、删除页面、获取总页数、添加文本内容、PDFBox坐标系】。

1.4、替换文本内容

替换文本内容,PDFBox并没有提供替换文本内容的方法,这里我是采用了某种方式来实现替换文本内容的功能,大致思路:

  • 首先读取文本内容,获取到替换的文本在PDF文档中的页面坐标位置。
  • 获取到替换文本的坐标之后,将这块区域内容写入一个矩形框,矩形背景颜色采用白色,也就是覆盖替换的文本。
  • 在白色矩形区域里面,重新写入替换之后的文本内容。
  • 采用这种思路,就可以大致实现替换指定文本的功能啦。

(1)自定义PDTextStripper类

要想获取到文本的坐标信息,必须自定义一个类,继承自PDTextStripper类,然后重写【writeString()】方法,这个方法有两个参数:

  • 第一个参数是text:表示当前读取到的文本内容。
  • 第二个参数是List<TextPosition>:表示当前文本内容中某一个字符的坐标信息。
package pdfbox.demo.text.keyword;

import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import org.apache.pdfbox.util.Matrix;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @version 1.0.0
 * @Date: 2023/7/18 10:18
 * @Author ZhuYouBin
 * @Description: 自定义文本提取器,获取查找文本的坐标位置
 */
public class KeyWordPositionStripper extends PDFTextStripper {

    /**
     * 查找的关键字集合
     */
    private final List<String> keyWordList;
    /**
     * 查找成功的关键字实体对象集合
     */
    private final List<KeyWordEntity> keyWordEntityList = new ArrayList<>();

    public KeyWordPositionStripper(List<String> keyWordList) throws IOException {
        this.keyWordList = keyWordList;
    }

    @Override
    protected void writeString(String text, List<TextPosition> positions) {
        int size = positions.size();
        for (String keyWord : keyWordList) {
            char[] chars = keyWord.toCharArray();
            for (int i = 0; i < size; i++) {
                // 获取当前读取的字符
                String currentChar = positions.get(i).getUnicode();
                // 当前字符 和 keyWord 关键字进行匹配
                if (!Objects.equals(currentChar, String.valueOf(chars[0]))) {
                    continue;
                }
                int count = 1;
                int j;
                for (j = 1; j < chars.length && i + j < size; j++) {
                    currentChar = positions.get(i + j).getUnicode();
                    if (!Objects.equals(currentChar, String.valueOf(chars[j]))) {
                        break;
                    }
                    count++;
                }
                // 匹配成功,记录文本的坐标位置
                if (count == chars.length) {
                    TextPosition startPosition = positions.get(i);
                    TextPosition endPosition = positions.get(i + j < size ? i + j : i + j - 1);
                    // 创建实体对象
                    KeyWordEntity entity = new KeyWordEntity();
                    entity.setKeyWord(keyWord);
                    // 获取起始字符坐标
                    Matrix matrix = startPosition.getTextMatrix();
                    float x = matrix.getTranslateX();
                    float y = matrix.getTranslateY();
                    // 获取结束字符坐标
                    Matrix endMatrix = endPosition.getTextMatrix();
                    float x2 = endMatrix.getTranslateX();
                    // 获取字体大小
                    float fontSizeInPt = startPosition.getFontSizeInPt();
                    entity.setX(x);
                    entity.setY(y - fontSizeInPt / 5);
                    float width = i + j < size ? x2 - x : x2 - x + fontSizeInPt;
                    entity.setWidth(width);
                    entity.setHeight(fontSizeInPt);
                    keyWordEntityList.add(entity);
                }
            }
        }
    }

    public List<KeyWordEntity> getKeyWordEntityList() {
        return keyWordEntityList;
    }
}

(2)创建KeyWordEntity实体类

创建一个KeyWordEntity实体类,用于表示需要查找的关键字文本,关键字也就是我们需要替换的文本内容,一般在实际开发中,就相当于是模板占位符内容。实体类需要设置关键字名称、文本的坐标信息。

package pdfbox.demo.text.keyword;

import java.io.Serializable;

/**
 * @version 1.0.0
 * @Date: 2023/7/18 11:22
 * @Author ZhuYouBin
 * @Description: 查找的关键字
 */
public class KeyWordEntity implements Serializable {
    private String keyWord;

    private float x;
    private float y;
    private float width;
    private float height;

    public String getKeyWord() {
        return keyWord;
    }

    public void setKeyWord(String keyWord) {
        this.keyWord = keyWord;
    }

    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 getWidth() {
        return width;
    }

    public void setWidth(float width) {
        this.width = width;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }
}

(3)下载字体文件

如果你不想使用PDFBox提供的字体,那么你可以使用外部字体文件,字体文件可以去【经典宋体简|经典|字体下载】网站下载。

(4)创建PDFUtil工具类

package pdfbox.demo.text.keyword;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.springframework.core.io.ClassPathResource;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.List;

/**
 * @version 1.0.0
 * @Date: 2023/7/18 16:01
 * @Author ZhuYouBin
 * @Description: 基于PDFBox的工具类
 */
public class PDFUtil {

    /**
     * 读取PDF模板文件,替换指定关键字的数据
     * @param keyWordMap 需要替换的关键字数据,key表示占位符,value表示替换后的内容
     * @param pdfPath PDF模板文件的路径
     * @param destPdf 生成的目标PDF文件
     */
    public static void replaceText(Map<String, String> keyWordMap, String pdfPath, String destPdf) throws IOException {
        if (keyWordMap == null || keyWordMap.keySet().size() <= 0) {
            return;
        }
        Set<String> keyWordSet = keyWordMap.keySet();
        // 1、读取PDF模板文件
        PDDocument document = PDDocument.load(new File(pdfPath));
        // 2、创建自定义文本提取器
        KeyWordPositionStripper stripper = new KeyWordPositionStripper(new ArrayList<>(keyWordSet));
        stripper.setSortByPosition(true);
        // 注意: writeString() 方法必须执行 getText() 方法之后才会执行
        stripper.getText(document);
        // 3、获取关键字实体对象
        List<KeyWordEntity> keyWordEntityList = stripper.getKeyWordEntityList();
        // 4、替换指定关键字文本内容
        PDPageContentStream stream = new PDPageContentStream(document, document.getPage(0), PDPageContentStream.AppendMode.APPEND, true);
        // 5、加载外部字体文件,这里是直接通过File加载,如果你是SpringBoot项目,则可以通过流加载
        PDType0Font font = PDType0Font.load(document, new File("D:\\simsun.ttf"));
        // 6、循环替换文本内容
        for (KeyWordEntity keyWord : keyWordEntityList) {
            stream.setNonStrokingColor(Color.WHITE);
            stream.addRect(keyWord.getX(), keyWord.getY(), keyWord.getWidth(), keyWord.getHeight());
            stream.fill();
            // 设置画笔颜色
            stream.setNonStrokingColor(Color.BLACK);
            // 替换关键字文本内容
            stream.beginText();
            stream.setFont(font, 14);
            stream.newLineAtOffset(keyWord.getX(), keyWord.getY());
            stream.showText(keyWordMap.get(keyWord.getKeyWord()));
            stream.endText();
        }
        // 关闭内容流
        stream.close();
        // 保存替换之后的文档
        document.save(destPdf);
        // 关闭文档
        document.close();
    }

    public static void main(String[] args) throws IOException {
        Map<String, String> keyWordMap = new HashMap<>();
        keyWordMap.put("{{name}}", "张三");
        keyWordMap.put("{{age}}", "25");
        keyWordMap.put("{{sex}}", "男");
        keyWordMap.put("{{address}}", "福建省厦门市");
        // 模拟测试
        PDFUtil.replaceText(keyWordMap, "D:\\pdfbox-template.pdf", "D:\\new-document.pdf");
    }
}

(5)运行效果

这里的PDF模板文件如下图所示:

使用PDFBox替换模板文件的内容之后,运行结果如下所示:

(6)不足之处

虽然这里可以实现替换文本内容,但是这个代码仍然存在一些不足之处,有以下几点:

  • 1、替换的文本位置无法保证和原文本内容对齐,需要自己根据实际模板,调整相应坐标位置。
  • 2、当替换的文本内容太多,会覆盖后面的文本内容。
  • 3、目前只能够替换指定页面的文本内容。
  • 4、其他不足。。。

到此,PDFBox操作文本就介绍完啦。

综上,这篇文章结束了,主要介绍PDFBox操作PDF文档之读取指定页面文本内容、读取所有页面文本内容、根据模板文件生成PDF文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/767281.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】STL之list容器的模拟实现

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处。 文章目录 前言一、list的三个类的关系分析图vector和list的区别1.节点的成员变量以及构造函数2.list的迭代器 二、list的增删查改工作2.1inse…

【数据结构】24王道考研笔记——图

六、图 目录 六、图定义及基本术语图的定义有向图以及无向图简单图以及多重图度顶点-顶点间关系连通图、强连通图子图连通分量强连通分量生成树生成森林边的权、带权网/图特殊形态的图 图的存储及基本操作邻接矩阵邻接表法十字链表邻接多重表分析对比图的基本操作 图的遍历广度…

# Linux下替换文件中的颜色等控制字符的方法

Linux下替换文件中的颜色等控制字符的方法 文章目录 Linux下替换文件中的颜色等控制字符的方法1 Linux下的控制字符&#xff08;显示的文字并不是他本身&#xff09;&#xff1a;2 颜色字符范例&#xff1a;3 替换4 最后 我们在shell编程显示输出时&#xff0c;会定义文字颜色&…

ESD放电模式以及电源箝位 (power clamp )电路

目录 1.ESD的基本概念 2.ESD放电模式与泄放路径 2.1 I/O端与 Vcc或者 I/O端与 Vss 2.2 I/O端与 I/O端 2.3 Vcc&#xff08;电源端&#xff09;与Vss&#xff08;地端&#xff09; 2.4不同类型电压源 3. 电源箝位 (power clamp )电路 4.全芯片ESD保护电路系统框图 参考…

wsl2 kali linux install android studio Android SDK platforms

studio下载网址为 https://developer.android.google.cn/ 解压后在bin目录下运行studio.sh提示错误&#xff1a; unable to access android sdk add-on list 选择“setup proxy” 选择Manual proxy configuration 设置 Host name 为&#xff1a;mirrors.neusoft.edu.cn 设置…

从零开发短视频电商 单元测试(TestNG)

文章目录 简介简单示例执行测试并查看测试报告方式一 在IDEA中运行testng.xml文件方式二 在IDEA中运行测试类或者package方式三 在Maven中运行测试 统计测试覆盖率方式一 IDEA 支持详细的代码测试覆盖率统计方式二 Maven支持测试覆盖率 在IDEA中创建测试用例使用 IDEA 快速创建…

LLaMA微调记录

本文基于开源代码https://github.com/Lightning-AI/lit-llama/tree/main执行微调 其他参考链接&#xff1a; Accelerating LLaMA with Fabric: A Comprehensive Guide to Training and Fine-Tuning LLaMA - Lightning AI 结构化数据示例&#xff1a; BelleGroup/train_0.5M_…

动态sql语句

1.1 动态sql语句概述 Mybatis 的映射文件中&#xff0c;业务逻辑复杂时&#xff0c; SQL是动态变化的&#xff0c;此时在前面的学习中 SQL 就不能满足要求了。 参考的官方文档&#xff1a; 1.2 动态 SQL 之<if> 根据实体类的不同取值&#xff0c;使用不同的 SQL语句…

常见安装工具以及命令(工作常用)长期维护

dockermongodbnginxredis 1.docker 启动2.docker 安装 MongoDB3.启动nginx4.redis配置&#xff0b;安装4.1 Redis的启动和停止4.2 后台启动方式 systemctl start docker redis-server /root/myredis/redis.conf docker start mymongo docker exec -it mymongo /bin/bash 1.doc…

代码随想录算法训练营第58天 | 单调栈 ●739 每日温度 ●496下一个更大元素I ●503下一个更大元素II ●42 接雨水 ●84 柱形图中最大的矩形

#单调栈&#xff1a; 单调栈就是保持栈内元素有序。和栈与队列&#xff08;239. 滑动窗口最大值 自己写一个class来实现单调队列&#xff09;一样&#xff0c;需要我们自己维持顺序&#xff0c;没有现成的容器可以用。 通常是一维数组&#xff0c;要寻找任一个元素的右边或者…

浅谈物联网工程专业:技术融合与未来发展

技术融合与未来发展 引言1. 专业的定义与概述2. 专业的知识体系3. 专业的实践应用4. 专业的发展趋势5. 专业的就业前景结语&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收…

zabbix 企业级级监控(1) 监控自己

重点一 Zabbix简介在企业网络运维过程中&#xff0c;管理员必须随时关注各服务器和网络的运行状况&#xff0c;以便及时发现问题&#xff0c;尽可能减少故障的发生。当网络中的设备&#xff0c;服务器等数量较多时&#xff0c;为了更加方便&#xff0c;快捷的获得监控信息&…

【软件测试面试】腾讯数据平台笔试题-接口-自动化-数据库

数据库题 答案&#xff1a; Python编程题 答案&#xff1a; 接口参数化题 答案&#xff1a; 接口自动化题 答案&#xff1a; 以下是我收集到的比较好的学习教程资源&#xff0c;虽然不是什么很值钱的东西&#xff0c;如果你刚好需要&#xff0c;可以评论区&#…

6.3.6 利用Wireshark进行协议分析(六)----网页提取过程的协议分析

6.3.6 利用Wireshark进行协议分析&#xff08;六&#xff09;----网页提取过程的协议分析 利用Wireshark捕获网页访问过程中产生的应用协议报文&#xff0c;还原Web服务中报文的交互过程&#xff0c;为了防止网页直接从本地缓存中获取&#xff0c;我们首先需要清空浏览器保存的…

GO语言GMP模型

目录 程序入口 协程主动让出: 被动让出: schedule 监控线程 程序入口 在执行一系列检查和初始化&#xff08;创建多少个P&#xff0c;与M&#xff10;关联&#xff09;后&#xff0c;进入runtime.main,创建main goroutine,执行mian.mian。 一开始GO语言的调度只有M和G。每个M…

【代码随想录 | Leetcode | 第七天】链表 | 链表相交 | 环形链表 II

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来链表相交和环形链表 II的分享✨ 目录 前言面试题 02.07. 链表相交142. 环形链表 II总结 面试题 02.07. 链表相交 ✨题目链接点这里 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找…

C/C++ new A与new A()的区别

在C中&#xff0c;POD是“Plain Old Data”的缩写&#xff0c;即“普通旧数据”。POD data是指一种特殊类型的数据结构&#xff0c;它们具有简单的内存布局&#xff0c;没有构造函数、虚函数、私有/保护非静态数据成员&#xff0c;也没有虚继承等特性。这些数据结构可以直接通过…

k8s与集群管理

从docker讲起 终于有人把 Docker 讲清楚了&#xff0c;万字详解&#xff01; Docker资源&#xff08;CPU/内存/磁盘IO/GPU&#xff09;限制与分配指南 默认情况下&#xff0c;Docker容器是没有资源限制的&#xff0c;它会尽可能地使用宿主机能够分配给它的资源。如果不对容器资…

C++--day3(内联函数、结构体、类、封装、this、构造函数、析构函数)

#include <iostream>using namespace std;class My_stack { private:int *ptr; //指向堆区空间int top; //记录栈顶元素int size; public://有参构造My_stack(int size):ptr(new int[size]),top(-1){this->sizesize;cout<<"My_stack::有参构造&…

基于STM32的智能喂养系统

基于STM32的智能喂养系统 系统简介 自动检测环境温湿度&#xff0c;当温湿度低于阈值时自动打开加湿器&#xff1b;自动检测水位&#xff0c;当水位低于阈值时自动加水&#xff1b;自动检测有害气体&#xff0c;当检测到有害气体时自动打开风扇&#xff1b;同步状态到微信小程…