如何提取PDF中PDFormXObject的流内容及BT-ET间操作符?
解决PDFBox获取FormXObject内容流的问题
看起来你在使用PDFBox提取FormXObject的核心内容时遇到了瓶颈——只拿到了字典元数据,却没拿到对应PostScript里PaintProc的实际绘图/文本指令。这是因为你调用的getContentStream().dump()方法仅输出对象的元信息,并不会解析和展示内容流里的操作符与文本内容。我来帮你梳理正确的解决思路:
问题根源
PDContentStream的dump()方法本质是输出对象的结构信息(比如字典里的Type、BBox这些字段),而不是解析内容流中的绘图指令。要拿到BT/ET之间的文本操作,你需要对内容流做令牌解析或者直接读取解码后的原始流内容。
解决方案
方法1:结构化解析内容流令牌(推荐)
用PDFBox的PDFStreamParser解析FormXObject的内容流,遍历所有令牌,精准筛选BT(文本开始)和ET(文本结束)之间的操作:
import org.apache.pdfbox.contentstream.PDFStreamParser import org.apache.pdfbox.cos.COSName import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject // 假设pdDoc是已加载的PDDocument实例,pageNum是目标页码 def pdResources = pdDoc.getPage(pageNum).getResources() Iterable<COSName> names = pdResources.getXObjectNames() for (COSName name : names) { def xObject = pdResources.getXObject(name) if (xObject instanceof PDFormXObject) { println "=== FormXObject: ${name.getName()} ===" // 创建内容流解析器 def parser = new PDFStreamParser(xObject.getContentStream()) parser.parse() def tokens = parser.getTokens() boolean inTextBlock = false for (def token : tokens) { if (token instanceof String && token == "BT") { inTextBlock = true println "--- 文本块开始 ---" } else if (token instanceof String && token == "ET") { inTextBlock = false println "--- 文本块结束 ---" } else if (inTextBlock) { // 输出文本块内的所有操作(包括字体设置、文本内容、坐标调整等) println token } } } }
方法2:获取解码后的原始流文本
如果你需要直接拿到stream和endstream之间的完整原始内容(自动解码压缩),可以通过COSStream读取:
import org.apache.pdfbox.cos.COSName import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject // 假设pdDoc是已加载的PDDocument实例,pageNum是目标页码 def pdResources = pdDoc.getPage(pageNum).getResources() Iterable<COSName> names = pdResources.getXObjectNames() for (COSName name : names) { def xObject = pdResources.getXObject(name) if (xObject instanceof PDFormXObject) { println "=== FormXObject: ${name.getName()} 原始内容 ===" // 获取COSStream并自动解码(处理FlateDecode等压缩方式) def decodedStream = xObject.getCOSObject().createInputStream() // 读取解码后的完整内容 println decodedStream.text decodedStream.close() } }
额外说明
- 方法1适合精准提取特定操作(比如文本绘制指令
Tj、坐标调整指令TD),方便后续做文本提取或指令分析; - 方法2适合快速查看完整的流内容,了解FormXObject的全部绘图逻辑;
- PDF内容流通常会被压缩,
createInputStream()会自动处理解码,无需手动解压。
内容的提问来源于stack exchange,提问作者PDFDev




