什么是最小可能的有效PDF?

188

出于简单的好奇心,看到了最小的GIF,那么最小的可能的有效PDF文件是什么?


取决于你的创建方式。很有可能你自己在编辑器中编写的代码比应用程序生成的代码更小。 - devnull
1
尝试将“showpage”(不带引号)输入到Ghostscript或ps2pdf中。 - devnull
10个回答

251

这是一个有趣的问题。按照书本上的方法,您可以从以下内容开始:

%PDF-1.0
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj
xref
0 4
0000000000 65535 f
0000000010 00000 n
0000000053 00000 n
0000000102 00000 n
trailer<</Size 4/Root 1 0 R>>
startxref
149
%EOF

这是一篇291字节的PDF文件,使用Acrobat打开它时会有一些投诉。其中只有一页内容,尺寸为3/72英寸,这是规范允许的最小尺寸。

不过,Acrobat X甚至不再需要交叉引用表,所以我们可以将其移除:

%PDF-1.0
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj
trailer<</Size 4/Root 1 0 R>>

Acrobat抱怨,但是打开了它。现在我们有178个字节。 事实证明,在尾部不需要那个/Size。现在我们只有172个字节:

%PDF-1.0
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj
trailer<</Root 1 0 R>>

原来在字典中,您不需要所有那些烦人的/Type元素:
%PDF-1.0
1 0 obj<</Pages 2 0 R>>endobj 2 0 obj<</Kids[3 0 R]/Count 1>>endobj 3 0 obj<</MediaBox[0 0 3 3]>>endobj
trailer<</Root 1 0 R>>

现在我们只有138个字节。

同时,事实证明当规范中说“应该是间接引用”和/ Count是必需的,并且标题“必须”为% PDF-1.0时,它们只是一些宽泛的建议。这是我能够做到的最小值,并且可以在Acrobat X中打开:

%PDF-1.
trailer<</Root<</Pages<</Kids[<</MediaBox[0 0 3 3]>>]>>>>>>

70字节。

现在,我的编辑器使用Windows的换行规则,但Acrobat接受Windows、Mac或Unix的约定。因此,通过使用十六进制编辑器,我用\r替换了\r\n,并完全删除了最后一个换行符,这使我只剩下67个字节。

25 50 44 46 2D 31 2E 0D 74 72 61 69 6C 65 72 3C 
3C 2F 52 6F 6F 74 3C 3C 2F 50 61 67 65 73 3C 3C 
2F 4B 69 64 73 5B 3C 3C 2F 4D 65 64 69 61 42 6F 
78 5B 30 20 30 20 33 20 33 5D 3E 3E 5D 3E 3E 3E 
3E 3E 3E 

我尝试取消最后一个字典(>>),但Acrobat不支持。内建于Google Chrome (FoxIt)的PDF阅读器无法打开它。
作为一种PostScript,如果您同意Acrobat“修复”文件,则会增加到3550个字节,其中大部分是可选元数据,但仍会留下一些明显的规范违规问题。

31
“当规范中说‘必须是间接引用’,并且要求有/Count,同时标题必须是%PDF-1.0时,这些都只是宽泛的建议”其实不是宽泛的建议,而是有效性的要求。即使一些PDF浏览器没有强制执行它们,不遵循这些要求也意味着无效,并且回答者要求提供一个有效的PDF文件。 - mkl
31
接受此答案的原因是它从规范允许的最低要求开始,然后超越了这个范围。非常好的答案,谢谢! :) - meshy
2
那就是规格。PDF中的对象图具有循环。 - plinth
25
我需要一个 PDF 文件的 Base64 表示。如果有人感兴趣,以下是 138 字节版本的 Base64 字符串:JVBERi0xLjAKMSAwIG9iajw8L1BhZ2VzIDIgMCBSPj5lbmRvYmogMiAwIG9iajw8L0tpZHNbMyAw\nIFJdL0NvdW50IDE+PmVuZG9iaiAzIDAgb2JqPDwvTWVkaWFCb3hbMCAwIDMgM10+PmVuZG9iagp0\ncmFpbGVyPDwvUm9vdCAxIDAgUj4+Cg== - towi
16
这是67字节版本的base64字符串:JVBERi0xLg10cmFpbGVyPDwvUm9vdDw8L1BhZ2VzPDwvS2lkc1s8PC9NZWRpYUJveFswIDAgMyAzXT4+XT4+Pj4+Pg== - MCattle
显示剩余12条评论

23

我无法打开“Hello World”示例。

对于一个相对较小的文本文件:

%PDF-1.2 
9 0 obj
<<
>>
stream
BT/ 9 Tf(Test)' ET
endstream
endobj
4 0 obj
<<
/Type /Page
/Parent 5 0 R
/Contents 9 0 R
>>
endobj
5 0 obj
<<
/Kids [4 0 R ]
/Count 1
/Type /Pages
/MediaBox [ 0 0 99 9 ]
>>
endobj
3 0 obj
<<
/Pages 5 0 R
/Type /Catalog
>>
endobj
trailer
<<
/Root 3 0 R
>>
%%EOF

3
这样是行不通的,你需要定义一个字体资源并在页面内容中选择它,才能显示文本。 - yms
2
这个文件在Mac OS X El Capitan下可以打开,而得到最高评价的PDF1.0答案则不能。 - Devy
15
也可以在 Chrome 中打开,数据:application/pdf;base64,JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyA5IFRmKFRlc3QpJyBFVAplbmRzdHJlYW0KZW5kb2JqCjQgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCA1IDAgUgovQ29udGVudHMgOSAwIFIKPj4KZW5kb2JqCjUgMCBvYmoKPDwKL0tpZHMgWzQgMCBSIF0KL0NvdW50IDEKL1R5cGUgL1BhZ2VzCi9NZWRpYUJveCBbIDAgMCA5OSA5IF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiVFVkVJTkcg== - Luke Rehmann
@yms 你有任何例子吗? - John Smith
当我尝试使用PyPDF2读取时出现以下错误:PyPDF2.errors.PdfReadError: startxref not found - Martin Thoma

17

基于这里所有的答案,这是最小的包含文本的PDF:

SMALL_PDF = (
    b"%PDF-1.2 \n"
    b"9 0 obj\n<<\n>>\nstream\nBT/ 32 Tf(  YOUR TEXT HERE   )' ET\nendstream\nendobj\n"
    b"4 0 obj\n<<\n/Type /Page\n/Parent 5 0 R\n/Contents 9 0 R\n>>\nendobj\n"
    b"5 0 obj\n<<\n/Kids [4 0 R ]\n/Count 1\n/Type /Pages\n/MediaBox [ 0 0 250 50 ]\n>>\nendobj\n"
    b"3 0 obj\n<<\n/Pages 5 0 R\n/Type /Catalog\n>>\nendobj\n"
    b"trailer\n<<\n/Root 3 0 R\n>>\n"
    b"%%EOF"
)

以base64编码。请将此复制并在Chrome中测试:

data:application/pdf;base64,JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyAzMiBUZiggIFlPVVIgVEVYVCBIRVJFICAgKScgRVQKZW5kc3RyZWFtCmVuZG9iago0IDAgb2JqCjw8Ci9UeXBlIC9QYWdlCi9QYXJlbnQgNSAwIFIKL0NvbnRlbnRzIDkgMCBSCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9LaWRzIFs0IDAgUiBdCi9Db3VudCAxCi9UeXBlIC9QYWdlcwovTWVkaWFCb3ggWyAwIDAgMjUwIDUwIF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiUlRU9G

要使页面更大,请调整MediaBox的尺寸 :)

/MediaBox [ 0 0 250 50 ]


当我尝试使用PyPDF2读取时出现以下错误:PyPDF2.errors.PdfReadError: startxref not found - Martin Thoma
我成功地在Mac上打开了base64版本:pbpaste | base64 -d > file.pdf,而且ls -lha显示了291B file.pdf,然后open file.pdf显示了一个较小的窗口。谢谢! - josephdpurcell
1
请将文本居中并适应于框内。: ) - undefined

6

最短的纯文本我发现在最近版本的Acrobat(以及几乎没有其他阅读器)被接受而不引起任何抱怨,因此“可读”和“可写”像line.pdf是没有意义的38个字节(接近Pancakes更短的36字节版本,使用了一个空值)。

%PDF-1.
trailer <</Root<</Pages<<>>>>>>

要被Chrome接受,它需要更接近76字节,但这样会被Acrobat等软件拒绝...
%PDF-1.
1 0 obj<</Pages<</Kids<<>>/Count 1>>>>endobj
trailer <</Root 1 0 R>>

我本来打算举一个我认为是最小有效的“通用”PDF的例子,直到我注意到使用PDF的整个理念就是确保它在所有设备和其PDF阅读器上都能完全相同地呈现。然而,在交叉检查我的“完美小巧格式良好的PDF”时,我发现了这个问题。简而言之,在我的个人最小文本模板中已经修复了这个问题(在末尾)。

enter image description here

所以基本规则是“最小可能的有效PDF”,但我认为这个缺陷应该被视为无效的PDF,因为它不符合“适用于目的”的概念,因此最小的PDF本身必须至少包含一种修复工作字体的方法。
为了解释我的提议解决方案以及为什么它不完美,我将以粗略的形式呈现,因为需要剪切和粘贴。
%PDF-1.0
%µ¶

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

2 0 obj
<</Kids[3 0 R]/Count 1/Type/Pages/MediaBox[0 0 595 792]>>
endobj

3 0 obj
<</Type/Page/Parent 2 0 R/Contents 4 0 R/Resources<<>>>>
endobj

4 0 obj
<</Length 58>>
stream
q
BT
/ 96 Tf
1 0 0 1 36 684 Tm
(Hello World!) Tj
ET
Q

endstream
endobj

xref
0 5
0000000000 65536 f 
0000000016 00000 n 
0000000062 00000 n 
0000000136 00000 n 
0000000209 00000 n 

trailer
<</Size 5/Root 1 0 R>>
startxref
316
%%EOF

虽然问题的规则没有明确要求,但我包含了一些用户问题的过去经验。
你可能注意到的第一个区别是第二个对象中的媒体框是一个混合的MediaBox[0 0 595 792],它是一个最小最大化的A4宽度和最小最大化的美国信纸高度,否则在大多数国家的“通用页面”会强制使用第二张纸以100%比例打印,因为页面定义对于地区默认值来说要么太宽要么太高。
而当前的问题在第三个对象中表现出来,因为资源中没有设置字体,所以在追求最小化的PDF时,我认为没有定义字体的文件将是无效的。
因此,到目前为止,包括我的答案在内,似乎都没有产生一个能够作为“有效”方式生成相同打印输出的PDF,无论平台或查看器如何。
转向图书馆,我找到了一个3MB的zip文件,其中包含一个非常多功能的windows.exe(一个可以执行大多数pdf功能的单个文件,如拆分合并导入盖章导出附件等),它可以接受命令行中的“Hello World!”并生成一个良好的工作文件,这是页面中心放大 {{link1:enter image description here}}。
它使用流来处理文本及其定位,并具有其他符合规范的数据,如生产者,因此我将其提供为一个潜在的最小化版本,注意,按照现有的呈现方式,由于从二进制到文本的流损坏,该文件将显示为空白。
%PDF-1.7
%µ¶

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

2 0 obj
<</Count 1/Kids[5 0 R]/MediaBox[0 0 595 792]/Type/Pages>>
endobj

3 0 obj
<</BaseFont/Helvetica/Encoding/WinAnsiEncoding/Subtype/Type1/Type/Font>>
endobj

4 0 obj
<</Filter/FlateDecode/Length 101>>
stream
xœ*Tp
QÐw3P04Ò30PISp
Q01
à˜kdf¢ga¬`bhâ%ç‚ô(„”#©Aîè"EéÚlA
HW‘‚†GjNN¾Bx~QNŠ¢¦BHÈÞ@@   ÿÿFå
endstream
endobj

5 0 obj
<</Contents 4 0 R/CropBox[0 0 595 792]/MediaBox[0 0 595 792]/Parent 2 0 R/Resources<</Font<</F0 3 0 R>>>>/Type/Page>>
endobj

6 0 obj
<</CreationDate(D:20220600600709+01'00')/ModDate(D:20220600600709+01'00')/Producer(me 2)>>
endobj

xref
0 7
0000000000 65536 f 
0000000016 00000 n 
0000000062 00000 n 
0000000136 00000 n 
0000000225 00000 n 
0000000395 00000 n 
0000000529 00000 n 

trailer
<</Size 7/Info 6 0 R/Root 1 0 R/ID[<A2A0CE5CCD9D0DABD5845AD574BF0A5C><09BF9D281BE12CB5B5933BB2B62B0D4D>]>>
startxref
636
%%EOF

P.S. 我故意添加了一个无效的项目,所以明显不是最小工作答案,看看你能不能找出明显的错误:-)

我的个人提供 所以我经常被问到如何编写纯文本模板化的PDF文件,因此需要字体保持静态(Helvetica或Courier应该可以),并且使用Windows命令行易于修改的结构,所以这符合我的目的,现在它只有698字节,显示了两个占位符以显示多行,所以如果需要,可以查找并替换HelveticaCourier(注意故意2个空格之后以保持字节计数)

%PDF-1.1
%âã
1 0 obj
<</Type/Catalog/Pages<</Type/Pages/Count 1/Kids[2 0 R]>>>>
endobj
2 0 obj
<</Type/Page/Parent 1 0 R/MediaBox[0 0 594 792]/Resources<</Font<</F1 3 0 R>>/ProcSet[/PDF/Text]>>/Contents 4 0 R>>
endobj
3 0 obj
<</Type/Font/Subtype/Type1/Name/F1/BaseFont/Helvetica>>
endobj
4 0 obj
<</Length 5 0 R>>
stream
BT
/F1 36 Tf
1 0 0 1 255 752 Tm
48 TL
( Hello)'
(World!)'
ET
endstream
endobj
5 0 obj
78
endobj
xref
0 6
0000000000 65536 f
0000000017 00000 n
0000000094 00000 n
0000000228 00000 n
0000000302 00000 n
0000000425 00000 n
trailer
<</Size 6/Info <</CreationDate(D:2023)/Producer(cmd2pdf)/Title(mini.pdf)>>/Root 1 0 R>>
startxref
446
%%EOF

要了解这种方法在Windows命令行中的工作原理,请右键单击并下载为文本https://github.com/GitHubRulesOK/MyNotes/raw/master/MAKE-PDF.cmd(现在有200行!)注意:浏览器安全性可能会要求您信任cmd作为下载,因此请使用.txt扩展名,并且一旦您确信它不会造成任何损害,仍然需要更改属性以解除阻止。

@mkl你准备好了吗?


6

我想制作一个最小的PDF文件,显示“Hello World”。文本位于左下角。很抱歉字体只有9点,更大的字体会多占用一个字节 :)

如果使用换行符和无尾随换行符或空字节保存,则Adobe Reader X需要172字节:

%PDF-1.
1 0 obj<</Kids[<</Parent 1 0 R/Resources<<>>/Contents 2 0 R>>]>>endobj 2 0 obj<<>>stream
BT/ 9 Tf(Hello World)' ET
endstream
endobj trailer<</Root<</Pages 1 0 R>>>>

Chrome内置的PDF阅读器需要120字节:

%PDF 1 0 obj<</Pages<</Kids[<</Contents<<>>stream
BT 9 Tf(Hello World)' ET endstream>>]>>>>endobj trailer<</Root 1 0 R>>

要在Chrome中轻松查看此内容,请复制以下URI到地址栏(SO不允许我链接它,并且在其他浏览器中它根本无法工作):

data:application/pdf,%25PDF%201%200%20obj%3C%3C%2FPages%3C%3C%2FKids%5B%3C%3C%2FContents%3C%3C%3E%3Estream%0ABT%209%20Tf(Hello%20World)'%20ET%20endstream%3E%3E%5D%3E%3E%3E%3Eendobj%20trailer%3C%3C%2FRoot%201%200%20R%3E%3E

2
相当简短。;) 根据规范,不合法。 - mkl
17
对我来说,在Chrome中无法打开。 - Luke Rehmann

2
根据Ange Albertini lecture,最小的有效PDF文件大小为36字节:

%PDF-(NULL)trailer<</Root<</Pages<<>>>>>>

其中(NULL)是不可打印的ASCII 0字符。
然而,正如Ange所指出的那样,虽然这个PDF在技术上是有效的,但大多数PDF阅读器应用程序会基于其大小将其视为无效,从而无法打开它。

2
根据规范(比讲座更重要),它在技术上并不有效,存在多个问题,缺少交叉引用,直接对象应该是间接对象等。 - mkl

0

0
在一家与PDF相关的公司工作,我知道以下内容将非常有效。这是一个有效的空白A4页面:

%PDF-1.4
%âãÏÓ
5 0 obj
<<
/Length 1
>>
stream
 
endstream
endobj
4 0 obj
<<
/Type /Page
/MediaBox [0 0 612 792]
/Resources <<
>>
/Contents 5 0 R
/Parent 2 0 R
>>
endobj
2 0 obj
<<
/Type /Pages
/Kids [4 0 R]
/Count 1
>>
endobj
1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj
3 0 obj
<<
/Creator (PDF Creator http://www.pdf-tools.com)
/CreationDate (D:20150701112447+02'00')
/ModDate (D:20220607183602+02'00')
/Producer (3-Heights\222 PDF Optimization Shell 6.0.0.0 \(http://www.pdf-tools.com\))
>>
endobj
xref
0 6
0000000000 65535 f
0000000226 00000 n
0000000169 00000 n
0000000275 00000 n
0000000065 00000 n
0000000015 00000 n
trailer
<<
/Size 6
/Root 1 0 R
/Info 3 0 R
/ID [<1C3500CA9F7232B97E0EF3F789E8B7F2> <254C8D153F655D49945EAD68D801E011>]
>>
startxref
505
%%EOF

如今使用Javascript,你可以将此嵌入到你的js bundle中。首先将上述内容进行base64编码,然后使用编码字符串并通过以下方式创建Blob文件:

const str = 'JVBERi0xLjQKJcOiw6PDj8OTCjUgMCBvYmoKPDwKL0xlbmd0aCAxCj4+CnN0cmVhbQogCmVuZHN0cmVhbQplbmRvYmoKNCAwIG9iago8PAovVHlwZSAvUGFnZQovTWVkaWFCb3ggWzAgMCA2MTIgNzkyXQovUmVzb3VyY2VzIDw8Cj4+Ci9Db250ZW50cyA1IDAgUgovUGFyZW50IDIgMCBSCj4+CmVuZG9iagoyIDAgb2JqCjw8Ci9UeXBlIC9QYWdlcwovS2lkcyBbNCAwIFJdCi9Db3VudCAxCj4+CmVuZG9iagoxIDAgb2JqCjw8Ci9UeXBlIC9DYXRhbG9nCi9QYWdlcyAyIDAgUgo+PgplbmRvYmoKMyAwIG9iago8PAovQ3JlYXRvciAoUERGIENyZWF0b3IgaHR0cDovL3d3dy5wZGYtdG9vbHMuY29tKQovQ3JlYXRpb25EYXRlIChEOjIwMTUwNzAxMTEyNDQ3KzAyJzAwJykKL01vZERhdGUgKEQ6MjAyMjA2MDcxODM2MDIrMDInMDAnKQovUHJvZHVjZXIgKDMtSGVpZ2h0c1wyMjIgUERGIE9wdGltaXphdGlvbiBTaGVsbCA2LjAuMC4wIFwoaHR0cDovL3d3dy5wZGYtdG9vbHMuY29tXCkpCj4+CmVuZG9iagp4cmVmCjAgNgowMDAwMDAwMDAwIDY1NTM1IGYKMDAwMDAwMDIyNiAwMDAwMCBuCjAwMDAwMDAxNjkgMDAwMDAgbgowMDAwMDAwMjc1IDAwMDAwIG4KMDAwMDAwMDA2NSAwMDAwMCBuCjAwMDAwMDAwMTUgMDAwMDAgbgp0cmFpbGVyCjw8Ci9TaXplIDYKL1Jvb3QgMSAwIFIKL0luZm8gMyAwIFIKL0lEIFs8MUMzNTAwQ0E5RjcyMzJCOTdFMEVGM0Y3ODlFOEI3RjI+IDwyNTRDOEQxNTNGNjU1RDQ5OTQ1RUFENjhEODAxRTAxMT5dCj4+CnN0YXJ0eHJlZgo1MDUKJSVFT0Y=';
const blob = new Blob([atob(str)], { type: 'application/pdf' });


0

这个问题经常在测试各种文件类型时出现,Github上有一个由mathiasbynens创建的伟大代码库 - https://github.com/mathiasbynens/small,它包含了各种最小尺寸的有效文件的存档:

这里是PDF文件的示例:

原始内容:

%PDF-1.
1 0 obj<</Pages 2 0 R>>endobj
2 0 obj<</Kids[3 0 R]/Count 1>>endobj
3 0 obj<</Parent 2 0 R>>endobj
trailer <</Root 1 0 R>>

并且以Base64编码形式:
data:application/pdf;base64,JVBERi0xLgoxIDAgb2JqPDwvUGFnZXMgMiAwIFI+PmVuZG9iagoyIDAgb2JqPDwvS2lkc1szIDAgUl0vQ291bnQgMT4+ZW5kb2JqCjMgMCBvYmo8PC9QYXJlbnQgMiAwIFI+PmVuZG9iagp0cmFpbGVyIDw8L1Jvb3QgMSAwIFI+Pg==

1
虽然可能被多个PDF阅读器接受,但这个PDF文件是无效的。(诚然,对于这个问题的多个其他答案也有同样的缺点...) - mkl

-3

在Java中,使用以下代码:

 private static String samplepdf = "255044462D312E0D747261696C65723C3C2F526F6F743C3C2F50616765733C3C2F4B6964735B3C3C2F4D65646961426F785B302030203320335D3E3E5D3E3E3E3E3E3E";

然后

byte[] bytes = hexStringToByteArray(samplepdf);

...

public byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                + Character.digit(s.charAt(i + 1), 16));
    }
    return data;
}

OP要求最小可能的有效PDF文件;根据规范,你的文件无效。 - mkl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接