这篇文章,主要介绍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;}@Overrideprotected 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文档。