前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PDF Explained(翻译)第二章 构建一个简单的PDF

PDF Explained(翻译)第二章 构建一个简单的PDF

作者头像
跑马溜溜的球
发布2021-07-14 14:59:13
1.3K0
发布2021-07-14 14:59:13
举报
文章被收录于专栏:日积月累1024日积月累1024

本文是对PDF Explained(by John Whitington)第二章《Building a Simple PDF》的摘要式翻译。

本章我们将使用文本编辑器手动构建PDF内容。然后我们将使用 pdftk将其转换为有效的PDF文件,并在PDF查看器中进行查看。

关于PDFTK(THE PDF TOOLKIT)

pdftk是一个开源的命令行程序,它的功能有:

  • 合并分割PDF文档
  • 旋转PDF页面
  • 加解密
  • 填充PDF表单
  • 加水印和图章
  • 打印和修改PDF元数据(metadata)
  • 添加附加

基本的PDF语法

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

  • document content(文档内容),是由众多对象连接而成的有向图。这些对象描述了文档的结构(页面,元数据,字体和资源)。
  • page content(页面内容),描述了一系列操作符,用于将文本和图形放在单一页面上。
  • file structure(文件结构),包括header(文件头),trailer(文件尾)和交叉引用表,帮助程序定位并读取文件内容。

文档内容(Document Content)

文档内容由以下元素构成的对象组成:

  • 名称, 写作 /Name
  • 整数,如 50
  • 带括号的字符串,如 (The Quick Brown Fox)
  • 对其他对象的引用,如 2 0 R,表示对对象2的引用。
  • 对象数组(有序集合),如 [50 30 /Fred],是一个包含三个元素的数组,顺序为:50, 30 和 /Fred。
  • 字典(Dictionaries)(从名称到对象的无序映射),如 <</Three 3 /Five 5>>,将/Three映射为3,将/Five映射为5。
  • 流(stream),它由字典和一些二进制数据组成。这些用于存储PDF图形运算符的流,以及其他二进制数据,如图像和字体。

例如,这是一个页面对象,它是一个包含许多元素的字典。

代码语言:javascript
复制
<< /Type /Page
   /MediaBox [0 0 612 792]
   /Resources 3 0 R
   /Parent 1 0 R
   /Contents [4 0 R]
>>

这个字典包含五个条目:

  • /Type /Page:/Page与字典中的key /Type相关联。
  • /MediaBox [0 0 612 792]:由4个整数组成的数组[0 0 612 792]与字典中的key /MediaBox相关联。
  • /Resources 3 0 R:3号对象与字典中的key /Resources相关联。
  • /Parent 1 0 R:1号对象与字典key /Parent相关联。
  • /Contents [4 0 R]:只包含一个元素的间接引用数组[4 0 R] 字典key /Contents 相关联。

页面内容(Page Content)

页面内容是一个运算符列表,每个运算符前面都有零个或多个 操作数。下例是一系列操作符,用于在当前位置放置文本,同时指定字体为/F0,字号36。

代码语言:javascript
复制
/F0 36.0 Tf
(Hello, World!) Tj

Tf和Tj是操作符,/F0, 36.0以及(Hello, World!)是操作数。你会发现在页面内容和文档内容中有些元素的语法是一致的。

文件结构(File Structure)

文件结构包括:

  • 文件头(header):用于将文件标记为PDF文档。
  • 交叉引用表: 列出了每个对象在文档中的字节偏移量–这 允许随机访问任意对象,而不必顺序读取。
  • 文件尾(trailer): 包括交叉引用表的字节偏移,后面跟着文件结束标记。

文档结构(Document Structure)

除了上文所述的的文件结构,一个最简的PDF文档还必须包一些基本部分:

  • 尾部字典(trailer dictionary): 提供信息,以方便读取文件中的其它对象。
  • 文档目录(document catalog): 对象图的根节点。
  • 页面树:包含了文档中的所有页面。
  • 至少一个页面。页面中包括:资源(比如字体),页面内容(用于绘制文本和图形的指令)
image
image

构建元素

我们将PDF数据输入到文本文件中。 我们会跳过一些难以手动填充的信息,依靠pdftk来填充它。我们会:

  • 使用简短的header。
  • 忽略页面内容流的长度
  • 省略几乎所有的交叉引用表
  • 使用0表示交叉引用表的字节偏移量,以避免必须计数它手动。

文件头

文件头通常由两行组成。第一行将文件标识为PDF并给出版本号:

代码语言:javascript
复制
%PDF-1.1 //PDF version 1.1 header

第二行很难输入文本编辑器,因为它包含不可打印的字符。 我们将它留给pdftk处理。

主要对象

下面来看页面的主体–对象。首先是页面列表,它是一个字典,链接了文档中的所有页面对象。

代码语言:javascript
复制
1 0 obj //Object 1 
<< /Type /Pages //It's a page list
   /Count 1 //There is one page
   /Kids [2 0 R] //页面对象编号列表。这里只有2号对象
>>
endobj //End of object 1

接下来是页面,它同样是一个字典。它包含纸张大小,以及对页面列表,图形内容和资源的间接引用。

代码语言:javascript
复制
2 0 obj
<< /Type /Page //It's a page
 /MediaBox [0 0 612 792] //Paper size is US Letter Portrait (612 points by 792 points)
 /Resources 3 0 R //Reference to resources at object 3
 /Parent 1 0 R //指向页面列表(父节点)
 /Contents [4 0 R] //Graphical content is in object 4
>>
endobj

只有一个资源条目,字体字典,它包含了一种字体,我们将用它在页面上写一些文本。

代码语言:javascript
复制
3 0 obj
<< /Font //The font dictionary
     << /F0 //Just one font, called /F0
          << /Type /Font //这三行引用了内建字体Times Italic
             /BaseFont /Times-Italic
             /Subtype /Type1 >>
     >>
>>
endobj

图形内容

页面内容流包括了一系列操作符,用于在页面上放置文本和操作符。它们被链接到了页面字典中的 /Contents条目。

流对象由一个字典和其后的原始数据流组成,包含了一系列操作答和操作数。通常这些内容会被压缩以减少文件大小,但我们是手动输入的,不去压缩它。我们还需要指明流的长度(字节为单位)–pdftk会将所需的/Length条目写入流字典。

代码语言:javascript
复制
4 0 obj //The page content stream
<< >>
stream //Beginning of stream
1. 0. 0. 1. 50. 700. cm //Position at (50, 700)
BT  //Begin text block
  /F0 36. Tf //Select /F0 font at 36pt
  (Hello, World!) Tj //Place the text string
ET //End text block
endstream //End of stream
endobj

上面的图形操作流在页面呈现的结果如下

image
image

目录,交叉引用表和文件尾(Trailer)

文件的最后一部分由文档目录开始,它是对象图(译者注:参看“文档结构”小节中的图示)的根对象。

接下来是交叉引用表,它给出了每个对象在文件中的字节偏移量。 我们让pdftk来填写此内容。

最后两行:一行给出交叉引用表起始位置的字节偏移量(我们写0让pdftk来计算它)。最后是文件结束标记%%EOF。

代码语言:javascript
复制
5 0 obj
<< /Type /Catalog //The document catalog
   /Pages 1 0 R //Reference to the page list
>>
endobj

xref //交叉引用表的开头,我们可以略过这部分
0 6
trailer
<< /Size 6 //交叉引用表的行数(对象个数加1)
   /Root 5 0 R //Reference to the document catalog
>>
startxref
0 //交叉引用表起始位置的字节偏移量, 我们设为0
%%EOF //End of file marker

合成

源文件可以在此在线资源中找到。你也可以自己输入,将其保存为hello-broken.pdf。

例2-1: 适合手动创建的无效 hello-broken.pdf PDF 文件

代码语言:javascript
复制
%PDF-1.1 File header
1 0 obj Main objects 
<< /Type /Pages
   /Count 1
   /Kids [2 0 R]
>>
endobj
2 0 obj
<< /Type /Page 
   /MediaBox [0 0 612 792] 
   /Resources 3 0 R
   /Parent 1 0 R
   /Contents [4 0 R]
>>
endobj
3 0 obj
<< /Font
     << /F0
          << /Type /Font
             /BaseFont /Times-Italic
             /Subtype /Type1 >>
     >>
>>
endobj
4 0 obj Graphical content
<< >>
stream 
1. 0. 0. 1. 50. 700. cm 
BT
  /F0 36. Tf 
  (Hello, World!) Tj 
ET 
endstream 
endobj
5 0 obj Catalog, cross-reference table, and trailer
<< /Type /Catalog
   /Pages 1 0 R 
>>
endobj
xref
0 6
trailer
<< /Size 6 
   /Root 5 0 R
>>startxref
0 
%%EOF

我们可以使用pdftk来修复hello-broken.pdf文件,将输出写入hello.pdf:

代码语言:javascript
复制
pdftk hello-broken.pdf output hello.pdf

pdftk读取文件及其对象,修补错误同时将缺失数据补全。生成的合法文件如示例2-2所示。

代码语言:javascript
复制
%PDF-1.1
%âãÏÓ //说明1
1 0 obj 
<<
/Kids [2 0 R]
/Count 1
/Type /Pages
>>
endobj 
2 0 obj 
<<
/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 
<<
/Font 
<<
/F0 
<<
/BaseFont /Times-Italic
/Subtype /Type1
/Type /Font
>>
>>
>>
endobj 
4 0 obj 
<<
/Length 65 //说明2
>>
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 //说明3
0000000000 65535 f 
0000000015 00000 n 
0000000074 00000 n 
0000000192 00000 n 
0000000291 00000 n 
0000000409 00000 n 
trailer

<<
/Root 5 0 R
/Size 6
>>
startxref
459 //说明4
%%EOF

说明:

  • 1: PDF头中添加了一些不可打印的字符–这可以确保 文件被传输程序(比如FTP等)识别为二进制文件(而不是文本)。
  • 2: 写入了流的字节长度。
  • 3: 交叉引用表已填入了每个对象的字节偏移量。
  • 4: 写入了交叉引用表起始位置的字节偏移量。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-07-09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 关于PDFTK(THE PDF TOOLKIT)
  • 基本的PDF语法
    • 文档内容(Document Content)
      • 页面内容(Page Content)
        • 文件结构(File Structure)
          • 文件头
          • 主要对象
          • 图形内容
          • 目录,交叉引用表和文件尾(Trailer)
      • 文档结构(Document Structure)
      • 构建元素
      • 合成
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档