You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何提取PDF中PDFormXObject的流内容及BT-ET间操作符?

解决PDFBox获取FormXObject内容流的问题

看起来你在使用PDFBox提取FormXObject的核心内容时遇到了瓶颈——只拿到了字典元数据,却没拿到对应PostScript里PaintProc的实际绘图/文本指令。这是因为你调用的getContentStream().dump()方法仅输出对象的元信息,并不会解析和展示内容流里的操作符与文本内容。我来帮你梳理正确的解决思路:

问题根源

PDContentStreamdump()方法本质是输出对象的结构信息(比如字典里的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:获取解码后的原始流文本

如果你需要直接拿到streamendstream之间的完整原始内容(自动解码压缩),可以通过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

火山引擎 最新活动