初步研究了下 PDF 的格式,打算和大家分享一下,如果
- 格式:doc
- 大小:134.00 KB
- 文档页数:11
应该项目需要,初步研究了一下PDF 的格式,打算和大家分享一下,如果有想做PDF 或PDF 阅读器开发的同学们,可以看看。
PDF 格式规范,语法简洁。
但初次查看PDF 却容易给人错觉——PDF 的二进制流太多了,原本清晰的数据格式被杂乱无章的二进制流给淹没了。
例如:Java代码1.stream2.5di?倯? 貓?匚{o绝幾Te:馎HG3.嵶?W}?妮雥??隱q?屸鷭鴊狳!赭€?鼜 ??烖K柽z煜?w? ??鴚¬D¬h?斜?醱釓?x?i佲匃??•?灗 Iu叁剪?4.…………………………像这样一大堆乱码的东西很多,所以,心理上无法耐心的继续看下去。
实际上PDF 就是由四部分组成:数据流的读写操作一般是从文件末尾开始的:PDF 所有的数据类型都是obj ,强调的也是一切皆对象。
我简要的把PDF 常见的几种数据类型给大家说说:注意“% ”表示注释1.Boolean Objects :只有true 和false2.Numeric Objects :●整型:123●浮点型-43.54 ,6.02E233. String Objects :●文字型,用圆括号括起来,如:(Hello World)●十六进制型,用尖括号括起来,如:<901FA3> 分别为90 ,1F ,A34. Name Objects :除null 以外的任意8 位字符组成的字符串,出现在PDF 中,用“/ ”作前导符号,如:/Name1 。
其实这与编程中的常量很类似。
5. Array Objects :PDF 中的数组用方括号“[] ”括起来,里面的数据可以完全不相同,如:[549 3.14 false (Ralph) /SomeName] 该数组里面分别装有 5 种不同的类型。
6.Dictionary Objects :其实就类似于Java 的Map 对象——即以key ,value 的形式成对出现。
用的时候要用“<<…>>”把key ,value 括起来。
例如:<</Name (leo) >>表示key为“name”的Name Objects ,而value 为“leo ”字符串。
<<>> 内部也可以嵌套使用,在PDF 源文件中应用的非常广泛。
7.Stream Objects :顾名思义,肯定有流有关。
这就是PDF 中专门用于存放二进行流的对象。
PDF 的实现者对String Objects 通常会做限制,但是Stream Objects 却没有这样的限制。
因此stream object 用在图片,页面描述信息上是非常的适合的。
看看使用示例:Java代码1.7 0 obj %某一个Image类型的对象2.<<3./Type /XObject4./Subtype /Image5./Name /Im76./Width 1897./Height 1898./BitsPerComponent 89./ColorSpace [/Indexed /DeviceRGB 255 …>]10./Mask [255 255]11./Length 383212./Filter /FlateDecode13.>> %紧接着这个对象定义后的Dictionary Objects对象后面14.stream %定义它的Stream Objects15.%这里放二进制流16.endstream8.Null Object :区别与其它任何对象,用关键字null 标注的对象就是null object 。
9.Indirect Objects :当然就是类似Java 中的“引用”概念啦,举个例子就说明一切了。
Java代码1.7 0 obj %有一个索引号为7 0的对象2.<< /Length 8 0 R >> % 在它的Length属性引用了一个索引号为8 0的对象3.%在这里“8 0 R”表示对8 0的对象引用(Reference)4.stream5.………….6.endstream7.endobj8.9.10.8 0 obj %被上面7 0对象所引用的那个索引号为8 0的对象11.77 %7 0对象的二进制流的长度12.endobj注意:obj …… endobj PDF文档组成的最小组成部分。
表示一个完整对象的定义够了,说了这么多,让我们实战吧。
现在我们来看一个完整的示例,该示例可以在附件中下载。
看的时候,可以按照下面的步骤一步一步来分析PDF 源文件:Java代码1.%PDF-1.32.%3. 2 0 obj %2 0 obj,里面是一些PDF的基本信息。
比如4.<<5./CreationDate (D:20081029093606+08'00')%创建时间6./ModDate (D:20081029093606+08'00') %最后修改时间7./Producer (leo 4.30 \(0615\))%创建人8./Creator (easyPDF SDK 4.3)%制作人9.>> %你还可以自由的编写任何信息10.endobj11.12.1 0 obj13.<<14./Count 015.>>16.endobj17.18.3 0 obj %这是PDF的根结点(不是起始位置)19.<<20./Type /Catalog21./Pages 4 0 R %这是所有页面的集合,我们主要关心这个22./Outlines 1 0 R23.>>24.endobj25.26.7 0 obj27.<<28./Type /XObject29./Subtype /Image30./Name /Im731./Width 18932./Height 18933./BitsPerComponent 834./ColorSpace [/Indexed /DeviceRGB 255 <%这里放每个像素点的值>]35./Mask [255 255]36./Length 383237./Filter /FlateDecode38.>>39.stream40.%这里放的全是图片的二进制流……..41.endstream42.endobj43.44.8 0 obj45.<<46./Length 5147./Filter /FlateDecode48.>>49.stream50.%这里放的全是图片的二进制流……..51.endstream52.endobj53.54.6 0 obj %重点分析一下这个55.<<56./Type /Page57./Parent 4 0 R58./MediaBox [ 0 0 595.276 841.89 ]59./Resources <<60./XObject <<61./Im7 7 0 R62.>>63./ProcSet [/PDF /Text /ImageB /ImageC] >>64./Contents 8 0 R65.>>66.endobj67.68.4 0 obj69.<<70./Type /Pages71./Kids [72.6 0 R %继续查找6 0 obj来看看具体的页面情况73.]74./Count 175.>>76.endobj77.78.xref %索引,f表示该地址未被使用,n表示已经被占用。
79.0 9 %索引的顺序与对象在文中出现的序号是一致的,比如80.0000000000 65535 f %默认占位符,不使用。
81.0000000177 00000 n %表示0 1 obj的偏移地址82.0000000015 00000 n %表示0 2 obj的偏移地地下面的以些类推83.0000000208 00000 n84.0000006159 00000 n85.0000000000 00000 n %表示0 5 obj的偏移为0,可能被删除操作给干掉了。
86.0000005981 00000 n87.0000000274 00000 n88.0000005858 00000 n89.trailer90.<<91./Size 992./Root 3 0 R %这个属性信息最重要,我们可以直接找到根结点93./Info 2 0 R94./ID[<112cbcc8320742d1cbe148d4ec0d7dba><112cbcc8320742d1cbe148d4ec0d7dba>]95.>>96.startxref %PDF文件解析的起点97.621998.%%EOF第一步:首先是先看文件末尾,找到它的“xref ”和“trailer ”部分。
Xref 表示对象索引,其实就是对象在文件中的偏移地址(PDF源文件中是以10进制表示的,你转换成16进制后,用UE或者其它工具查看即可)。
Trailer 虽然从字面上看是“尾部”的意思,但是解析却从它开始。
“startxref ”表示文件的入口地址,像java 的main() 方法。
后面紧跟着一个10 进制的数字,指向的是索引“xref ”的位置。
Trailer 里面的值分别表示:1.Size 9 :表示9 个索引值2.Root 3 0 R :根结点是3 0 obj 的引用 2 0 R :相关信息是2 0 obj 的引用4.ID :类似于Java 的Serializable 机制,给本PDF 文件生成一个独一无二的标识第二步:按照Root 3 0 R ,我们向上查找,找到了我们要找的Root 对象,里面的信息从字面上很容易理解:1./Type /Catalog :表示该对象的类型是目录2./Pages 4 0 R :表示所有的PDF 页面在一个叫4 0 R 的对象上3./Outlines 1 0 R :PDF 文件的大纲,也就是左侧展示的东西。
继续查找4 0 obj ,果然与预期所想的一样,的确是所有页面的集合,并且还有一个附加属性Count 统计页面对象的个数:/Type /Pages :表示该对象的类型是所有页面的集合/Kids […] :这是一个数组,里面装的是每个页面的具体引用。
因为这个PDF 就一页,所有只有一个“6 0 R ”。
下一步,就从这里入手。
/Count 1 :页的总数大小,因为这个PDF 就一页,所以值为1 。