如何找到预告片词典?

时间:2011-06-24 02:06:01

标签: parsing pdf

通过PDF规范,它表示trailer位于startxref之前。对我而言,xref可以出现在文档的任何位置,但trailer仍然出现在startxref之前。这是有道理的,直到你必须解析它,因为你必须反过来解析你不能考虑注释或字符串。让我们更加古怪。

trailer<< %\
  /Size 4 %\
  /Root 1 0 R %\
  /Info 4 0 R %\
  /Key (\
trailer<< %\
  /Size 4 %\
  /Root 2 0 R %\
  /Info 3 0 R %\
>>%)
>>&)
% test test )
startxref
 15
%%EOF

这是一本非常有效的预告片。第一个是真正的预告片,但第二个是“字符串”。在这种情况下,反向解析将无法捕获注释。如果它的注释或字符串分开,寻找字符串预告片将会失败。我想知道找出预告片开始位置的最佳方法是什么?

更新 - 此预告片似乎在Acrobat Reader中打开

%PDF-1.3
%âãÏÓ
xref
0 4
00000000 65535 f
00000110 00000 n
00000250 00000 n
00000315 00000 n
00000576 00000 n

1 0 obj <<
  /Type /Catalog
  /Pages 2 0 R
  /OpenAction [ 3 0 R /XYZ null null null ]
  /PageLabels << /Nums [0 << /S /D >> ] >>
>>
endobj
2 0 obj <<
  /Type /Pages
  /Kids [ 3 0 R ]
  /Count 1
>>
endobj
3 0 obj <<
  /Type /Page
  /Parent 2 0 R
  /Resources << >>
  /MediaBox [ 0 0 612 792 ]
>>
endobj
4 0 obj <<
  /Producer (Me)
  /CreationDate (D:20110626000000Z)
>>
endobj

trailer<< %\
  /Size 4 %\
  /Root 1 0 R %\
  /Info 4 0 R %\
  /Key (\
trailer<< %\
  /Size 4 %\
  /Root 2 0 R %\
  /Info 3 0 R %\
>>%)
>>%)
% test test )
startxref
 15
%%EOF

就语法而言,这符合规范。不知何故,他们似乎能够知道他们是在评论中还是在字符串中。解析L-R,第二个预告片是一个%尾的字符串,在预告片之后有一条评论。但是R-L解析,你不知道第一个是否是注释的一部分,或者是字符串定义的结尾。

另一个例子:

%PDF-1.3
%âãÏÓ
xref
0 8
0000000000 65535 f
0000000210 00000 n
0000000357 00000 n
0000000428 00000 n
0000000533 00000 n
0000000612 00000 n
0000000759 00000 n
0000000830 00000 n
0000000935 00000 n

1 0 obj <<
  /Type /Catalog
  /Pages 2 0 R
  /OpenAction [ 3 0 R /XYZ null null null ]
  /PageLabels << /Nums [0 << /S /D >> ] >>
>>
endobj
2 0 obj <<
  /Type /Pages
  /Kids [ 3 0 R ]
  /Count 1
>>
endobj
3 0 obj <<
  /Type /Page
  /Parent 2 0 R
  /Resources << >>
  /MediaBox [ 0 0 612 792 ]
>>
endobj
4 0 obj <<
  /Producer (Me)
  /CreationDate (D:20110626000000Z)
>>
endobj
5 0 obj <<
  /Type /Catalog
  /Pages 6 0 R
  /OpenAction [ 7 0 R /XYZ null null null ]
  /PageLabels << /Nums [0 << /S /D >> ] >>
>>
endobj
6 0 obj <<
  /Type /Pages
  /Kids [ 7 0 R ]
  /Count 1
>>
endobj
7 0 obj <<
  /Type /Page
  /Parent 6 0 R
  /Resources << >>
  /MediaBox [ 0 0 100 100 ]
>>
endobj
8 0 obj <<
  /Producer (Me)
  /CreationDate (D:20110626000000Z)
>>
endobj

trailer<< %\
  /Size 8 %\
  /Root 1 0 R %\
  /Info 4 0 R %\
  /Key (\
trailer<< %\
  /Size 8 %\
  /Root 5 0 R %\
  /Info 8 0 R %\
>>%)
>>%)
% test test )
startxref
 17
%%EOF

此示例在Adobe中正确显示。在我的上一个案例中,你声称它会失败,因为“root”节点无效,但是这个新的样本,root是有效的,但它从未实际使用过。那么它不应该显示100x100窗口,而不是8.5“x11”?

关于资源

  (Required; inheritable) A dictionary containing any resources required by the page 
(see Section 3.7.2, “Resource Dictionaries”). If the page requires no resources, the 
value of this entry should be an empty dictionary. Omitting the entry entirely
indicates that the resources are to be inherited from an ancestor node in the page 
tree.

5 个答案:

答案 0 :(得分:4)

startxref语句通常位于文件的末尾,预告片在其前面。

更新: 上面的介绍性句子没有明确表达,正如杰里米·沃尔顿正确观察到的那样(尽管后来的评论在我的回答中暗示了例外情况)。它应该是:startref语句通常作为单个实例出现在文件的末尾,预告片在它之前(除非你的文件经历了增量更新,在这种情况下你可能会有不同的与各种预告片交叉引用的实例。“

如果PDF中有注释,那么当涉及外部参照表字节偏移计算的字节计数时,它们与“真实”PDF页面描述代码相同。因此,正确解析它不是问题。

直接引用“马口”(PDF specification ISO 32000-1,第7.5.5节):

  

“PDF文件的预告片使符合本标准的阅读器能够快速找到交叉引用表和某些特殊对象。符合条件的阅读器应该从其末尾读取PDF文件。最后一行文件只应包含文件结束标记%%EOF。前两行应包含每行一个顺序的关键字startxref和从开头开始的解码流中的字节偏移量文件到最后一个交叉引用部分xref keyword的开头.startxref行前面应该是预告片词典,由关键字trailer后跟用双角括号[...]“

包围的一系列键值对

此处需要考虑的关键表达方式是 “最后 交叉引用部分”

如果您正在考虑更新预告片,请查看第7.5.6节。

是的,你必须反过来解析。要读取的第一个交叉引用部分是文件中出现的最后一个 - 它将具有前一个最后一个预告片。第二个要阅读的是文件中出现的最后一个 - 带有前面的最后一个预告片。 Etc.pp ....如果您必须阅读多个预告片/外部参照片段,您阅读的每个片段都必须包含对下一个片段的引用。

你是否认为“评论”是你可以自由插入PDF而不破坏其结构的东西:然后思考不同。插入注释后,您必须至少更新外部参照表(可能还有对象的/Length个键)。


更新2: Jeremey构建的trailer<<...>>字典可能根本就不是有效字典,因此它也不是有效的预告片< / em>字典......

无论如何,根据规范,预告片字典必须包含“一系列键值对”。预告片字典中的“合法”键限于一个非常窄的集合,其中一些甚至是可选的(参见7.5.5节中的表15)。

Jermey似乎已经构建了他的示例,以便(错误地)将此片段理解为潜在有效的预告片词典:

trailer<<%) >>
% test test )

当然,这根本不是字典,因为我们在这里看不到任何键值对。

他的完整示例也无效,因为名为/Key的“密钥”不在预告片的有效密钥名称中(根据表15:/Size,{ {1}},/Prev/Root/Encrypt/Info/ID)。

因此,Jeremy应该在他的PDF解析代码中完成所有理智,甚至是最疯狂的PDF处理库所做的事情:放弃明显无效的构造而不是在其中搜索感觉并告诉用户“你该死的PDF已损坏,因为我们无法识别文件“

的假定预告片部分中的有效密钥

答案 1 :(得分:3)

问:医生,我这样做会很疼。
答:不要这样做。

解析PDF结尾的正确方法是这样的:

  1. 查找最后startxref
  2. 备份到该字节偏移并开始解析外部参照表条目
  3. 在最后一个外部参照表之后,解析预告片。
  4. 如果你只是想找到预告片,你真的不需要解析对象数和字节偏移等等。您需要做的就是查看外部参照的给定子部分中有多少条目,跳过20 * N字节,并检查另一个子部分(或“预告片”)。当你最终点击“预告片”而不是数字时,你就在那里。

    那么为什么在地球上你只想要预告片呢?


    当我在浏览PDF参考资料时,我希望找到一些文字说明标题/正文/外部参照/预告片必须按此顺序排列。我没有。

    我发现的是这样的:

      

    基本符合PDF文件应由以下四个要素构成(见图2):
       - 单行标题...
       - 身体......
       - 交叉引用表...
       - 预告片......

    这些部分前面有子弹,而不是数字。

    因此,所有提示符合PDF可以通过交换正文和外部参照的顺序而逃脱。另一方面,标题必需是第一个,预告片必需是最后一个,并且PDF的所有部分都按该顺序列出。这意味着秩序,但不会在法庭上搁置。

    但是如果你看一下名为“PDF文件的初始结构”的图2(第7章,第5.1节),你会看到视觉上定义的顺序。这有点薄,但无论如何我都会坚持下去。

    我发现在xref表之后将其正文放入某些PDF查看器(特别是程序试图修复它的格式错误的PDF)时,我不会感到惊讶。

    我已经使用PDF文件十多年了。在那段时间里,我永远不会看到一个PDF,其中外部参照在身体之前。而且我看到一些真的搞砸了PDF。

    因此,虽然我的“解析PDF的正确方法”可能不是Iron Clad,但它仍然非常耐用。


    如果您绝对坚持要备份以找到关键字“预告片”,那么在解析出您找到的预告片后,您可以查找“关闭数组或字典”标记。如果它被包裹在一个字符串中,则必须转义所有名称斜杠,导致Bad Parsing。您不能在名称中包含空格...这样只留下数组和字典。

    但是你在现实生活中遇到这个问题的几率是天文数字很小,除非你打算打破PDF软件并自己创建这些PDF。这会让你的动机受到质疑。

答案 2 :(得分:2)

Jeremy一再编辑他的问题和示例代码。这使我的原始答案和我的一些原始评论部分无效,并忽略了这一点。

事实是(并且是印前贸易和行业人士中众所周知的事实):Adobe在很多情况下默默地进行,没有警告过程并显示未通过严格有效性检查的PDF文件。

杰里米似乎已经构建了这样一个案例。他的最新例子将使任何PDF解析器将以下片段解释为预告片(我删除了注释):

trailer<<
  /Size 4
  /Root 2 0 R
  /Info 3 0 R
>>

然而,获取此预告片中的信息将导致解析器在对象2处查找/Root(而对象2实际上是/Type /Pages时应该是/Type /Catalog作为根对象)。

因此,PDF解释器必须

  • (a)继续搜索预告片的另一个实例,以确定下一个实例是否包含合法的PDF信息,
  • (b)或放弃处理文件并抛出错误。

Adob​​e似乎遵循备选方案(a)。

Ghostscript似乎遵循备选方案(b)。


注意, 根据我的字节计数,Jeremy的PDF示例还有一个问题:它的外部参照表无效。它每行只有16个字节而不是20个。来自PDF规范文档:

  

[....]交叉引用条目本身,每行一个。每个条目应精确到20个字节长,包括行尾标记。有两种交叉引用条目:一种用于正在使用的对象,另一种用于已删除的对象,因此是免费的。两种类型的条目都具有相似的基本格式,区别在于关键字 n (对于正在使用的条目)或 f (对于免费条目)。使用中条目的格式应为:

     
    

nnnnnnnnnn ggggg n eol

  
     

其中:

     
    

nnnnnnnnnn 应为解码流中的10位字节偏移量
     ggggg 应为5位数代数      n 应为将此标识为使用中条目的关键字      eol 应为2个字符的行尾序列

  
     

解码流中的字节偏移量应为10位数字,必要时用前导零填充,给出从文件开头到对象开头的字节数。

因此,为了使Jeremy的外部参照表成为有效的,它应该用另外两个前导'0'填充并阅读:

xref
0 4
0000000000 65535 f 
0000000110 00000 n 
0000000250 00000 n 
0000000315 00000 n 
0000000576 00000 n 

但是,将这些2'0'添加到每个外部参照线上,也会使每个对象偏移10个字节,因此nnnnnnnnnn数字也应该被修正(懒惰,我没有这样做)。 / p>

因此,Acrobat确实打开了Jeremy的构建文件(没有任何警告)

  • (1)尽管预告片定义无效,
  • (2)尽管有明显不合规的外部参照表。

这为我在第二段中所说的内容增加了两个证据:Adobe的PDF解析接受违反Adobe自己的PDF标准的文件。

这很不幸。它可以让懒惰的开发人员编写草率的代码,这些代码可以发送不符合规范的PDF文件而不会受到惩罚。 Adobe没有完全拒绝这些糟糕的文件这一事实可能符合“用户友好性”的利益,但会促使违反标准。至少,Adobe在遇到这样的东西时应该总是发出警告。

由于Jeremy似乎正在编写一个想要涵盖所有极端案例的PDF解析器,他的用户应该希望他至少在遇到糟糕的PDF时警告他们。

无论如何:我看到很多不合规的PDF文件由蹩脚的PDF生成器发出。但到目前为止,我从来没有遇到过将trailer部分的评论加入其中的问题。所以试图覆盖角落的情况应该从较低的悬挂果实开始。

答案 3 :(得分:2)

我想我找到了解决方案。经过广泛的测试和其他事情,通过Adobe,我发现adobe所做的是找到可以解析的最后一个已知结构,并从那里开始工作。然后它找到可以正确解析的最后一个预告片。因此,即使在可以解析的最后一个有效预告片之前的预告片中有正确的根节点,如果最后一个预告片中的根无效,它仍然会失败。还需要注意的是,这仍然是基于令牌的解析转发。因为()之间的预告片被忽略,所以流/ endstream之间的预告片也是如此,除非该流具有无效长度,或者在流之后的obj中指定的长度(因为这些对象未在外部参照表中指定)。现在Adobe似乎更进一步,通过实际在外部参照表中的“间隙”中找到预告片,这不符合当前的规范模型,因为预告片最后被发现,而不是在正文中或外部参照表。所以我认为最好的模型是获取外部参照表的最大偏移量,以及外部参照表的位置,如果外部参照表位于对象的最大偏移之后,则使用它,然后从那里开始工作。这将允许我正确解析字符串和注释而不用担心。感谢大家对此事的帮助。希望这有助于人们构建更强大的PDF解析器。

答案 4 :(得分:1)

预告片字典遵循外部参照部分。根据startxref值,跳转到外部参照部分的开头。阅读外部参照部分后,您将看到预告片字典。预告片关键字始终是其中的第一个(在其前面允许使用空格)。 PDF文件允许增量更新,因此您可以遇到具有多个外部参照部分和预告片的PDF文件,但处理规则是相同的,首先处理外部参照部分然后处理预告片。如果文件包含增量更新,则预告片部分将包含对前一个外部参照部分的引用。