为什么嵌入到 PDF 中的 JPEG 渲染与原始图像略有不同?

Why does a JPEG embedded into a PDF render slightly differently than the original?

PDF 容器允许将完整的 JPEG 文件(包括 header 和所有文件)嵌入到文档中。但是,即使存储在 PDF 中的 JPEG 与原始文件 bit-by-bit 相同,它的呈现方式也会与原始 JPEG 略有不同。我想知道为什么会这样,以及如何使 PDF 中的 JPEG 呈现与原始 JPEG 文件完全一样。以下是重现我的发现的方法:

设置

拿这张测试图(md5sum: 5085774e481966b3359df0745c57daca)

curl https://i.stack.imgur.com/fRHo8.jpg > test.jpg

并将其放入 pdf 容器中。您可以使用工具 img2pdf

img2pdf --producer="" --nodate test.jpg > test.pdf

或者您可以使用 PDF 的这个 gzip base64 表示:

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

将 base64 复制粘贴到文本文件中,然后 运行:

$ base64 -d test.txt | gzip -cd > test.pdf

最终PDF的md5sum:156994ee6590ef8421fad1325378906d

大概关键的部分:

6 0 obj
<</BitsPerComponent 8 /ColorSpace /DeviceRGB /Filter /DCTDecode /Height
  60 /Length 1790 /Subtype /Image /Type /XObject /Width 60>>
stream

在 PDF 查看器中查看它会显示原始图像。至少那是你的想法,但我们现在将揭示其中的微小差异。为了测试 PDF 的渲染方式,我们使用了三种不同的渲染引擎,以确保这是一个系统错误,可能不是特定渲染引擎的问题

ghostscript

$ gs -dNOPAUSE -dBATCH -sDEVICE=png16m -r96 -sOutputFile=gs.png test.pdf

波普勒

我们使用 Debian 和衍生产品中 poppler-utils 软件包中的 pdftocairo 工具。

$ pdftocairo -r 96 -png test.pdf poppler

mupdf

$ mutool draw -o mupdf.png -r 96 test.pdf

评价

第一个观察结果:这三个工具都生成相同的 PDF 渲染图。我们使用 imagemagick 进行比较。

$ compare -metric AE mupdf.png gs.png null:
0
$ compare -metric AE poppler-1.png mupdf.png null:
0
$ compare -metric AE gs.png poppler-1.png null:
0

现在我们与原始输入图像进行比较:

$ compare -metric AE mupdf.png test.jpg null:
105
$ compare -metric AE poppler-1.png test.jpg null:
105
$ compare -metric AE gs.png test.jpg null:
105

让我们想象一下差异:

有人可能会认为,当我们将 test.jpg 嵌入 PDF 容器时,某些数据可能已更改,所以让我们从 PDF 中提取 JPEG:

$ pdfimages -j test.pdf extracted

pdfimages 从 PDF 中提取的数据与输入图像完全相同 test.jpg:

$ cmp test.jpg extracted-000.jpg || echo different
$ md5sum extracted-000.jpg test.jpg
5085774e481966b3359df0745c57daca  extracted-000.jpg
5085774e481966b3359df0745c57daca  test.jpg

结论

因此,显然,PDF 文件中嵌入的 JPEG bit-by-bit 与原始 JPEG 相同。不过,渲染 PDF 会产生与输入略有不同的结果。更重要的是:三个不同的 PDF 引擎产生相同的差异。

这是为什么?

如何使 PDF 显示与输入的 JPEG 完全一样?

我猜测,ImageMagick 使用的是与其他三个引擎不同的 JPEG 解码器,因为它们彼此一致。我知道 Ghostscript 和 MuPDF 使用 jpeglib,不知道 poppler。

所以你说的不是 PDF 变体 'wrong' 只是 'not the same as compare (the ImageMagick tool ?)' 这不是 (IMO) 相同的东西。您也可以让 Ghostscript 渲染原始 JPEG,通过在 lib 目录中使用 viewjpeg.ps,我相当确定 MuPDF 也可以直接渲染 JPEG 文件。我敢打赌,他们渲染 JPEG 的方式与渲染包含 JPEG 的 PDF 相同。

JPEG 是一种有损格式,它使用的离散余弦变换是一种数学变换(它使用高通和低通滤波器),我敢打赌这仅仅是由于所用数学的舍入差异在从过滤后的数据中重建样本时,我强烈怀疑您无法通过肉眼发现任何差异。您是否看过图像中相关位置的样本 RGB 值?

我建议您尝试使用 JPEG 作为 MuPDF 和 Ghostscript 的直接输入(如果可以,还可以使用 poppler)。我的期望是结果将与 PDF 的呈现相匹配。

在这种情况下,谁是奇数鬃毛?

两者都是色彩空间sRGB。但一个区别是您的 test.jpg 没有颜色配置文件。但是 test.pdf 有一个 sRGB.icc 配置文件,至少在使用 Imagemagick 转换为 convert test.jpg test.pdf 时是这样。添加颜色配置文件通常可以改变图像的查看方式,即使数据相同,但查看方式可能取决于查看者以及是否采用 sRGB 配置文件(即使它不存在)或忽略配置文件。使用 Imagemagick identify -verbose,我明白了

test.jpg

 Properties:
    date:create: 2018-08-18T09:57:16-07:00
    date:modify: 2018-08-18T09:57:16-07:00
    jpeg:colorspace: 2
    jpeg:sampling-factor: 1x1,1x1,1x1
    signature: fa0b78710e2c79f1cc852e0ae805393ea96d1f51769beb9d1729ec330da71303
  Artifacts:
    filename: test.jpg
    verbose: true
  Tainted: False
  Filesize: 1790B
  Number pixels: 3600
  User time: 0.000u
  Elapsed time: 0:01.000
  Version: ImageMagick 6.9.10-10 Q16 x86_64 2018-08-14 https://www.imagemagick.org


test.pdf

 Properties:
    date:create: 2018-08-18T09:59:37-07:00
    date:modify: 2018-08-18T09:59:37-07:00
    icc:copyright: Copyright Artifex Software 2011
    icc:description: Artifex Software sRGB ICC Profile
    pdf:HiResBoundingBox: 60x60+0+0
    pdf:Version: PDF-1.3  1 0 obj << 
    signature: b3783659c15300ffb14e4da501db28afcc0e0b0060286effa74823327a24abd6
  Profiles:
    Profile-icc: 2576 bytes
  Artifacts:
    filename: test.pdf
    verbose: true
  Tainted: False
  Filesize: 8415B
  Number pixels: 3600
  Pixels per second: 360000B
  User time: 0.000u
  Elapsed time: 0:01.009
  Version: ImageMagick 6.9.10-10 Q16 x86_64 2018-08-14 https://www.imagemagick.org


在转换为 PDF 之前,尝试将 sRGB.icc 配置文件添加到您的 test.jpg 文件。看看有没有帮助。