zoukankan      html  css  js  c++  java
  • PDF格式详解-ISO 32000-1:2008学习

    1、简介

    PDF(英语:Portable Document Format,缩写:PDF)

    PDF的历史与规范化

    ISO 32000-1:2008

    2、文件结构

    一般的PDF文件由四部分组成

    • header部分:单行,标识文件所遵循的PDF规范的版本
    • body部分:多行,包含了由多个文件对象构成的文件主体
    • cross-reference table 部分:交叉引用表,包含文件中间接对象信息
    • trailer部分:包含交叉引用表的位置以及文件正文中某些特殊对象的位置

    另外,PDF支持文件增量更新,新增部分可以附加到原始文件之后,通过同样类似的结构追加进来。

    按照规范标准约定,PDF文件按照一定的标记排列成行,每行的终结符可以是回车符( 、0x0D、ASCII 13),也可以是换行符( 、0x0A、ASCII 10),也可以两者同时存在( 、0x0D0x0A)表示一行结束。为了统一,行标记占两个字符,通常为空格+回车、空格+换行或者回车+换行这3种形式。包含二进制数据的部分可以是任意多行。

    为了增强PDF格式文件解析处理程序的兼容性,不属于流对象数据的行的行数不得超过255个字符。 有个列外情况,从PDF 1.3开始,签名字典的Contents字符串不受行长的限制。

    以上为基本的PDF格式,PDF在设计时考虑了扩展结构,以适用于在不同环境下的使用。例如线性化PDF文件是PDF文件的一种特殊格式,可以通过Internet更快地进行查看。线性化PDF文件包含允许的信息字节流服务器一次下载PDF文件一页。如果在服务器上禁用了字节流,或者PDF文件未禁用线性化后,必须先下载整个PDF文件才能查看。所有受支持的IDS版本都会生成线性化的PDF文件。对于PDF线性化来说,在页面数量很多的情况下,能突出表现出它快速网络浏览的优势。 

    2.1、Header

    PDF文件的第一行,由5个字符组成,%PDF-后跟形式为1.N的版本号,其中N是介于0和7之间的数字。

    %PDF–1.0
    %PDF–1.1
    %PDF–1.2
    %PDF–1.3
    %PDF–1.4
    %PDF–1.5
    %PDF–1.6
    %PDF–1.7

    PDF低于1.4版本时,唯一由Header确定版本。从1.4开始,版本标识可以从文档的目录字典中获取(文档目录字典位于位于文件Trailer部分的中的Root条目),通常PDF生成器遵循两个版本标记一致原则。

    2.2、Body

    PDF文件的body由代表文档内容的一系列间接对象组成。

    • Boolean values
    • Integer and Real numbers
    • Strings
    • Names
    • Arrays
    • Dictionaries
    • Streams
    • Null object
    • Object Streams

    PDF包括八种基本类型的对象:Boolean values, Integer and Real numbers, Strings, Names, Arrays, Dictionaries, Streams, and the null object。通过这8种基本对象可以用来描述文档的各种特征,例如字体、页面和采样的图像。

    从PDF 1.5开始,Body还可以包含对象流(Object Streams),每个对象流可以包含一系列的间接对象(Indirect Objects)。

    2.3、Cross-Reference Table(交叉引用表)

    交叉引用表包含允许随机访问文件中间接对象的信息,因此无需读取整个文件即可找到任何特定对象。 该表应为每个间接对象包含一个单行条目,并指定该对象在文件正文中的字节偏移量。

    从PDF 1.5开始,部分或全部交叉引用信息可能会包含在交叉引用流(Cross-Reference Streams)中。

    交叉引用表是PDF文件中具有固定格式的唯一部分,它允许随意访问表中的条目。PDF文件可以包含一个或多个交叉引用部分。例如原始文件可能只有一个交叉引用表,如果文件是线性化的,则文件结构是由两部分组成,每次文件进行增量更新时,都应该增加一个交叉引用表结构。

    每个交叉引用部分均应以包含关键字xref的行开头。此行之后是一个或多个交叉引用小节,这些引用小节可以以任何顺序出现。

    对于从未进行过增量更新的文件,交叉引用节应仅包含一个子节,其对象编号从0开始。子节结构对于增量更新很有用,因为它允许将新的交叉引用节添加到PDF文件中,其中仅包含已添加或删除的对象的条目。

    每个交叉引用小节均应包含对象编号连续范围的条目。该小节应以包含以空格隔开的两个数字的行开头,表示该小节中第一个对象的对象编号及该小节中的条目数。

    例如以下,表示从28~32的连续5个对象为其中的一个小节

    xref
    28 5
    ...

    对于给定的对象编号只允许出现在某一个小节,不允许在其他小节重复引用出现。

    在编号行之后是交叉引用条目本身,每行一个条目。每个条目为20个字节长,包括行尾标记。

    交叉引用条目有两种:一种用于使用中的对象,另一种用于已删除并因此是空闲的对象。两种类型的条目都有类似的基本格式,以关键字n(in-use)或f(free)来区分。条目的格式为:

    nnnnnnnnnn ggggg n eol
    ...
    nnnnnnnnnn ggggg f eol
    • nnnnnnnnnn为解码流中10位字节偏移量,绝对占位10,不够10位时用0做前导填充
    • ggggg为5位字节生成编号,绝对占位5,不够5位时用0做前导填充
    • n表示该对象处于使用中,f表示该对象已删除
    • eol表示换行标记,可以是SP CR、SP LF或者CR LF

    4节数据默认以空格为间隔,组成一行,每行固定占位20字节。

    交叉引用表中的第1个编号为0的对象始终是(f),并且生成号为65535。

    除了编号0的对象外,交叉引用表中的所有对象最初的生成号应为0。

    删除间接对象时,应将其交叉引用条目标记为(f),并将其添加到free条目的链表中。

    下次创建具有该对象编号的对象时,条目的生成号应增加1,最大生成号为65535;当交叉引用条目达到此值时,它将永远不会被重用。

    连续的交叉引用表

    下面显示了一个交叉引用表,该表由一个包含6个条目的小节组成:4个正在使用(对象编号1、2、4和5)和2个空闲(对象编号0和3)。 对象编号3已被删除,使用该对象编号创建的下一个对象的生成编号为7。

    xref
    0 6
    0000000003 65535 f 
    0000000017 00000 n 
    0000000081 00000 n 
    0000000000 00007 f 
    0000000331 00000 n 
    0000000409 00000 n

    非连续的交叉引用表

    下面显示了具有4个子节的交叉引用表,总共包含5个条目。

    第1个小节包含1个条目,对象编号为0,标记是f(free),默认的。

    第2个小节包含1个条目,对象编号为3,正在使用(n)。

    第3小节包含2个条目,对象编号为23、24,正在使用(n)。从对象编号23的生成编号为2可以看出,它已被重用(n)。

    第4小节包含1个条目,对象编号30,正在使用中(n)。

    xref
    0 1
    0000000000 65535 f 
    3 1
    0000025325 00000 n 
    23 2
    0000025518 00002 n 
    0000025635 00000 n 
    30 1
    0000025777 00000 n 

    2.4、File Trailer(文件尾)

    PDF文件的trailer文件尾使得应用程序可以快速找到交叉引用表和某些特殊对象。应用程序从文件末尾读取PDF文件。

    文件的最后一行应仅包含文件结尾标记%%EOF。

    倒数往前两行应每行依次包含关键字startxref和解码后的流中从文件起始位置到最后一个交叉引用节中xref关键字开头的字节偏移量。

    startxref行之前必须是Trailer字典,该字典由关键字Trailer组成,后跟一系列用双尖括号(<< ... >>)括起来的键/值对。 总体结构如下:

    trailer
    << key1 value1 key2 value2
    ...
    keyn valuen
    >>
    startxref
    Byte_offset_of_last_cross-reference_section
    %%EOF

     Trailer字典类型

    Key  Type Value
    Size 数字型 所有间接对象的个数。一个PDF文件,如果被更新过,则会有多个对象集合、交叉引用表、trailer,最后一个trailer的这个字段记录了之前所有对象的个数。这个值必须是直接对象。
    Prev 数字型 

    当文件有多个对象集合、交叉引用表和trailer时,才会有这个键,它表示前一个相对于文件头的偏移位置。这个值必须是直接对象。

    Root 字典 Catalog字典(文件的逻辑入口点)的对象号。必须是间接对象。
    Encrypt 字典 文档被保护时,会有这个字段,加密字典的对象号。
    info 字典 存放文档信息的字典,必须是间接对象。
    ID 数组 文件的ID

      

    一个未增量更新过的PDF原始文件的trailer文件尾如下格式,标志是没有Prev这个Key

    trailer
        << /Size 22
           /Root 20R /Info 10R
           /ID [ <81b14aafa313db63dbd6f981e49f94f4>
                 <81b14aafa313db63dbd6f981e49f94f4>
               ]
        >>
    startxref
    18799
    %%EOF

    2.5、Incremental Updates(增量更新)

    PDF文件内容可以增量更新而无需重写整个文件。更新变化数据需要附加到文件尾段,而其原始内容可以保持不变。

    PDF支持增量更新的优点:

    • 可以快速保存对大型文档的较小更改。
    • 在某些情况下,例如通过HTTP连接编辑文档或使用OLE嵌入(Windows特定技术)时,符合条件的编写器无法覆盖原始文件的内容时,增量更新可用于保存对文档的更改。

    增量更新后的文件结构示例:

    增量更新时新增的交叉引用表部分仅包含那些已经被修改,替换,或删掉的对象。删除的对象原封不动的留在文件中,需要通过交叉引用表把对应的交叉引用表项标记为已删除(f)。新增的trailer文件尾包括所有的选项包括之前的trailer文件尾部分,字典中的Prev项标记之前交叉引用部分的位置。被更新过多次的PDF文件通常包含了多个trailer文件尾;每个trailer文件尾都有自己的结束标识(%%EOF)。

    由于更新的部分被添加到文件中,一个文件可能有一个相同对象的多个副本,而保持相同的对象标识(对象编号和生成编号)。可能出现以下情况,例如如果一个文本标注被修改多次并且文件保存了多次的修改。因为文本标注对象没删除,它仍保持着之前相同的对象号和生成号。但一个被修改过的对象副本会被包含在更新部分添加到文件中。更新的交叉引用表部分包含这个新的对象的字节偏移值,覆盖原先交叉引用表中旧的字节偏移值。当一个用户应用程序读取文件时,它必需用这种方法构建它自己的交叉引用表信息,即文件中每个对象的最新副本是被访问的那一个。

    PDF 1.4之后的版本,版本号Version是支持在增量更新中修改。

    Copyright © 2021 Primzahl. All rights reserved.

  • 相关阅读:
    八款常用的 Python GUI 开发框架推荐
    scrapy + mogoDB 网站爬虫
    tkinter界面卡死的解决办法
    通过核心API启动单个或多个scrapy爬虫
    爬虫怎样绕过验证码?
    Python爬虫入门教程 33-100 电影评论数据抓取 scrapy
    Python开发 之 Websocket 的使用示例
    StringBuffer详解
    其实python正则表达式就这样简单明了
    ABAP和Java里的单例模式攻击
  • 原文地址:https://www.cnblogs.com/Primzahl/p/14735567.html
Copyright © 2011-2022 走看看