PDF文档格式

生成pdf

mkdir demo && cd demo
npm install pdfkit

然后创建index.js文件,内容如下:

const PDFDocument = require('pdfkit');
const fs = require('fs');

const doc = new PDFDocument({'compress':false});
doc.pipe(fs.createWriteStream('out.pdf'));
doc.info['Title'] = 'Test Document';
doc.info['Author'] = 'zhangzifan';
doc.fontSize(25).text('hello world!', 100, 100);
doc.addPage().text('fivecakes.com', 100, 180);
doc.end();

执行下列命令可生成out.pdf:

node index.js

pdf格式

用文本编辑器打开out.pdf:

%PDF-1.3
%ÿÿÿÿ
7 0 obj
<<
/Type /Page
/Parent 1 0 R
/MediaBox [0 0 612 792]
/Contents 5 0 R
/Resources 6 0 R
>>
endobj
6 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 8 0 R
>>
>>
endobj
5 0 obj
<<
/Length 125
>>
stream
1 0 0 -1 0 792 cm
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 100 674.05 Tm
/F1 25 Tf
[<68656c6c6f2077> 10 <6f72> -15 <6c6421> 0] TJ
ET
Q

endstream
endobj
11 0 obj
<<
/Type /Page
/Parent 1 0 R
/MediaBox [0 0 612 792]
/Contents 9 0 R
/Resources 10 0 R
>>
endobj
10 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 8 0 R
>>
>>
endobj
9 0 obj
<<
/Length 132
>>
stream
1 0 0 -1 0 792 cm
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 100 594.05 Tm
/F1 25 Tf
[<666976> 25 <6563616b> 20 <6573> 15 <2e636f6d> 0] TJ
ET
Q

endstream
endobj
13 0 obj
(PDFKit)
endobj
14 0 obj
(PDFKit)
endobj
15 0 obj
(D:20210628082421Z)
endobj
16 0 obj
(Test Document)
endobj
17 0 obj
(zhangzifan)
endobj
12 0 obj
<<
/Producer 13 0 R
/Creator 14 0 R
/CreationDate 15 0 R
/Title 16 0 R
/Author 17 0 R
>>
endobj
8 0 obj
<<
/Type /Font
/BaseFont /Helvetica
/Subtype /Type1
/Encoding /WinAnsiEncoding
>>
endobj
4 0 obj
<<
>>
endobj
3 0 obj
<<
/Type /Catalog
/Pages 1 0 R
/Names 2 0 R
>>
endobj
1 0 obj
<<
/Type /Pages
/Count 2
/Kids [7 0 R 11 0 R]
>>
endobj
2 0 obj
<<
/Dests <<
  /Names [
]
>>
>>
endobj
xref
0 18
0000000000 65535 f 
0000001195 00000 n 
0000001259 00000 n 
0000001133 00000 n 
0000001112 00000 n 
0000000208 00000 n 
0000000119 00000 n 
0000000015 00000 n 
0000001015 00000 n 
0000000580 00000 n 
0000000490 00000 n 
0000000384 00000 n 
0000000910 00000 n 
0000000763 00000 n 
0000000788 00000 n 
0000000813 00000 n 
0000000849 00000 n 
0000000881 00000 n 
trailer
<<
/Size 18
/Root 3 0 R
/Info 12 0 R
/ID [<96ff39d83752608197aa661f6a13aaf9> <96ff39d83752608197aa661f6a13aaf9>]
>>
startxref
1306
%%EOF

pdf由以下几个部分组成:

image

header部分只有2行,主要指定的pdf的版本。

obj是pdf的主要内容,每个obj的第一个数字是它的编号,obj可以不必按照顺序排列,阅读器通过xref table来定位每个obj的位置。

xref给出每个对象相对pdf尾部的偏移量,这样当pdf文件很大时,阅读器不必读取整个pdf就能快速显示内容。

trailer是整个pdf的入口,之所以这样说是因为,xref只是提供了obj的偏移,但是并没有提供这些obj之间的逻辑关系。这些obj的逻辑关系也存储在一个obj中,trailer中的/Root记录了这个obj的编号,所以我们可以从这个obj开始慢慢展开pdf的内容。另外trailer中还要/Info记录了作者、标题等其他信息,startxref记录了xref相对尾部的偏移值。

阅读器在读取pdf文件时会先读取尾部的Trailer,然后查找/Root所标记的对象:

3 0 obj
<<
/Type /Catalog
/Pages 1 0 R
/Names 2 0 R
>>
endobj

这个对象中/Pages指向一个存放所有页面的obj,/Outlines会指向存放文章大纲的obj。

来看一下Pages对象:

1 0 obj
<<
/Type /Pages
/Count 2
/Kids [7 0 R 11 0 R]
>>
endobj

其中/Kids属性列举出了所有页面对象,这里是按照顺序排列的。想要交换两个页面的顺序,只需改以下这里就可以了。

Page

来看一下这两个页面obj:

7 0 obj
<<
/Type /Page
/Parent 1 0 R
/MediaBox [0 0 612 792]
/Contents 5 0 R
/Resources 6 0 R
>>
11 0 obj
<<
/Type /Page
/Parent 1 0 R
/MediaBox [0 0 612 792]
/Contents 9 0 R
/Resources 10 0 R
>>
endobj

Contents对象里面是stream,stream里面的内容一般是经过编码以便减少体积,但是我们生成示例的时候设置不编码,方便我们研究:

5 0 obj
<<
/Length 125
>>
stream
1 0 0 -1 0 792 cm
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 100 674.05 Tm
/F1 25 Tf
[<68656c6c6f2077> 10 <6f72> -15 <6c6421> 0] TJ
ET
Q

endstream
endobj
9 0 obj
<<
/Length 132
>>
stream
1 0 0 -1 0 792 cm
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 100 594.05 Tm
/F1 25 Tf
[<666976> 25 <6563616b> 20 <6573> 15 <2e636f6d> 0] TJ
ET
Q

endstream
endobj

stream里面是pdf的图形与文字描述符,可以理解成类似html的标记语言,如果你感兴趣可以在pdf标准的第8章Graphics和第9章Text了解他们的用法,这种描述语言能够保证内容在任何机器上都不变形。

附录-字体历史

电脑字体的数据格式可以分为三大类:点阵字体、轮廓字体和笔画字体。日常所说的字体是轮廓字体。

很早很早以前,那时候还没有PDF,排版是个问题,在某台机器上排版之后,换到另一台机器打印时,排版就会乱掉。为了解决这个问题,Adobe在1984年推出PostScript,同时支持两种字体格式:Type 1 和 Type 3,它们都采用三次贝塞尔曲线,把字体的尺寸 (metrics) 信息和字形 (glyph) 信息分别存储。Type 1 专利许可费十分昂贵,穷人们只好用免费的 Type 3。

为了打破Adobe的垄断,苹果在1991年发布了TrueType,它采用二次贝塞尔曲线。二次曲线处理起来比三次曲线快,所以TrueType逐渐占据垄断地位,而Type 1从贵族堕落为平民。

为了打破苹果的垄断,1996 年微软和 Adobe 联合发布了 OpenType,它可以被认为是 Type 1 和 TrueType 的超集,既可使用二次曲线,也可使用三次曲线。它比起TrueType的优势还有:平台独立、开放、易于开发,并且支持更多的语言。

TrueType 和 OpenType 将字体数据都存在一个文件里,它们的文件 后缀分别是是.ttf 和.otf。

这些字体格式按照技术先进性,从高到低依次为:OpenType, TrueType, Type 1, Type 3。

再后来PDF代替了PostScript,并同时支持这四种字体。


pdf生成:https://github.com/foliojs/pdfkit
pdf浏览:https://github.com/mozilla/pdf.js
pdf标准:Document management — Portable document format — Part 1: PDF 1.7
字体解析库:https://github.com/photopea/Typr.js

posted @ 2021-06-28 18:20:03
评论加载中...
发表评论