本文是对PDF Explained(by John Whitington)第二章《Building a Simple PDF》的摘要式翻译。
本章我们将使用文本编辑器手动构建PDF内容。然后我们将使用 pdftk将其转换为有效的PDF文件,并在PDF查看器中进行查看。
pdftk是一个开源的命令行程序,它的功能有:
PDF文件至少包含三种不同的语言:
文档内容由以下元素构成的对象组成:
例如,这是一个页面对象,它是一个包含许多元素的字典。
<< /Type /Page
/MediaBox [0 0 612 792]
/Resources 3 0 R
/Parent 1 0 R
/Contents [4 0 R]
>>
这个字典包含五个条目:
页面内容是一个运算符列表,每个运算符前面都有零个或多个 操作数。下例是一系列操作符,用于在当前位置放置文本,同时指定字体为/F0,字号36。
/F0 36.0 Tf
(Hello, World!) Tj
Tf和Tj是操作符,/F0, 36.0以及(Hello, World!)是操作数。你会发现在页面内容和文档内容中有些元素的语法是一致的。
文件结构包括:
除了上文所述的的文件结构,一个最简的PDF文档还必须包一些基本部分:
我们将PDF数据输入到文本文件中。 我们会跳过一些难以手动填充的信息,依靠pdftk来填充它。我们会:
文件头通常由两行组成。第一行将文件标识为PDF并给出版本号:
%PDF-1.1 //PDF version 1.1 header
第二行很难输入文本编辑器,因为它包含不可打印的字符。 我们将它留给pdftk处理。
下面来看页面的主体–对象。首先是页面列表,它是一个字典,链接了文档中的所有页面对象。
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
接下来是页面,它同样是一个字典。它包含纸张大小,以及对页面列表,图形内容和资源的间接引用。
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
只有一个资源条目,字体字典,它包含了一种字体,我们将用它在页面上写一些文本。
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条目写入流字典。
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
上面的图形操作流在页面呈现的结果如下
文件的最后一部分由文档目录开始,它是对象图(译者注:参看“文档结构”小节中的图示)的根对象。
接下来是交叉引用表,它给出了每个对象在文件中的字节偏移量。 我们让pdftk来填写此内容。
最后两行:一行给出交叉引用表起始位置的字节偏移量(我们写0让pdftk来计算它)。最后是文件结束标记%%EOF。
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 文件
%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:
pdftk hello-broken.pdf output hello.pdf
pdftk读取文件及其对象,修补错误同时将缺失数据补全。生成的合法文件如示例2-2所示。
%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
说明: