如何解析未知长度的二进制 PDF 流?

U. *_*ndl 3 pdf parsing stream

来自 PDF 文档:“流字典后面的关键字流应后跟行尾标记,该标记由 CARRIAGE RETURN 和 LINE FEED 或仅 LINE FEED 组成,而不是单独的 CARRIAGE RETURN。组成流的字节序列位于流关键字后面的行尾标记和 endstream 关​​键字之间;流字典指定了确切的字节数。

由于内容可能是二进制的,因此endstream的出现不一定表示流的结束。现在考虑这个流时:

%PDF-1.4
%307?
5 0 obj
<</Length 6 0 R/Filter /FlateDecode>>
stream
x234+T03203T0^@A(235234?^_d256220^314^U310^E^@[364^F!endstream
endobj
6 0 obj
30
endobj
Run Code Online (Sandbox Code Playgroud)

长度是一个间接对象如下的流。显然,该长度只能在解析流才能读取。

我认为让 Length 成为只能在流才能解决的间接对象是设计缺陷。虽然它可以帮助 PDF 编写者按顺序输出 PDF,但它使 PDF 阅读器的解析变得非常困难。考虑到 PDF 文件的阅读频率高于写入频率,我不明白这一点。

那么如何正确解析这样的流呢?

mkl*_*mkl 5

长度是跟随该流的间接对象。显然,该长度只能在解析流后才能读取。

如果假设要从头到尾依次读取文件,那么这是一个可以理解的结论。

但是,这种假设是不正确的,因为从前面解析 PDF 并在运行时确定 PDF 对象并不是解析 PDF 的推荐方式。

虽然 ISO 32000-1 在这里有点含糊,只是说

符合要求的读者应该从头开始阅读 PDF 文件。

(ISO 32000-1,第 7.5.5 节文件预告片)

ISO 32000-2 明确规定:

除线性化 PDF 文件外,所有 PDF 文件都应使用以下子条款中所述的预告片和交叉引用表来阅读。由于在增量更新后处理对象的方式,以串行方式读取非线性化文件是不可靠的。(请参阅 6.3.2,“PDF 处理器的一致性”。)

(ISO 32000-2,第 7.5 节文件结构)

因此,对于您的 PDF 摘录,PDF 处理器试图读取对象 5 0

  • 5 0在交叉引用中查找对象并获取其在文件中的偏移量,
  • 转到该偏移量并开始读取对象,首先解析流字典,
  • atstream关键字识别出该对象是一个流并检索其Length值,该值恰好是对 的间接引用6 0
  • 6 0 在交叉引用中查找对象并获取其在文件中的偏移量,
  • 转到该偏移量并读取对象,数字30
  • 读取流对象的流内容,5 0知道其长度为 30。

像您这样的方法被明确认为是“不可靠的”。


我认为让 Length 成为只能在流后才能解决的间接对象是设计缺陷。

如果没有交叉引用,你是对的。这也是 FDF 格式(没有强制交叉引用)指定的原因:

FDF 基于 PDF;它使用相同的语法并具有基本相同的文件结构(7.5,“文件结构”)。但是,它在以下方面与 PDF 不同:

[...]

  • 流的长度不应由间接对象指定。

(ISO 32000-2,第 12.7.8 节表格数据格式)


关于评论:

所以我是正确的,不能按顺序解析 PDF,

虽然 PDF 的原始设计可能是为了顺序解析,但它已经被进一步开发,只考虑通过交叉引用进行访问。PDF 不再意味着要按顺序解析。当我在 90 年代末开始处理 PDF 时,情况已经如此。

唯一的原因是可以在流之后定义所需的二进制流长度。

这远不是唯一的原因,还有更多情况需要交叉引用查找才能正确解析。

正如@mkl 所指出的,解析器必须在 PDF 文件结束之前读取某处以获得 startxref,希望它不会在二进制流的中间开始解析。

那不正确。PDF 必须以“%%EOF”结尾加上可选的行尾。在此之前必须有一个行尾,在此之前有一个数字,在此之前必须有一个行尾,在那个 startxref 之前。

这在 ISO 32000-1 中已经明确表达:

文件的最后一行应仅包含文件结束标记%%EOF。前两行应按顺序包含关键字startxref和解码流中从文件开头到最后一个交叉引用部分中的外部参照关键字开头的字节偏移,每行一行。

(ISO 32000-1,第 7.5.5 节文件预告片)

因此,如果 PDF 有效,则没有“处于二进制流中间”的危险。

我不喜欢 PDF 格式的另一件事是:在开发解析器时,您通常会使用正在处理的某些元素创建测试文件。这种方法似乎适用于除流之外的所有内容。语法元素的绝对文件位置和多次随机访问的要求使这项任务更加困难。

您似乎误以为 PDF 格式是一种标记文本格式,如 HTML。不是这种情况。尽管使用一些 ASCII 关键字定义了许多语法元素并且存在“行”,但 PDF 是一种二进制格式,交叉引用表不是噱头而是对象的中央访问枢纽,随机访问的优化是通过设计完成的.