PDF文件初探

1,105 阅读6分钟

编码是隐匿在计算机软硬件背后的语言,是计算机的灵魂。

1.引言

简单地介绍完香农的信息论之后,接下来笔者将介绍一些更加贴近大家生活实际的内容。

随着计算机网络、社交媒体的快速发展,多媒体数据的生成、处理和获取变得越来越方便,多媒体数据量呈现出爆炸性增长。笔者这里将我们从最常见的PDF文件讲起,介绍PDF文件内部的编码格式。

上节说到,文件的封装方案同样非常重要。而关于PDF文件的语法解析和渲染,有一个著名的项目PDFium(由Google和Foxit合作在2014年开源)。PDFium是一款基于C/C++ 编写的PDF渲染引擎。

应该说,Google这几年开源了大量优秀的项目,笔者了解到常见的的有TensorFlow(机器学习系统)、RPC 框架 GRPC、WebRTC(音视频通话)。如果有空的话,笔者后面将会给大家分享一下音视频编解码、实时通信的相关内容。

2.PDFium项目简介

PDFium项目地址:github.com/PDFium/PDFi…
感兴趣的家人们可以参考链接里的说明文档,自行编译或者编写一个解析渲染PDF的引擎。

项目整体: 2.png

关于如何使用PDFium库,在samples文件夹中,有一个pdfium_test.cc文件(入口文件),核心函数RenderPdf:

  1. 加载文档 FPDF_LoadCustomDocument
  2. 填表功能 FPDFDOC_InitFormFillEnviroument
  3. 加载页面 FPDF_LoadPage
  4. 加载文本 FPDFText_LoadPage
  5. 页面渲染FPDF_RenderPageBitmap
  6. 表单渲染:FPDF_FFLDraw
  7. 释放资源

3.PDF文件封装格式

针对PDF文件的解析和渲染,主要是参考PDF的文件格式。

由Adobe Systems Incorporated开发的PDF(便携式文档格式)被Adobe描述为一种通用的文档表示语言。PDF代表格式化的,面向页面的文档。这些文档可以是结构化的或简单的。它们可能包含文本图像图形和其他多媒体内容,例如视频音频。支持注释,元数据,超文本链接,电子签名和书签。更高版本提供了其他功能,例如,将地理空间信息嵌入到代表地图或其他地理空间图像(例如卫星照片)的文档中。

也可以了解一下PDF扩展标准:

  • PDF/A(基于PDF 1.4及以上):针对长期保存的电子文档,所有字体必须嵌入PDF,不允许嵌入音频、视频、Javascript等信息,不允许加密;
  • PDF/X(基于PDF 1.4及以上):针对图像打印制定的标准,所有字体必须嵌入;
  • PDF/E(基于PDF 1.6及以上):针对工程文件(建筑图纸、工程制图、地图等)的标准,支持动画和3D图像的绘制.

PDF文件格式的版本目前仍然在迭代中,PDF1.7的标准可以参考ghostscript.com/~robin/pdf_….

这里也推荐一本叫做《PDF Explained》的书,在线的中译本:zxyle.github.io/PDF-Explain….

3.1 PDF文件的基本组成

一个PDF文件一般来说可以分4个部分:

3.png

其中,交叉引用表是为了能对间接对象进行随机存取而设立的一个间接对象的地址索引表,列出文件中每个对象的位置便于随机访问。需要注意的是,交叉引用表是可选的(Optional)。
文件尾(trailer)声明了交叉引用表的地址即指明了文件体的根对象(Catalog),从而能够找到PDF文件中各个对象体的位置,同时还保存了PDF文件的加密等安全信息。

3.2 基本PDF语法

PDF文件至少包含以下三种不同的语言:

  1. 文档内容(document content):相互之间有链接的多个对象,形成了有向图。这些对象描述了文档的结构(包括页面、元数据、字体和资源);
  2. 页面内容(page content):描述了如何使用一系列操作符将文本和图像放到页面上
  3. 文件结构:包括Header(文件头),trailer(文件尾)和交叉引用表(xref table),可以帮助程序找到并读取文件的内容,文件结构即上图中浅黄色的部分.

HelloWorld.pdf文件示例(附带注释)如下:

%PDF-1.4     %Header
1 0 obj      %Body(含有多个对象)
<< 
/Kids [2 0 R]
/Count 1
/Type /Pages
>>
endobj
2 0 obj     %对象2 世代号为0
<<
25
/Rotate 0
/Parent 1 0 R
/Resources 3 0 R
/MediaBox [0 0 612 792]
/Contents [4 0 R]
/Type /Page
>>
endobj
3 0 obj  %对象3(以此类推)
<<
/Font
<<
/F0
<<
/BaseFont /Times-Italic
/Subtype /Type1
/Type /Font
>>
>>
>>
endobj
4 0 obj
<<
/Length 65
>>
stream
1. 0. 0. 1. 50. 700. cm
BT
 /F0 36. Tf
 (Hello, World!) Tj
ET
endstream
endobj
5 0 obj
<<
/Pages 1 0 R
/Type /Catalog
>>
endobj
xref            %交叉引用表
0 6
0000000000 65535 f %特别条目
0000000015 00000 n %对象1的字节偏移量为15(以此类推)
0000000074 00000 n
0000000192 00000 n
0000000291 00000 n
0000000409 00000 n
trailer         %文件尾
<<
/Root 5 0 R     %Root关键字
/Size 6
>>
startxref
459            %交叉引用表的字节偏移量
%%EOF

3.3 解析PDF文件

读取整个PDF文件的流程如下:

  1. 从文件开头读取header,确认这PDF文档并检索其版本号;
  2. 通过从末尾向后搜索找到文件结束标记 文件。读取trailer字典,以及开头的字节偏移量检索交叉引用表
  3. 读取交叉引用表。了解每个对象在文件哪里。
  4. 读取和解析所有对象
  5. 提取页面,解析图形内容,提取元数据等,存在许多可能的复杂性(加密,线性化,对象和交叉引用流)

具体的解析流程:

  1. 从trailer中找到Root关键字,Root是指向Catalog字典,Catalog是一个PDF文件的总入口,它包含Page tree,Outline hierarchy等。
  2. 从Catalog中找到Pages关键字,Pages是PDF所有页面的总入口,即Page Tree Root。
  3. 从Pages中找到Kids和Count关键字,Kids中包含Page子节点,Count列出该文档的总页数
  4. 从Page字典中获取MediaBox、Contents、Resources等信息,MediaBox包含页面宽高信息,Contents包含页面内容,Resources包含页面所需要的资源信息
  5. 从Contents指向的内容流中获取页面内容。

读者可以对照解析流程阅读上面的代码,当然,如果能够自行编写代码更好!

4 总结

PDF文件封装了文本、图像等多种内容,同时由于这种文件格式(封装方案)与操作系统平台无关,故其成为在互联网上进行电子文档发行和数字化信息传播的理想文档格式。 后面的话,我将针对PDF文件的格式进行更进一步的分析。