参考:https://resources.infosecinstitute.com/topic/pdf-file-format-basic-structure/

PDF是一种可移植的文档格式,可用于显示包含文本,图像,多媒体元素,网页链接等内容。PDF文件格式规范链接:https://opensource.adobe.com/dc-acrobat-sdk-docs/。官方链接中仅仅文件格式的文档就有800多页,因此通读该文档并不是意见容易的事。

PDF不仅具有文本功能,还具有更多的功能:它可以包含图像和其他多媒体元素,受密码保护,执行JavaScript等。下图显示了PDF文件的基本结构:

下文中[]中的内容为上图中的结构体。

Header

[PDFHeader]这是PDF文件的第一行,它指定文档使用的所用PDF规范的版本号.此处PDF文档版本为1.7.

[PDFComment] 这部分是PDF文档的注释部分

Body

在PDF文档的正文中,有一些对象通常包括文本流,图像,其他多媒体元素等。“正文”部分用于保存所有显示给用户的文档数据。
即[PDFObj] sPdfObj[0]-sPdfObj[12]都是Body部分。

xref table

这是交叉引用表,其中包含对文档中所有对象的引用。交叉引用表的目的是允许它随机访问文件中的对象,因此我们无需阅读整个PDF文档即可找到特定的对象。每个对象由交叉​​引用表中的一个条目表示,该条目始终长20个字节。让我们来看一个例子:

xref
0 14
0000000000 65536 f
0000000017 00000 n
0000000066 00000 n
0000000122 00000 n
0000000258 00000 n
0000000367 00000 n
0000000500 00000 n
0000000831 00000 n
0000001013 00000 n
0000001197 00000 n
0000010222 00000 n
0000010294 00000 n
0000010366 00000 n
0000010431 00000 n

我们可以通过使用文本编辑器简单地打开PDF并滚动到文档底部来显示PDF文档的交叉引用表。

0 14

这行中的第一个数字0对应于对象编号,而第二列显示当前子节中的对象数量14。

0000000000 65536 f
0000000017 00000 n
0000000066 00000 n

。。。。。。

每个对象由一个条目表示,该条目长20个字节(包括CRLF)。

前10个字节是对象从PDF文档开始到该对象开始的偏移量。接下来是一个空格分隔符,其中另一个数字指定对象的世代号。之后,还有另一个空格分隔符,后跟字母“ f”或“ n”以指示对象是空闲的还是正在使用的。

Trailer

PDF Trailer 指定读取PDF文档的应用程序应如何找到交叉引用表和其他特殊对象。所有PDF阅读器都应从文件末尾开始阅读PDF。以下是一个Trailer示例:

trailer
<<
/Root 1 0 R
/Info 3 0 R
/Size 14/ID[<EA61F2FE98704E5009E01A59031F93DD><EA61F2FE98704E5009E01A59031F93DD>]>>
startxref
10750
%%EOF

PDF文档的最后一行包含“ %% EOF”文件字符串的结尾。在文件标记末尾之前,有一行带有startxref字符串的行,用于指定从文件开头到交叉引用表的偏移量。在此例中,交叉引用表从偏移量10750字节开始。在此之前是Trailer字符串,用于指定当前部分的开始。此节的内容嵌入在<<和>>字符内(这是一个接受键值对的字典)。

我们可以看到此部分定义了几个键,每个键用于一个特定的动作。此部分指定以下键:

  • / Size [integer]:指定交叉引用表中的条目数(也对更新部分中的对象进行计数)。使用的号码不应是间接引用。
  • / Prev [integer]:指定从文件开头到上一个交叉引用节的偏移量,如果有多个交叉引用节,则使用此偏移量。该数字应为交叉引用。
  • / Root [dictionary]:指定文档目录对象的引用对象,这是一个特殊对象,其中包含指向不同种类的其他特殊对象的各种指针(稍后会对此进行更多介绍)。
  • / Encrypt [dictionary]:指定文档的加密字典。
  • / Info [dictionary]:指定文档信息字典的参考对象。
  • / ID [array]:指定由两个字节的未加密字符串组成的数组,这些字符串构成文件标识符。
  • / XrefStm [integer]:指定从文件开头到解码流中的交叉引用流的偏移量。这仅存在于混合引用文件中,如果我们也想打开文档,即使应用程序不支持压缩的参考流,也可以指定该文件。

我们必须记住,如果以后再更新PDF文档,则可以修改初始结构。该更新通常在文件末尾附加其他元素。

增量更新

PDF的设计考虑了增量更新,因为我们可以在不重写整个文件的情况下将一些对象附加到PDF文件的末尾。因此,可以快速保存对PDF文档的更改。下图显示了PDF文档的新结构:

我们可以看到PDF文档仍然包含原始的标题,正文,交叉引用表和trailer。此外,PDF文档中还添加了其他正文,交叉引用和trailer部分。其他交叉引用部分将仅包含已更改,替换或删除的对象的条目。删除的对象将保留在文件中,但将带有“ f”标记。每个trailer都需要以“ %% EOF”标签终止,并应包含/ Prev条目,该条目指向上一个交叉引用部分。

在PDF版本1.4和更高版本中,我们可以在文档的目录字典中指定版本条目,以覆盖PDF标头中的默认版本。

例子

本篇文章所用示例文档下载地址:https://download.csdn.net/download/lacoucou/15709804

此文档是使用wps创建的,内容仅仅有一行 ,其二进制结构如下图:

010editor 模版解析出来的结构:

PDF文件数据类型

PDF 文件基本元素是 PDF 对象(PDF Object), PDF 对象包括直接对象(Direct Object)和间接对象(Indirect Object)。直接对象如下八种类型;间接对象,又叫 labelled object, 嵌套在关键词 n 0 obj 和 endobj 之间, 是用一种表示来标识一个 PDF 对象,通过标识来让别的 PDF 对象引用,这个标识叫做间接对象的 ID. https://lazymind.me/2017/10/pdf-structure/

直接对象类型

  1. Boolean value(布尔)

    布尔类型,值只能是 true 和 false

  2. Integer and Real number(数值)

    数值类型,包括整数和实数,与普通编程语言中的数值类型大体相同。

  3. String(字符串)

    字符串类型,包括包含在圆括号 ( ) 内的文字字符串(literal string)和包含在单尖括号 < > 内的十六进制字符串(hexadecimal string)两种。

    例:

    1. (Hello World)
    2. <9ADCF1>
  4. Name(名字?)

    名字类型,用字符组成的字符串,用 / 作为前导符号,在 PDF 文件中具有唯一性,相同的名字表示相同的对象(the same sequence of character denotes the same object)。常见用在 Dictionary 里面作 Key,用来表示对象名称。

    例:

    /Page
    /Kid
  5. Array(数组)

    数组类型,存在于方括号 [ ] 内,元素可以是除 Stream 外的所有类型。PDF 中数组只支持一维数组。

    例:

    [/Page false 17 (hello)]  该数组包含了4种类型元素
  6. Dictionary(字典)

    字典类型,包含在双尖括号 << >> 内,每两个元素为一对,第一个为 key, 第二个为 value, key 只能是 Name 类型,value 可以是任意类型,即可以嵌套为 Dictionary

    例:

    <</Page 1 0 obj
    /Filter /FlateDecode
    /Name (Hello)>>
  7. Stream(流对象)

    流对象,是用字节表示的序列,长度理论上没限制。包含在 stream 和 endstream 之间。以 CRLF或 LF 结尾,不能单独以 CR 结尾。dicionary 里的内容用来描述该 stream 的相关信息。

    dictionary
    stream......
    endstream
  8. Null object(空对象)

    空对象类型,用关键词 null 表示。

间接对象类型

使用 unique object identifier 来表示,方便其他对象引用。结构如下:

12 0 obj
........
endobj

第一行第一个 12 规定为 positive integer, 表示对象 ID; 第二个 0 表示生成号(generation number),通常为0;第三个为固定 obj 表示,以最后一行 endobj 表示结束。中间 ...... 表示内容。其他地方引用该对象时,使用如下格式,其中 R 为关键字:

12 0 R

文件结构

PDF文档由PDF文件的主体部分中包含的对象组成。PDF文档中的大多数对象都是字典。文档的每个页面都由页面对象表示,页面对象是一个字典,其中包含对页面内容的引用。页面对象连接在一起并形成页面树,该页面树在文档目录中使用间接引用进行声明。

在上图中,我们可以看到文档目录包含对页面树,大纲层次结构,文章线程,命名目的地和交互式表单的引用( page tree, outline hierarchy, article threads, named destinations and interactive form)。我们不会详细介绍每个部分的功能,但仅介绍最重要的部分,即“页面树”。

Document catalog

从上图可以看出,文档目录 是PDF文档中对象的根。我们已经说过,“PDF”尾部中的/ Root元素指定了文档目录。文档目录包含对其他定义文档内容的对象的引用。它还包含声明如何在屏幕上显示文档的信息。文档目录中的条目如下:

  • / Type:目录描述的PDF对象的类型(在我们的示例中,这是Catalog,因为这是文档目录对象)。
  • / Version:文档所依据的PDF规范的版本。
  • / Extensions:有关本文档中开发人员扩展的信息。
  • / Pages:对对象的间接引用,该对象是文档页面树的根。
  • /Dests:对对象的间接引用,该对象是指定的目标对象的根。
  • / Outlines:对大纲目录对象的间接引用,该对象是文档大纲层次结构的根。
  • / Threads:对表示文档文章线程的线程词典数组的间接引用。
  • / Metadata:对包含文档元数据的元数据流的间接引用。

其他 Catalog字典中,常用的字段一般有以下一些:

  • 字段

    类型

    Type

    name

    (必须)必须为Catalog。

    Version

    name

    (可选)PDF文件所遵循的版本号(如果比文件头指定的版本号高的话)。如果这个字段缺省或者文件头指定的版本比这里的高,那就以文件头为准。一个PDF生成程序可以通过更新这个字段的值来修改PDF文件版本号。

    Pages

    dictionary

    (必须并且必须为间接对象)当前文档的页面集合入口。

    PageLabels

    number tree

    (可选) number tree,定义了页面和页面label对应关系。

    Names

    dictionary

    (可选)文档的name字典。

    Dests

    dictionary

    (可选;必须是间接对象)name和相应目标对应关系字典。

    ViewerPreferences

    dictionary

    (可选)阅读参数配置字典,定义了文档被打开时候的行为。如果缺省,则使用阅读器自己的配置。

    PageLayout

    name

    (可选) 指定文档被打开的时候页面的布局方式。SinglePageDisplay 单页OneColumnDisplay 单列TwoColumnLeftDisplay 双列,奇数页在左TwoColumnRightDisplay 双列,奇数页在右TwoPageLeft 双页,奇数页在左TwoPageRight 双页,奇数页在右缺省值: SinglePage.

    PageMode

    name

    (可选) 当文档被打开时,指定文档怎么显示UseNone 目录和缩略图都不显示UseOutlines 显示目录UseThumbs 显示缩略图FullScreen 全屏模式,没有菜单,任何其他窗口UseOC 显示Optional content group 面板UseAttachments显示附件面板缺省值: UseNone.

    Outlines

    dictionary

    (可选;必须为间接对象)文档的目录字典

    Threads

    array

    (可选;必须为间接对象)文章线索字典组成的数组。

    OpenAction

    array or dictionary

    (可选) 指定一个区域或一个action,在文档打开的时候显示(区域)或者执行(action)。如果缺省,则会用默认缩放率显示第一页的顶部。

    AA

    dictionary

    (可选)一个附加的动作字典,在全局范围内定义了响应各种事件的action。

    URI

    dictionary

    (可选)一个URI字典包含了文档级别的URI action信息。

    AcroForm

    dictionary

    (可选)文档的交互式form (AcroForm)字典。

    Metadata

    stream

    (可选;必须是间接对象)文档包含的元数据流。

我们可以看到还有许多其他条目属于文档目录,但是这里不再对其进行描述。读者可以查看我们的资源以了解详细信息。下面显示了文档目录的示例:

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

Page tree

通过页面树可以访问文档的页面,该页面树定义了PDF文档中的所有页面。该树包含代表PDF文档页面的节点,该节点可以有两种类型:中间节点和叶节点。中间节点也称为页面树节点,而叶节点称为页面对象。

最简单的页面树结构可以由单个页面树节点组成,该节点直接引用所有页面对象(因此所有页面对象都是叶子)。

页面树中的每个节点必须具有以下条目:

  • / Type:此对象描述的PDF对象的类型(在本例中为Pages ,因为我们正在谈论页面树节点)。
  • /Parent:应该在所有页面树节点中存在,但在根目录中除外,在根目录中不得存在该条目。此项指定其父项。
  • / Kids:应该出现在除叶子以外的所有页面树节点中,并指定可从当前节点直接访问的所有子元素。
  • / Count:指定在后续页面树中作为该节点后代的叶节点的数量。

我们必须记住,页面树与PDF文档中的任何内容都不相关,例如页面或章节。

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

4 0 obj
<</Type/Page/Parent 2 0 R /MediaBox[ 0 0 595.3 841.9]/Resources 10 0 R /Contents 13 0 R >>
endobj

字段

类型

Type

name

(必须)必须是Page。

Parent

dictionary

(必须;并且只能是间接对象)当前page节点的直接父节点page tree 。

LastModified

date

(如果存在PieceInfo字段,就必须有,否则可选)记录当前页面被最后一次修改的日期和时间。

Resources

dictionary

(必须; 可继承)记录了当前page用到的所有资源。如果当前页不用任何资源,则这是个空字典。忽略所有字段则表示继承父节点的资源。

MediaBox

rectangle

(必须; 可继承)定义了要显示或打印页面的物理媒介的区域(default user space units)

CropBox

rectangle

(可选; 可继承)定义了一个可视区域,当前页被显示或打印的时候,它的内容会被这个区域裁剪。默认值就是 MediaBox。

BleedBox

rectangle

(可选) 定义了一个区域,当输出设备是个生产环境( production environment)的时候,页面显示的内容会被裁剪。默认值是 CropBox.

Contents

stream or array

(可选) 描述页面内容的流。如果这个字段缺省,则页面上什么也不会显示。这个值可以是一个流,也可以是由几个流组成的一个数组。如果是数组,实际效果相当于所有的流是按顺序连在一起的一个流,这就允许PDF生成的时候可以随时插入图片或其他资源。流之间的分割只是词汇上的一个分割,并不是逻辑上或者组织形式的切割。

Rotate

integer

(可选; 可继承) 顺时钟旋转的角度数,这个必须是90的整数倍,默认是0。

Thumb

stream

(可选)定义当前页的缩略图。

Annots

array

(可选) 和当前页面关联的注释。

Metadata

stream

(可选) 当前页包含的元数据。

在我们这个例子中,所有对象的引用关系如下图:

上图内容解释:

0bj0 代表对象编号,后边的色块为对象包含的内容。

再加一个例子:

文档只有一页,内容如下:

obj关系如下:

继续分析:参考https://blog.csdn.net/pdfMaker/article/details/573990

根据链接中描述以及上图内容,已经上边的两个例子,我们知道字体,图片等内容是在资源obj中,而文字对象是存在于contents obj中,查找了相关资料,发现文本内容的保存相对于图片等内容来说要复杂的多。

引用wike中关于text的描述

Text[edit]  //https://en.wikipedia.org/wiki/PDF

Text in PDF is represented by text elements in page content streams. A text element specifies that characters should be drawn at certain positions. The characters are specified using the encoding of a selected font resource.

文本在PDF中以页面内容字符串流中的“文本元素”呈现出来。一个文本元素指定字符应在指定位置描绘。字符用所选字体源的编码指定。 //https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%A7%BB%E6%A4%8D%E6%96%87%E6%A1%A3%E6%A0%BC%E5%BC%8F

官方文档:

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf

第五章是说文字相关的, 直接用书中的例子:

Example 5.1 illustrates the most straightforward use of a font.  示例5.1演示了字体的最直接用法。
It places the text ABC 10 inches from the bottom of the page and 4 inches from the left edge, using 12-point Helvetica. 它使用12点大小的Helvetica字体将文本ABC放置在距页面底部10英寸、距左边缘4英寸的位置。
Example 5.1
BT
/F13 12 Tf
288 720 Td
(ABC) Tj
El
The five lines of this example perform the following steps:本例中的五行执行以下步骤:
1. Begin a text object.1开始一个文本对象。
2. Set the font and font size to use, installing them as parameters in the text state. (The font resource identified by the name F13 specifics the font externally known as Helvetica.)设置要使用的字体和字号,将其作为文本状态中的参数安装。(由名称F13标识的字体资源指定了外部称为Helvetica的字体。)
3. Specify a starting position on the page, setting parameters in the text object.指定页面上的起始位置,在文本对象中设置参数。
4. Paint the glyphs for a string of characters there.在该字形上绘制一串字符
5. End the text object.结束文本对象。
The following paragraphs explain these operations in more detail.以下段落将更详细地解释这些操作。

由上边的例子我们知道,字符是放置在BT  和 ET 标签中间的。知道了这个,我们还不一定能看到相关文字,想看到相关文字还需要搞懂cMap这些标签,由于内容较多,请直接查看原文档。下边举一个例子来说明相关内容。

在wps中写入:

Hello 中文

此内容作为正文内容,未设置字体以及格式等内容,然后生成pdf,效果如下图:

下边我们首先捋一捋obj对象图:

由上图可知,文字相关信息在obj19:

而在这个图中,我们确实看到了直接介绍的BT  TJ等标签,而根据我们上边的说明,文字是保存的在TD TJ中间的,我们把这部分提取一下

BT
/DeviceRGB cs 0 0 0 scn /DeviceRGB CS 0 0 0 SCN /FXF1 209 Tf 0 Tc 0 Tw 0.05 0 0 0.05 0 0 Tm 0 Tr /RelativeColorimetric ri 1 i

/FXE1 gs 1800 14854.6 TD[(01)2.9342(02)-8.23325(03)-0.66507(03)-0.66507(04)]TJ
/FXF2 209 Tf 0.05 0 0 0.05 0 0 Tm 2347.19 14854.6 TD[(01)0.971863(02)]TJ

抛去我们看不懂的先不管,只挑熟悉的说:

/FXE1 gs 1800 14854.6 TD[(01)2.9342(02)-8.23325(03)-0.66507(03)-0.66507(04)]TJ

这里/fxe1 在上边的 obj图中可以找到

由图可见,/fxe1 就是字体相关信息了,而TD是字符相关信息,根据PDF类型信息可知

字符串是放置在()中,但是我们这里()内的却是0x1,那真实的字符在那呢,答案是在obj6中,

这里的beginbfchar 部分就是字符信息,0001对应的是48,48在ascii中就是H,正好与我们的内容对应,经过一一对比,我个括号内容连起来,确实是hello.

那"中文"这两个字符在哪呢?答案是在obj11中

4e2d就是中,6587是文。

PDF 文件格式 基本结构相关推荐

  1. pdf 文件格式及对象模型

    pdf 文件格式及对象模型 对象模型 PDF文件主要是由object组成的, 通过object的内部关系形成如下的逻辑结构 /Catalog     /Pages         /MediaBox ...

  2. PDF文件格式转换攻略:PDF格式转换图片格式

    关于PDF文件格式的转换大家有了解多少吗?就比如将PDF格式转换成图片格式,可能之前大家对于PDF件大家都有了解到,办公中我们经常遇到过.现在小编在这里教大家如何将PDF格式转换成图片格式,有兴趣的伙 ...

  3. 基于PDF文档结构的数字隐写

    前言 前面看了PDF隐写,研究了一下是怎么回事,自己想实现一下,然后在网上也没看到有相应脚本,就花时间写了一下,也趁此做下记录. PDF文档结构 PDF文件结构由四部分元素构成:文件头 文件体 交叉引 ...

  4. 如何一键完成照片转PDF文件格式?职场老师傅教你一招搞定它

    在我们处理日常工作文件的过程中,会经常遇到需要我们进行格式转换后才能进行处理的文件,这就给我们原本繁重的日常工作增添了几分烦恼,特别是在身边没有电脑的情况下,我们该如何利用手机进行PDF文件格式的转换 ...

  5. PDF文件格式转换器下载免费版

    相信不少朋友对PDF文档无法编辑这一限制感到非常烦恼,无论对于工作还是学习都是极为不便的,要将PDF文件转化为其它格式.本文推荐一款万能PDF转换器--讲述PDF to Word等转换方法. 在针对P ...

  6. html txt doc pdf区别,HTML、PDF文件格式转换工具一览doc

    HTML.PDF文件格式转换工具一览 转换HTML为TXT文件 这类软件挺多的,转换效果也有较大区别.仅名为HTML2TXT的软件就不止一个.我们就 以华语通HTML2TXT来看看其使用方法吧.虽然& ...

  7. 三种方法将Word文档转换为PDF文件格式

    如何将Word文档转换为PDF文件格式呢?大家在传输文件时,很多人喜欢使用PDF文件格式,因为它非常稳定,不会出现格式混乱的问题.但有些人可能不知道如何进行转换,今天我将介绍三种转换方法,让我们一起来 ...

  8. excel怎么转换成pdf文件格式

    excel是我们工作中比较重要的一种文件格式,它记录的都是一些数据,一旦某一个数字发生错误,那么后面也会接着错,所以该文件的安全性.稳定性非常的重要.儿提高excel文件安全性的最好方法就是将其转换成 ...

  9. C++ 如何解析PDF文件格式?

    C++ 如何解析PDF文件格式? C++ 如何解析PDF文件格式? C++ 如何解析PDF文件格式? 请问C++如何解析Adobe SanPDF文件格式? 最近项目中需要嵌入一个PDF文档格式解析的模 ...

  10. html转为exe格式文件格式,HTML、PDF文件格式转换工具一览

    这类软件挺多的,转换效果也有较大区别.仅名为HTML2TXT的软件就不止一个.我们就 以华语通HTML2TXT来看看其使用方法吧.虽然"个头"稍大了一点点(683KB),但Wind ...

最新文章

  1. 苹果设备iphone,ipad,macbook网络连接慢,开机开什么卡什么,一步解决
  2. 批量修改数据映射_Docker 数据卷操作「Docker 系列-6」
  3. java jdk中的归并排序实现
  4. 机器学习-终结者是否会出现
  5. jQuery 如何ajax
  6. HTML5 CANVAS 弹幕插件
  7. MFC实现BCB中的ProcessMessages
  8. 动态创建form传参
  9. 【Codevs3027】线段覆盖2
  10. python print_Python print()
  11. java什么是隐式说明,java中 显式和隐式是怎么回事
  12. 设置Log4j配置文件路径
  13. 39.伪造重定向ICMP数据包
  14. EXP-00091 Exporting questionable statistics
  15. openStreetMap数据分析举例-使用Qt统计城市科技指数排名
  16. 关于英语单词记忆的总结
  17. angular里的$even和$odd的应用
  18. 移动端h5头像上传、头像裁切、上传图片
  19. Win7+Ubuntu 16.04LTS 双系统安装
  20. 【转载】ADB命令使用大全

热门文章

  1. 9.2 正方形 2711
  2. HTML中使用a标签实现文本内链接—零基础自学网页制作
  3. 2021-08-24
  4. [OpenHarmony] [RK2206] 构建OpenHarmony编译器 (二)
  5. 微信开放平台基于网站应用授权登录源码(java)
  6. 2019蓝桥杯Java决赛题答案_2019第十届蓝桥杯Java题
  7. U盘启动盘制作,金士顿2GU盘量产工…
  8. 标签系列三:spring 中property解释以及property标签里面的属性
  9. webrtc QOS方法一(NACK实现)
  10. Linux系统 查看系统版本、CPU、内存、主频等信息