为了了解pdf.js源码的详细结构和功能,先来看看PDF的文件结构,然后才能知道pdf.js中的代码是如何解析并且为何这样操作PDF文件的。
PDF文件基本是由header、body、trailer组成。header包含了这个PDF的信息,比如PDF的版本,创建时间,以及作者等。body包含了PDF文件的实际内容,比如文本,图片,以及其他多媒体内容。trailer包含了PDF文件的文件大小、文件校验、文件再硬盘中的位置等。
PDF包含了不仅仅是文本的内容,他还包含图片和其他的多媒体组件,密码保护,可执行的javascript脚本等。基本的PDF文件结构如图所示
PDF的Header
PDF文件的头部包含了PDF的版本号。比如%PDF-1.7
。其中%
再PDF中表示注释。
PDF文档中的Body
body块是用来存储所有被展示给用户的数据。换言之,PDF文档的body包含了文本流、图片、其他多媒体组件等。
xref表格
PDF文档中所有的对象都有一个引用地址被放在了交叉表格中(xref),这使得能够快速并且随机的查找到PDF文档中所需要的对象,所以没有必要读取整个PDF文档的数据来定位某个特定的对象。xref中每个入口包含了20字节,下面是一个示例:
xref
0 1
0000000023 65535 f
3 1
0000025324 00000 n
21 4
0000025518 00002 n
0000025632 00000 n
0000000024 00001 f
0000000000 00001 f
36 1
0000026900 00000 n
交叉表格位于PDF文档的最后面。上面这个示例中包含了4个子块(注意只包含了2个数字的行)。这4行的第一个数字表示了对象的数字,第二个数字表示了当前子块中包含了几个对象。每个对象包含了一个入口,这个对象的引用使用20个字节来展示。
前面10个字节(比如0000000023)表示了这个对象从当前文件的字节偏移数量,从这个PDF文件的最开始计算字节偏移量。
后面用空格分开的表示了当前对象的第几代(已经修改了几次)。接着是字符f
或者n
,表示这个对象是否是在使用中。n
表示在使用;f
表示没使用。
xref中的第一个对象使用了65535代的表示了当前文档开始的位置。
xref中最后一个对象表示的代数式0。
在第二个子块中包含了一个ID为3的只包含了一个组件对象,这个对象开始的位置在25324字节。子块3中包含了4个对象,ID式21然后开始的位置式25518。其余对象的ID分贝式22、23、24。
每个在文件中的对象被指定了一个标记来表示当前对象是否正在被使用(n
表示在使用,f
表示没有使用)。没有使用的对象包含了下一个没有使用对象的引用,以及当自由对象被使用后他的代数值。这种标记方法保证了这个文件中的每个部分都被用上了。
在交叉表中因为0指向了下一个没被使用的对象(f标记的行),对象23,因为对象23也没有使用并且指向了下一个没有使用的对象,所以对象24又被指回了0。
所以如果查看完整的交叉表就应该是这样的
xref
0 1
0000000023 65535 f
3 1
0000025324 00000 n
21 1
0000025518 00002 n
22 1
0000025632 00000 n
23 1
0000000024 00001 f
24 1
0000000000 00001 f
36 1
0000026900 00000 n
第一个示例减去了没有被使用的多余对象,进行了省略的写法。
表示代数的这个数字式递增的。如果将对象变成不使用,那么他的代数会增加为2,如果对象23变成可用,他的代数又会仍然保持为1。然而,当对象23再次被变为不可用,那么他的代数会增加为2。
当一个文档更新之后,他通常包含了多子块。不然他只应该包含一个0开始的子块。
Trailer文档尾
所有的PDF阅读器都应该从文件的尾部开始读起。因为PDF的Trailer包含了交叉表和其他特定对象来读取PDF文档。
一个PDF文件的trailer示例:
trailer
<< /Size 22 /Root 2 0 R /Info 1 0 R >>
startxref
24212
%%EOF
文件的最后一行使用了一个结束符%%EOF
。交叉表xref使用了startxref
字符串来表示xref的开始位置。这个文件的xref开始位置在24212字节。trailer块使用了trailer
字符串表示开始。trailer的内容使用了<<和>>字符来进行包裹(这是字典对的格式)。
trailer块定义了几个关键字,比如/Size
,/Root
,/Info
等。
正向更新
PDF文档被设计为正向更新,表示我们可以增加新的对象在文件的结尾而不是重新覆盖整个文档。这能够快速的保存和修改。下面是一个被修改后的PDF文档的内容。可以看到每次修改都会在文件结尾添加trailer即可。
对于删除对象,是将他标记为f
,每个trailer都是使用%%EOF
进行结尾,并且包含了一个只想上一个xref块的引用值,使用/Prev
关键字来标记。