如何让 pdftex 在每次运行相同的输入时都获得相同的 PDF 输出

如何让 pdftex 在每次运行相同的输入时都获得相同的 PDF 输出

有没有办法让 pdftex 输出精确的每次在相同的输入上运行时,pdf 都是相同的吗?

我们正在使用内部工具生成用于工程设计文档的 LaTeX。我们在 Linux 系统上通过 pdflatex(链接到 pdftex)运行生成的 .tex 文件来生成文档

$ pdflatex -version
pdfTeX 3.14159265-2.6-1.40.15 (TeX Live 2014)
kpathsea version 6.2.0
Copyright 2014 Peter Breitenlohner (eTeX)/Han The Thanh (pdfTeX).
There is NO warranty.  Redistribution of this software is
covered by the terms of both the pdfTeX copyright and
the Lesser GNU General Public License.
For more information about these matters, see the file
named COPYING and the pdfTeX source.
Primary author of pdfTeX: Peter Breitenlohner (eTeX)/Han The Thanh (pdfTeX).
Compiled with libpng 1.6.10; using libpng 1.6.10
Compiled with zlib 1.2.8; using zlib 1.2.8
Compiled with xpdf version 3.03

这有效,但每次对同一输入文件运行 pdftex 时,它都会生成略有不同的 .pdf。查看文件时,甚至通过几个在线 pdf diff 工具运行时,差异并不明显,但常规的旧 diff 表明它们是不同的,并且 sha512 哈希值也不同:

$ ls
foo.tex
$ pdflatex foo.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode

<A bunch of noise>

Output written on foo.pdf (29 pages, 167416 bytes).
Transcript written on foo.log.
$ mv foo.pdf first.foo.pdf
$ pdflatex foo.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode

<More noise>

Output written on foo.pdf (29 pages, 167416 bytes).
Transcript written on foo.log.
$ diff first.foo.pdf foo.pdf 
Binary files first.foo.pdf and foo.pdf differ
$ sha512sum -b first.foo.pdf 
cdfe293ef87e0ef6721fb749ea964bbeb88438df382302ff4b767e51e63e2831423ae960ceb0d2dc71357125efe906b349960f525d8b73a997c9bcf8e356d454 *first.foo.pdf
$ sha512sum -b foo.pdf 
81def851fbaa9aafa79ce524215916d455a5ed4d7062b639900793c3ceeea73992924462baa479f8fd655d0c3f8130a85c9cae0186b0e764c343f11d799c9b1e *foo.pdf

使用 hexdump 来比较文件之间的差异,看起来很怀疑前两个差异是日期戳:

$ hexdump -C first.foo.pdf > first.foo.pdf.hexdump
$ hexdump -C foo.pdf > foo.pdf.hexdump
$ diff foo.pdf.hexdump first.foo.pdf.hexdump 
10241c10241
< 00028000  31 38 30 37 31 31 32 33  35 31 31 37 5a 29 0a 2f  |180711235117Z)./|
---
> 00028000  31 38 30 37 31 31 32 33  35 31 30 31 5a 29 0a 2f  |180711235101Z)./|
10243c10243
< 00028020  37 31 31 32 33 35 31 31  37 5a 29 0a 2f 54 72 61  |711235117Z)./Tra|
---
> 00028020  37 31 31 32 33 35 31 30  31 5a 29 0a 2f 54 72 61  |711235101Z)./Tra|
10431,10435c10431,10435
< 00028be0  20 30 20 52 0a 2f 49 44  20 5b 3c 33 35 34 33 35  | 0 R./ID [<35435|
< 00028bf0  45 35 35 30 32 38 36 30  45 39 45 39 31 44 31 41  |E5502860E9E91D1A|
< 00028c00  38 30 32 43 45 30 34 36  36 44 39 3e 20 3c 33 35  |802CE0466D9> <35|
< 00028c10  34 33 35 45 35 35 30 32  38 36 30 45 39 45 39 31  |435E5502860E9E91|
< 00028c20  44 31 41 38 30 32 43 45  30 34 36 36 44 39 3e 5d  |D1A802CE0466D9>]|
---
> 00028be0  20 30 20 52 0a 2f 49 44  20 5b 3c 46 39 33 31 36  | 0 R./ID [<F9316|
> 00028bf0  42 39 46 42 41 36 37 37  46 41 35 32 35 36 46 39  |B9FBA677FA5256F9|
> 00028c00  37 42 43 37 33 38 45 32  38 43 34 3e 20 3c 46 39  |7BC738E28C4> <F9|
> 00028c10  33 31 36 42 39 46 42 41  36 37 37 46 41 35 32 35  |316B9FBA677FA525|
> 00028c20  36 46 39 37 42 43 37 33  38 45 32 38 43 34 3e 5d  |6F97BC738E28C4>]|

我怀疑后续的差异是校验和/哈希值,我预计它们会随着不同的时间戳而不同。

这些都不是真正的问题,只是由于超出该问题范围的原因,我们频繁重新生成 pdf 并将生成的 pdf 文件提交到 git(使用 LFS),并且由于存在差异,每次我们提交时 git 都会认为 pdf 已被更改。

pdftex 在其 -help 中间接提到将输出注释从日期更改为任意字符串,但仅限于 dvi 模式。我无论如何都试过了,但问题没有解决。

有没有办法让 pdftex 输出精确的每次在相同的输入上运行相同的 pdf 吗?或者,如果没有,有没有办法修改 pdf 以使用任意时间戳?

答案1

这是一个有趣的问题:)

pdfTeX 手册显示出对可复制 PDF 的一些担忧。

如果您希望每次运行 pdfTeX 都生成完全相同的 PDF,则需要设置两个(三个)原语。

首先是/CreationDate/ModDate。您可以使用 来设置这些值\pdfinfo

\pdfinfo{%
  /CreationDate D:YYYYMMDDhhmmssTZ
  /ModDate      D:YYYYMMDDhhmmssTZ
}

或者你可以用 完全禁用它们\pdfinfoomitdate=1

然后就是你在问题中提到的 ID。你可以/ID\pdftrailerid原语设置。你可以给它任何文本,包括空字符串:\pdftrailerid{}

这两者将保证在同一台计算机上以相同版本运行两次 pdfTeX 将会生成相同的 PDF。

记录的另一组数据取决于 pdfTeX 的版本和您使用的 TeX 发行版,因此如果您更新系统等,这可能不成立。还有四个记录项:

1     PTEX.Fullbanner
2     PTEX.FileName
4     PTEX.PageNumber
8     PTEX.InfoDict

可以通过将要禁用的项总数传递给 来选择性地禁用这些项\pdfsuppressptexinfo。例如,\pdfsuppressptexinfo=3将抑制FullbannerFileName。要禁用所有项,您可以传递\pdfsuppressptexinfo=-1

因此,总而言之,以下代码将在每次运行 pdfTeX 时生成相同的 PDF:

\pdfinfoomitdate1
\pdfsuppressptexinfo-1
\pdftrailerid{}

\input story

\bye

更新:

正如 John DeRoo 所说,他必须更新系统才能使用上述解决方案。进一步阅读 pdfTeX 手册,可以看到它说“该原语是在 pdfTeX 1.40.17 中引入的”。事实上,NEWS 文件包含:

pdfTeX 3.14159265-2.6-1.40.17 (TeX Live 2016)  (May 20, 2016)
- changes:
  - if the environment variable SOURCE_DATE_EPOCH is set, use its value for
    the PDF CreationDate and ModDate values, and to seed the trailer /ID.
    This by itself should suffice to create reproducible PDFs.  The
    new primitives below support more granular output tweaks with the
    same intent.
  - if the environment variable SOURCE_DATE_EPOCH_TEX_PRIMITIVES is set
    to 1, the \year, \day, and \time primitives are also initialized
    from the SOURCE_DATE_EPOCH value, instead of the current time.

  - new primitive \pdfinfoomitdate to omit CreationDate and ModDate keys.
  - new primitive \pdftrailerid to set seed for the trailer /ID
    computation; with an empty argument \pdftrailerid{}, the /ID is omitted.
  - new primitive \pdfsuppressptexinfo to omit PTEX.* keys from output;
    the value is a bitmask:
    % 1 -> PTEX.Fullbanner
    % 2 -> PTEX.FileName
    % 4 -> PTEX.PageNumber
    % 8 -> PTEX.InfoDict (/Producer /Creator /CreationDate /ModDate /Trapped)

所以在 2016 年 5 月之前的版本中,无法复制 PDF/CreationDate/ModDate仍然可以替换为\pdfinfo,但/ID仍然不同。

相关内容