pdfTeX

pdfTeX

我感兴趣的是使用 LaTeX 的方式,这样当我编译两次时,我会得到完全相同的结果文件。

我的测试.tex:

\documentclass{article}
\begin{document}
Hello, World!
\end{document}

编译产生不同的哈希值:

8493b40b225993d01d46ed7479725d8b4e9f6efbfddcc8d6d657f00084d41cdb  test.pdf
05f2a3cd3780df33470a4363da18b008595e42acd9a085d76c83b6c83dc71c41  test.pdf

等等。(这也适用于至少间隔一分钟编译为 DVI 的情况。)

我猜测这至少是由于 PDF 的创建和修改元数据造成的。我跟着这个答案来修复这些问题,但我仍然得到不同的哈希值。

我发现使用faketime '2008-12-24 08:15:42' pdflatex test该文件进行编译时可以重现。我得出结论,其中不涉及随机数据,但它仅取决于时间。

我的问题是,我可以从我的 TeX 文档中影响 pdflatex 的时间吗?

答案1

自从TeX Live 2016,有几个选项可以实现可重现的构建:

pdfTeX

对于 pdfTeX (版本≥1.40.17),有三个新的原语:

  • \pdfinfoomitdate,这将删除文档信息字典中的/CreationDate/ModDate条目。默认情况下,这些条目将设置为文档编译的日期。它们可能已经使用在旧版本的 pdfTeX 中进行了修改\pdfinfo{/CreationDate (...)} /ModDate (...)},但使用\pdfinfoomitdate=1,它们可以从生成的 PDF 文件中完全删除。
  • \pdftrailerid,它在文档信息条目中设置 PDF 文档的文件标识符,/IDPDF 规范,第 10.3 节默认情况下,它是通过散列当前日期和时间(即使\pdfinfoomitdate=1使用)和输出文件的完整路径来计算的。通过包含固定\pdftrailerid{string}细绳在您的文档中,此字符串的哈希值将用作标识符。将其留空会\pdftrailerid{}完全删除该/ID条目。
  • \pdfsuppressptexinfo控制写入文档的一些额外元数据:首先,pdfTeX 通常会创建一个PTEX.Fullbanner包含完整版本字符串的条目,如 的输出所示pdftex --version。此外,对于文档中包含的每个 PDF 图像,写入一些额外的元数据。对于可重现的构建来说,抑制这些条目并不是绝对必要的,但如果您想在不同的系统上编译相同的文档,这可能会有所帮助。可以通过发出 来完成\pdfsuppressptexinfo=-1

TL; DR:因此,获得可重现的 PDF 输出的最简单方法是使用

\documentclass{article}
\pdfinfoomitdate=1
\pdftrailerid{}
\begin{document}
Hello, World!
\end{document}

 

LuaTeX

自 TeX Live 2017 起,LuaTeX(版本≥1.0.4)也支持这些功能,尽管语法略有不同:

  • \pdfvariable suppressoptionalinfo防止某些元数据包含在生成的 PDF 文件中,类似于\pdfsuppressptexinfopdfTeX,但具有更多选项:

    \pdfvariable suppressoptionalinfo \numexpr
            0
        +   1   % PTEX.FullBanner
        +   2   % PTEX.FileName
        +   4   % PTEX.PageNumber
        +   8   % PTEX.InfoDict
        +  16   % Creator
        +  32   % CreationDate
        +  64   % ModDate
        + 128   % Producer
        + 256   % Trapped
        + 512   % ID
    \relax
    
  • \pdfvariable trailerid允许您指定自己的文件标识符\pdftrailerid,就像你必须自己掌握正确的语法,因此我建议直接使用上述命令来抑制 ID。

TL; DR:对于 LuaLaTeX 中的可重现构建,请使用

\documentclass{article}
\pdfvariable suppressoptionalinfo \numexpr32+64+512\relax
\begin{document}
Hello, World!
\end{document}

 

特克斯

自 TeX Live 2019 起,XeTeX 支持指定文件标识符:

  • pdf:trailerid\special识别的命令dvipdfmx,XeTeX 使用它来生成 PDF 文件。值格式与\pdfvariable traileridLuaTeX 中的 相同:两个 PDF 字符串的原始 PDF 数组。两个字符串都必须是 16 个字节。dvipdfmx 文档提供了一个带有文字字符串(在括号之间指定)的示例。另一个示例是带有 16 字节十六进制字符串(在括号之间指定<[…]>)的 MD5 哈希值,它可以是标识文档的 MD5 哈希值:

    \special{pdf:trailerid [
        <00112233445566778899aabbccddeeff>
        <00112233445566778899aabbccddeeff>
    ]}
    

 

所有主流引擎(pdfTeX、LuaTeX、XeTeX)

作为替代方案,pdfTeX、LuaTeX 和 XeTeX 支持SOURCE_DATE_EPOCH

如果将环境变量设置SOURCE_DATE_EPOCH为某个日期(以 Unix 时间戳的形式,例如由 的输出生成date +%s),则将使用此日期而不是当前日期。因此,将其设置为固定日期可让您创建可重现的 PDF 文件,而无需对 LaTeX 源代码进行任何更改。但请记住,输出文件名(对于 pdfTeX 和 LuaTeX 包括完整路径,对于 XeTeX 仅包括名称本身)仍用于计算上述文件标识符:因此,如果您移动或重命名 LaTeX 文档并进行编译,则生成的 PDF 文档将发生变化。

答案2

更新pdftex 中现在有(texlive 2016)SOURCE_DATE_EPOCH 支持来解决这个问题(参见其他答案)。

如果你将源代码修改为

\pdfcompresslevel=0
\pdfobjcompresslevel=0
\documentclass{article}
\begin{document}
Hello, World!
\end{document}

运行两次,你会发现有三行发生了变化

/CreationDate (D:20150222233514Z)
/ModDate (D:20150222233514Z)

/ID [<84943B8BBB033F5EF8FAE4B3E350E35C> <84943B8BBB033F5EF8FAE4B3E350E35C>] >>

因此,一种可能性是使用运行 pdflatex 的包装脚本,然后清空这些字段,保持字节数保持不变。

答案3

pdfprivacy您可以使用带有以下选项的包nopdftrailerid

pdfprivacy 还可以删除文档元数据:作者、标题、主题和关键字;以及允许您创建可复制 pdf 的 pdftrailerid。

https://ctan.gutenberg.eu.org/macros/latex/contrib/pdfprivacy/pdfprivacy.pdf


\usepackage[nopdftrailerid=true]{pdfprivacy}

也可以看看:在不同安装上可重现的构建

相关内容