引言
在上一章介绍了 PDF 的基础结构:Header、Body、xref、Trailer,其中 Body(文件体) 是最核心的部分,它存放了 PDF 的全部内容。但和一般的纯文本文件不同,PDF 的 Body 不是“平铺直叙”的数据,而是由一个个 对象(Object) 组成的。 这些对象就像 乐高积木:
- 每个对象定义了文档的一部分内容(比如一页纸、一个字体、一张图像、一个注释);
- 不同对象之间通过 引用 彼此关联;
- 最终拼装成一棵完整的 结构树(Document Tree) ,让阅读器能够正确显示文档。
要真正理解 PDF,必须先看懂对象:
- 它们长什么样?(语法规则)
- 它们分哪几类?(常见对象分类)
- 它们之间是如何组织的?(对象结构树)
对象语法规则
上文说过,PDF对象的内容是7种基本类型(布尔值、数字、字符串、字典、列表、流等),在实际的PDF文件中,最常见的是内容是字典和流,以一个常见的Page对象为例:
3 0 obj
<< /Type /Page
/Parent 2 0 R
/MediaBox [0 0 595 842]
/Contents 4 0 R
/Resources 5 0 R >>
endobj
这个对象的内容(在obj和endobj中间的部分)是由<< 和 >> 包围的字典对象,内部是键值对:
/Type /Page:说明该对象是一个 页面对象。/Parent 2 0 R:父级是对象2 0 obj(即 Pages 对象)。/MediaBox [0 0 595 842]:页面尺寸(595×842 pt ≈ A4 纸)。/Contents 4 0 R:页面的内容流,包含绘制指令。/Resources 5 0 R:资源字典(字体、图片等)。
PDF的字典对象与常规语言中的字典一致,是一系列的键值对,转换为字典是:
{
Type: 'Page',
Parent: '2 0 R',
MediaBox: [0 0 595 842],
Contents: '4 0 R',
Resources: '5 0 R'
}
字典的key一般是以 / 开头的名称(如 /Type),值可以是任意PDF对象类型(名称、字符串、数组、字典、间接引用等)。所以会存在字典嵌套字典、列表嵌套字典的情况。
常见对象类型
对象的/Type字段会标识这个对象的类型,表明他的作用。在实际的PDF文件中,常见的对象类型有:
文档结构对象
- Catalog(/Type /Catalog) :文档根对象,入口。
- Pages(/Type /Pages) :页面树节点,管理所有页面。
- Page(/Type /Page) :单个页面,包含尺寸、内容、资源。
元信息对象
- Info:文档元信息(标题、作者、创建时间等)。
- Metadata:基于 XML 的文档元数据(PDF/A、XMP 使用)。
资源对象
- Font:字体对象,定义字体信息。
- XObject:外部对象,可以是图像(Image XObject)、表单(Form XObject)。
- ColorSpace:颜色空间对象。
- Pattern:图案填充对象。
- Shading:渐变对象。
内容相关对象
- Stream:流对象,存放二进制数据(文本指令流、图片数据、压缩数据)。
- Contents:页面的内容流,描述绘制操作。
导航与交互对象
- Outline(书签) :文档目录结构。
- Annotation(注释) :文档批注、高亮、评论。
- Action:触发操作(跳转页面、打开链接)。
PDF文档结构
综合上述内容,我们可以知道,PDF文件中的对象有不同的类型,并且PDF中对象是互相引用的。分析一篇实际的PDF文件,可以发现,PDF 文档通过 对象引用关系 构建成一棵结构树,构造流程如下:
- 首先找到
Catalog对象,这是页面的入口
123 0 obj
<<
/Type /Catalog
/Outlines 124 0 R
/Pages 2 0 R
/PageLayout /OneColumn
>>
endobj
可以看到,里面标识了Pages对象为2 0 R。
- 找到
Pages对象,这里标记了所有页面
2 0 obj
<<
/Type /Pages
/Kids [ 3 0 R 8 0 R 13 0 R 18 0 R 23 0 R 28 0 R 33 0 R 38 0 R
43 0 R 48 0 R 53 0 R 58 0 R 63 0 R 68 0 R 73 0 R 78 0 R
83 0 R 88 0 R 93 0 R 98 0 R 103 0 R 108 0 R 113 0 R 118 0 R
]
/Count 24
>>
endobj
可以看到,Pages对象中标识了这个PDF共有(/Count 24)24页,Kids中指向每一页分别是3 0 R、8 0 R...
- 找到第一页
3 0 obj对象,这是第一页的入口
3 0 obj
<<
/Type /Page
/Parent 2 0 R
/MediaBox [0.0000 0.0000 595.0000 842.0000]
/Contents 4 0 R
/Resources <<
/XObject << /Im1 6 0 R >>
/ProcSet [ /ImageB ]
>>
>>
endobj
这一页的对象中指定了内容(Contents)在4 0 R,页面宽高MediaBox和引用资源Resources等。
- 在后续的
4 0 obj中又会引用字体对象、字符串对象、流对象等等。如此不断引用,构成了整个PDF树。
总结
理解了对象和结构树,PDF 就不再是“黑盒文件”,而是一个由积木搭建的有序系统。下一篇,将继续深入 交叉引用表和 Trailer,看看阅读器是如何快速找到这些对象的。