使用pdftk和Ghostscript进行PDF比较

4
我已经创建了一个脚本,通过查看Kurt Pfeifle的一些答案,将两个PDF文件并排合并成一个。
但我遇到的问题是代码不够灵活。我的意思是,如果一个PDF比另一个PDF大或具有不同的分辨率,输出的PDF(并排的PDF)会很差。
图示如下:
Input file: a.pdf
+--------+ 
|        |
|  a     |
|        |
+--------+

Input file: b.pdf
+--------+ 
|        |
|  b     |
|        |
+--------+

Desired output file: compare.pdf
+--------+--------+ 
|        |        |
|   a    |  b     |
|        |        |
+--------+--------+

所以在合并PDF之前,我需要确保两个PDF具有相同的正常A4大小和分辨率吗?我尝试了很多代码和脚本,但无法解决这个问题。我该怎么做?脚本需要足够强大,以便可以使用和比较任何PDF,即使它们没有相同的大小。

我的脚本现在看起来像这样,并且在某些具有相同大小和分辨率的PDF上运行:

gswin64c.exe                        ^
          -o c.pdf                  ^
          -sDEVICE=pdfwrite         ^
          -g11690x8270              ^
          -dFIXEDMEDIA              ^
          -dPDFSETTINGS=/prepress   ^
          -r300                     ^
          -c "<</PageOffset [0 0]>>setpagedevice" ^
          -f a.pdf

这将创建c.pdf,如下所示:
c.pdf
+--------+--------+ 
|        |        |
|   a    | (empty)|
|        |        |
+--------+--------+

下一条命令:
gswin64c.exe                       ^
          -o left-side-outputs.pdf ^
          -sDEVICE=pdfwrite        ^
          -g11690x8270             ^
          -dPDFSETTINGS=/prepress  ^
          -c "<</PageOffset [0 0]>>setpagedevice" ^
          -f b.pdf

这将创建名为left-side-outputs.pdf的文件,如下所示:
left-side-outputs.pdf
+--------+--------+ 
|        |        |
|   b    | (empty)|
|        |        |
+--------+--------+

下一条指令:
gswin64c.exe                        ^
          -o right-side-outputs.pdf ^
          -sDEVICE=pdfwrite         ^
          -g11690x8270              ^
          -dPDFSETTINGS=/prepress   ^
          -c "<</PageOffset [596 0]>>setpagedevice" ^
          -f c.pdf

这将创建名为right-side-outputs.pdf的文件,效果如下:
right-side-outputs.pdf
+--------+--------+ 
|        |        |
|(empty) |  b     |
|        |        |
+--------+--------+

上一个命令:
pdftk left-side-outputs.pdf multistamp right-side-outputs.pdf output compare.pdf

这样就生成了最终结果compare.pdf
Desired output file: compare.pdf
+--------+--------+ 
|        |        |
|   a    |  b     |
|        |        |
+--------+--------+

我希望有些IT技术高手能够帮助我解决如何处理具有不同页面大小的PDF输入文件的问题。


你的绘图和命令看起来不正确。我认为我知道你想要实现什么。我会编辑你的问题。如果我误解了,请撤销我的编辑... - Kurt Pfeifle
你看过pdfnup吗?它是pdfjam的一部分,是LaTex的pdfpages包的前端。它可以自动缩放pdf文件,并且非常稳定。 - Jakob
@Jakob:通常情况下,使用 pdfnup 可能更适合做2-up... 但在这种情况下,你的提示并没有太大帮助。首先,问题明确是关于 Ghostscript 和 pdftk 的。其次,任务是比较两个不同的文件,其中一个生成的“2-up”页面由来自两个原始文件中的页面组成。我不知道有什么简单的方法可以使用 pdfnup 实现这一点... - Kurt Pfeifle
@Kurt Pfeifle,您对问题和限制完全正确,这就是为什么我发表评论而不是回答的原因!尽管如此,pdfnup是一个很好的工具,可以将多个pdf文件进行nup处理,而无需使用gs进行操作。 - Jakob
2个回答

3

关于你的问题...

所以,在将它们合并之前,我需要确保这两个PDF都具有相同的常规A4大小和分辨率吗?

...答案是“是的,关于页面大小——不需要关注分辨率(无所谓)”

使用Ghostscript缩放PDF页面(1)

缩放所有混合大小的PDF页面为全A4页面的命令如下:

 gswin64c.exe           ^
     -o all-a4.pdf      ^
     -sDEVICE=pdfwrite  ^
     -g5950x8420        ^
     -dPDFFitPage       ^
     -f input.pdf

这个方法可以使媒体大小和内容按比例缩放(已使用GS v9.10测试过)。

参数-dPDFFitPage将始终保持宽高比。它会自动旋转内容以获得最适合的适配效果。但是它不允许将页面朝一个方向进行拉伸。但是,下一个方法可以实现该目的。


[更新]

我认为我没有清楚地传达出这种方法的一点。

问题在于:如果您输入文件中的媒体宽高比与目标媒体不同,则-dPDFFitPage将不能完全覆盖您的目标媒体。

假设您的输入媒介使用正方形页面大小500x500点。如果您使用A4的目标大小(-g5950x8420)处理此内容,则-dPDFFitPage将保持正方形纵横比,并仅生成-g5950x5950的输出尺寸。

但您也不能省略-dPDFFitPage--否则,您无法将原始的400x400内容进行缩放,而只能将其放置在更大的595x842页面上,并放置在左下角。

[更新结束]


使用Ghostscript缩放PDF页面(2)

将所有PDF页面内容缩放为其各自尺寸的50%的命令如下:

 gswin64c.exe                                      ^
     -o 50pc.pdf                                   ^
     -sDEVICE=pdfwrite                             ^
     -c "<</Install {.5 .5 scale}>> setpagedevice" ^
     -f input.pdf

然而,这将不会同时缩放媒体框!如果您知道PDF文件中的所有页面大小都相同,您可以使用此方法将A3 PDF缩放为A4:
 gswin64c.exe                                      ^
     -o A4-50pc.pdf                                ^
     -g5950x8420                                   ^
     -sDEVICE=pdfwrite                             ^
     -c "<</Install {.5 .5 scale} /AutoRotatePages /None>> setpagedevice" ^
     -f A3.pdf

然而,我回答中的第一个命令当然也可以使用,而且更加简单易用!

对于 A5 -> A4A4 -> A3 ,请使用以下命令:

                    {1.415 1.415 scale}

对于 A3 -> A4A4 -> A5

                    { .707  .707 scale}

但是现在更有趣的是,您也可以 '拉伸' 内容! 要水平缩放到75%并垂直缩放到66%,请使用
     -c "<</Install {.75 .666 scale}>> setpagedevice"

如果需要在 LetterA4 之间进行一种类似于“液体”般的缩放,您可以使用下列方法:

  • A4 -> Letter: {1.028571 .940617 scale}
  • Letter -> A4: { .972222 1.063131 scale}

对于上述所有内容,您都可以提供一个 -gNNNNxMMMM 值(这将确定输出 PDF 的固定页面大小——以设备的默认内部分辨率720 ppi表示的像素尺寸为单位,每个 PostScript 点有10个像素...)-

如果您没有提供一个 -gNNNNxMMMM 值,那么将使用原始页面尺寸(即使它们是混合值),但其内容将根据您指定的缩放因子绘制在这些页面上。

我现在不知道的内容:如何一次性“液体缩放”混合尺寸 PDF 中的每一页 包括媒体尺寸...

比较基于 A4 的全 Letter 和全 A5 PDF 文件:

假设您现在想要将全 Letter 大小的 PDF 与全 A5 大小的 PDF 进行比较,并希望首先将其缩放到 A4,请按照以下步骤操作:

将 Letter 缩放为 A4:

 gswin64c.exe                                      ^
     -o a4-1.pdf                                   ^
     -sDEVICE=pdfwrite                             ^
     -g5950x8420                                   ^
     -c "<</Install{.972222 1.063131 scale}>>setpagedevice" ^
     -f letter.pdf

将固定比例的A5调整为A4:

 gswin64c.exe                                      ^
     -o a4-2.pdf                                   ^
     -sDEVICE=pdfwrite                             ^
     -g5950x8420                                   ^
     -c "<</Install{1.415 1.415 scale}>>setpagedevice" ^
     -f a5.pdf

或者,作为替代选择:
 gswin64c.exe          ^
     -o a4-2.pdf       ^
     -sDEVICE=pdfwrite ^
     -g5950x8420       ^
     -dPDFFitPage      ^
     -f a5.pdf

现在比较一下你的两个A4 PDF文件...

优化您的比较工作流程

您还可以按照您提出的问题中概述的步骤节省一个步骤。以下是更好的方法。

第一步:准备左侧(与以前相同)

假设您有A4输入,最终输出应为A3:

 gswin64c.exe                   ^
      -o left-sides.pdf         ^
      -sDEVICE=pdfwrite         ^
      -g11900x8420              ^
      -c "<</PageOffset [0 0]>>setpagedevice" ^
      -f a.pdf

这将创建:
left-sides.pdf
+--------+--------+   ^
|        |        |   |
|        |        |   |
|  a     |(empty) |  595 pt == 5950 pixels
|        |        |   |
|        |        |   |
+--------+--------+   v

<-----1190 pt----->
   == 11900 pixels

第二步:准备正确的侧面(一次性完成)
 gswin64c.exe                   ^
      -o right-sides.pdf        ^
      -sDEVICE=pdfwrite         ^
      -g11900x8420              ^
      -c "<</PageOffset [595 0]>>setpagedevice" ^
      -f b.pdf

这将创建:
right-side.pdf
+--------+--------+   ^
|        |        |   |
|        |        |   |
|(empty) |  b     |  595 pt == 5950 pixels
|        |        |   |
|        |        |   |
+--------+--------+   v

<-----1190 pt----->
   == 11900 pixels

第三步:使用pdftk叠加两个文件。
pdftk right-sides.pdf multistamp left-sides.pdf output compare.pdf

或者
pdftk left-sides.pdf multistamp right-sides.pdf output compare2.pdf

这将创建:
compare.pdf
+--------+--------+   ^
|        |        |   |
|        |        |   |
|  a     |  b     |  595 pt == 5950 pixels
|        |        |   |
|        |        |   |
+--------+--------+   v

<-----1190 pt----->
   == 11900 pixels

关于裁剪、修剪、美术和出血边框的更新

还有一件事。

有时候上述命令似乎不能正常工作。原因是PDF文件不仅使用了被简单认为的"页面尺寸",而且还使用了更复杂的MediaBox(通常被认为是"页面尺寸")、TrimBoxBleedBoxArtBoxCropBox设置。在这里可以找到对这些盒子的精确描述...

为了测试您的PDF文件(包括输入、结果和中间结果)的所有这些框的值,请使用pdfinfo命令:

pdfinfo -f 1 -l 5 -box a.pdf
pdfinfo -f 1 -l 5 -box b.pdf
pdfinfo -f 1 -l 5 -box right-sides.pdf
pdfinfo -f 1 -l 5 -box left-sides.pdf
pdfinfo -f 1 -l 5 -box compare.pdf
CropBox 是 PDF 阅读器(和打印机)仅显示(或打印)页面 MediaBox 上的内容的设置,如果它与重新缩放任务不同,则会妨碍此任务。如果 Ghostscript 检测到这种情况,它将不会接触它。

尽管文件已经处理成功,但在查看器中仍然显示相同的视口。

为了“解除武装”这些框的影响,您可以使用一种非常简单的技巧:将 PDF 中的这些字符串重命名为全小写名称。以下是使用命令行中的sed 命令如何实现此操作(在 Windows 可能不可用):

cat input.pdf                    \
   | sed 's#CropBox#cropbox#g'   \
   | sed 's#TrimBox#trimbox#g'   \
   | sed 's#BleedBox#bleedbox#g' \
   | sed 's#ArtBox#artbox#g'     \
> disarmed.pdf

或者更简洁一些,但不容易解析的:

sed 's#CropB#cropb#g;s#TrimB#trimb#g;s#BleedB#bleedb#g;s#ArtB#artb#g' \
  in.pdf > out.pdf

由于Ghostscript是二进制文件格式,因此在某些版本的sed中,您可能会遇到错误消息,如:

sed:RE错误:非法字节序列

在这种情况下,请尝试使用其他版本,如GNU sed,gsed...


哇,非常感谢你的回答,Kurt - 你付出了很多努力 - 非常感激。现在需要进行一些测试 :-) 我还有一个问题,希望你能帮我解决。如果您尝试将此PDF的宽度设置为11900像素,则无法完成,您能想出原因吗?http://gassalg.dk/~/media/gassalg.dk/dokumenter/regnskab/hmn_annual_report12_uk_issuu_01a.ashx - Mark Chabert Bergh
代码十分“标准”,但是它无法更改 PDF 的大小:gswin64c.exe -o left-side-outputs.pdf -sDEVICE=pdfwrite -g11900x8420 -dFIXEDMEDIA b.pdf。 - Mark Chabert Bergh
@MarkChabertBergh:你给的链接对我来说返回了一个空白页面... -- -dFIXEDMEDIA不是我的命令。正如我在对KenS的评论中指出的那样,使用-gNNNxMMM会自动隐含-dFIXEDMEDIAgswin64c.exe -version对你返回了什么?-- 你所说的*'it simply can't be done'*是什么意思?你有收到任何错误信息吗? - Kurt Pfeifle
gswin64c.exe -version 返回 Ghostscript 9.10 (2013-08-30)。我现在已经删除了dFIXEDMEDIA。我没有收到任何错误消息,但Ghostscript无法像脚本请求的那样将PDF的宽度扩展到11900像素。输出文件仍然保持相同的宽度。我无法弄清楚为什么以及如何完成?这只是一个无法工作的文件,但我发现有很多代码根本无法更改宽度。http://wikisend.com/download/524852/hmn_annual_report12_uk_issuu_01a.pdf - Mark Chabert Bergh
@MarkChabertBergh:你真的看了我最后一次更新吗(在你的评论之前九个小时完成)?!关于{Trim,Bleed,Crop,Art}Boxes的那个?这就是问题的原因...“修复”也在其中... - Kurt Pfeifle
抱歉没看到你的更新,不过你说得对。我正在使用一台Windows电脑,但是通过advanced-pdf-tools删除了Boxes,现在它运行得非常好!非常感谢Kurt——没有你,我永远找不到解决方案! - Mark Chabert Bergh

1

PDF文件不包含分辨率,因此这不能成为问题。我通常也不会使用-r与Ghostscript,因为所有这些都只是指定在将无法“原样”发射到PDF文件中的任何内容呈现为图像时呈现该内容的分辨率。它不影响该内容的大小或位置。

您不应该需要/PageOffset,我认为那根本没有任何效果(如果输入是PDF)。

我不会使用/PDFSETTINGS。通过使用它,您正在导入各种罐装设置,除非您确信这些设置正是您想要的,否则最好使用默认值并单独更改任何要更改的开关。

您很可能需要放置/AutoRotatePages=/None,否则pdfwrite将尝试使大部分文本水平地从左到右运行。

您正在两次转换其中一个文件,应尽量避免这种情况,转换次数越多,出现问题的可能性就越大。

您已在所有三个Ghostscript输入上指定了媒体大小,但您尚未在其中两个上指定FIXEDMEDIA。对于其中一个来说,这可能是可以接受的,因为它是第一个的重新处理(在那里您确实指定了FIXEDMEDIA),但第二个实例怎么办?

您并没有明确说明您所遇到的问题是什么。您也没有说这个问题是在单独的文件中出现,还是只有在使用pdftk合并它们时才会出现。没有这些信息和一些能够展示问题的样例文件,我们就无法为您提供更多的指导。
另外顺便提一下,您实际上可以直接使用Ghostscript来进行n-up排版,尽管您需要比使用pdftk更多的工作量。通过一点努力,我可能可以在一个Ghostscript命令中完成整个过程。

非常感谢您抽出时间!如何使用Ghostscript进行n-up排版?我需要将页面放在同一页上(这样就是普通A4纸的两倍宽)。 - Mark Chabert Bergh
@KenS:使用“-gNNNxMMM”指定媒体大小是意味着“-dFIXEDMEDIA”,对吗?因此,甚至不需要在两个未包含它的命令中指定它... - Kurt Pfeifle
@KenS:哦,我也很想看看你的“一次Ghostscript调用完成整个事情的小尝试”的结果。这将非常酷,并且会受到非常高的赞赏!;-) - Kurt Pfeifle
很抱歉,我并不是在提供帮助,只是指出这是可以完成的。请注意,如果您想要修改/缩放混合页面大小的PDF文件,您可以重新定义setpagedevice。每当PDF页面大小更改时,Ghostscript将执行带有包含/PageSize的字典参数的setpagedevice。因此,您可以让您重新定义的函数检查字典中是否存在该键。如果存在,则用适当的大小替换数组,并插入/替换/Install矩阵以缩放CTM。我没有尝试过这个方法,但它应该可以工作,我以前也使用过类似的技巧。 - KenS

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