Ghostscript:如何将 STDIN 自动裁剪为“边界框”并写入 PDF?

Posted

技术标签:

【中文标题】Ghostscript:如何将 STDIN 自动裁剪为“边界框”并写入 PDF?【英文标题】:Ghostscript: How to auto-crop STDIN to "bounding box" and write to PDF? 【发布时间】:2016-12-14 09:46:42 【问题描述】:

这里已经有很多关于使用 Ghostscript 裁剪文档的问题和答案。 但是,答案与我的确切需求不符,仍然让我感到困惑。 我希望会有一个选项,例如“-AutoCropToBBox”或类似的东西。

为了澄清,作为边界框,我理解最小的矩形框,它完全包含所有(非白色(?))打印对象。

此外,我想/必须使用打印机端口重定向 (RedMon) 通过从基本上任何应用程序打印到 Postscript 打印机来生成裁剪的 PDF。 所以,在Win7/64bit下,我设置了重定向的端口属性: Redirected port properties Win7/64bit

输出重定向到 C:\Windows\system32\cmd.exe

程序的参数是:

/c gswin64c.exe -sDEVICE=pdfwrite -o -sOutputFile="%1".pdf -

“%1”包含用户输入的文件名。有了这个,我得到了一个整页的 PDF。很好!

但是如何添加裁剪选项?

补充问题: 如果我有一个多页文档,这样的(自动)裁剪对于每一页来说都是单独的吗?或者是否可以选择保持相同,例如喜欢第一页还是喜欢所有页面中最大的边界框?

另一个相关问题: 提示输入文件名的窗口总是在我正在打印的应用程序后面弹出。有什么想法总是把它带到最前面吗?

另一个问题: 有 Perl 脚本“ps2eps”和程序 bbox.exe(参见http://ctan.org/pkg/ps2eps)。据说 Ghostscript(或 ps2epsi)偶尔(?)计算错误的边界框。这(仍然)是真的吗?

感谢您的帮助。

【问题讨论】:

仅供参考,您可以通过运行gswin64c.exe -sDEVICE=bbox -dBATCH -dNOPAUSE pdfname获取bbox 谢谢,@Stefan Hegny。我还在这里找到:gswin64c.exe -q -dBATCH -dNOPAUSE -sDEVICE=bbox -dLastPage=1 GsCrop.ps | findstr %%BoundingBox我的问题是如何进一步使用这些数字? 【参考方案1】:

您的第一个问题是 PostScript 程序通常被编写为期望渲染为特定的媒体大小,并且通常没有严格限制它。空格对于可读性很重要。

因此,您生成的 PostScript 程序通常会请求特定的媒体大小,解释器会尽力匹配该大小。如果无法匹配,它将使用策略尝试尽可能接近,并缩放整个内容以适应该媒体。

如果打印机在完成之前不知道所需的尺寸,并且在渲染所有标记内容之前无法确定边界框,您就不能指望打印机执行任何这些操作。确实,某些文件通常 EPS 文件有一个 %%BoundingBox 注释,但是.. 这是一个注释,它在 PostScript 中没有影响,它的存在是为了那些不想解释 PostScript 的应用程序的利益。

这就是为什么你想要的简单开关不存在的原因,它会破坏解释器的正常功能,用于渲染。

因此,您需要做的第一件事是确定内容的边界框。正如 Stefan 所说,您可以通过使用 bbox 设备来做到这一点。在那一点上,据我所知,bbox 设备可以产生准确的输出。如果没有,那么我们将不胜感激一个错误报告来证明它,以便我们可以修复它。如果人们不报告错误,我们应该如何了解它们?看到有人传播 FUD 而不是帮助提交错误报告,这令人失望.......

ps2epsi 不是 Ghostscript,它是一个廉价而令人愉快的糟糕脚本,我不会使用它。但是.....如果原始 PostScript 将东西留在堆栈上,那么它将最终成为损坏(或无效)的 EPS 文件,并且应在尝试使用原始 PostScript 之前对其进行修复,因为它会破坏任何尝试使用的 PostScript 程序使用它(例如,如果您将 EPS 包含在文档中,然后打印出来)。

因此,如果您使用的是 Ghostscript,并且您想使用 PostScript 程序并从中获取 EPS,请使用 eps2write 设备。坦率地说,它不会有预览。

现在如果我没记错的话,bbox 设备(和 eps2write)记录所有标记操作,你不能简单地记录所有非白色标记操作;如果白色覆盖页面上的现有标记怎么办?如果媒体不是白色怎么办?请注意,如果您使用 Ghostscript 渲染为 PNG,则输出的未触及部分是透明的,而白色标记则不是。

因此,bbox 是所有标记操作的范围,与颜色无关。唯一的其他方法是渲染内容并计算非白色像素。但这只适用于特定的分辨率,改变分辨率和精确的边界框也可能改变。

一旦你有了边界框,你就可以告诉 Ghostscript 使用那个大小的媒体。请注意,您几乎肯定必须翻译原点,因为内容不太可能从左下角紧紧地开始。您将需要 -dDEVICEWIDTHPOINTS 和 -dDEVICEHEIGHTPOINTS 来设置媒体大小,并且您需要使用 -c 和 -f 发送 PostScript 以适当地更改原点。在简单的情况下,“-x -y translate”就足够了,但如果程序执行 initgraphics,您将不得不设置一个 BeginPage 过程来更改初始 CTM。

如果您使用 -dDEVICEWIDTHPOINTS 等设置媒体大小,则所有页面的大小将相同。如果你不想这样,那么你需要编写一个 BeginPage 过程来单独调整每个页面的大小(你还需要挂钩 setpagedevice 并从字典中删除 /PageSize 条目。

我不知道为什么 Windows 将对话框放在活动窗口后面,它似乎已经开始使用 Windows 7(或可能是 Vista)。我看不到任何改变它的方法,因为我不确定是什么生成了对话框.....

我个人建议您尝试通过 Ghostscript 的 eps2write 设备运行原始文件的两步方法,然后获取 EPS 并使用 pdfwrite 设备和 -dEPSCrop 开关创建一个 PDF 文件。双重转换很糟糕,但其他解决方案更糟糕。请注意,EPS 文件不能是多页的,因此您必须从一个 n 页 PostScript 程序创建“n”个 EPS 文件,然后提供一个列出每个 EPS 文件的命令行作为输入到pdfwrite 设备。

在您尝试编写脚本之前,获取一个示例文件并在命令行中尝试一下。

【讨论】:

感谢@KenS 的详细解释。我必须先消化这个并进一步尝试...... 我现在尝试了这个命令行:/c gswin64c.exe -q -sDEVICE=eps2write -sOutputFile="%1".eps -o - && gswin64c.exe -q -sDEVICE=pdfwrite -dEPSCrop -o "%1".pdf "%1".eps,它会生成 EPS 和 PDF。然而,两者都被裁剪,而不是最小可能的盒子。左侧似乎还可以,顶部和底部剩余空间太大。它是错误还是功能?在我的测试文件的右侧有一条垂直线,如果我在 AcrobatReader 中打开 PDF,我看不到它。杂技演员的错误?但是,如果我在 GSView 中查看 EPS 和 PDF,我可以看到这条线。很奇怪?! 抱歉,右边好像还可以。由于页面周围的背景较暗,这条线在 Acrobat 中几乎不可见。 不确定您是否仍然认为有问题?我必须查看文件才能确定。我猜bbox计算可能无法正确拾取一条粗线宽度的线条。我认为它应该,但我可以相信这是一个错误。如果你想让我看一下,我需要看一下测试文件..... 哦,忘了提一下,如果在页面的两端包含文本,这可能会导致盒子不够紧凑。原因是我们不知道每个字形的精确度量,所以我们不得不使用字体边界框。有些字体弄错了。此外,没有下降线的文本会在底部留下一个白色间隙(如果有下降线,它将适合那里),没有上升线的文本将出于类似原因在顶部留下一个白色间隙。【参考方案2】:

我从@KenS 的解释中了解到:

    eps2write 的工作方式,它可能不会或不会或实际上不能产生最小可能的边界框 需要通过 -sDEVICE=bbox 进行两步处理

因此,我现在完成了以下过程,以“打印”具有正确的最小可能边界框的 PDF:

将打印机端口重定向到 cmd.exe C:\Windows\system32\cmd.exe

程序的参数:

 /c gswin64c.exe -q -o "%1".ps -sDEVICE=ps2write - && gswin64c.exe -q -dBATCH -dNOPAUSE -sDEVICE=bbox -dLastPage=1 "%1".ps 2>&1 >nul | perl.exe C:\myFiles\CropPS2PDF.pl "%1"

不幸的是,它需要一个小的 Perl 脚本(我们称之为:CropPS2PDF.pl):

#!usr/bin/perl -w
use strict;
my $FileName = $ARGV[0];
$/ = undef;
my $Crop = <STDIN>; 

$Crop =~ /%%BoundingBox: (\d+) (\d+) (\d+) (\d+)/s;   # get the bbox coordinates
my ($llx, $lly, $urx, $ury) = ($1, $2, $3, $4);
print "\n$FileName: $llx, $lly, $urx, $ury \n";   # print just to check

my $Command = qqgswin64c.exe -q -o $FileName.pdf -sDEVICE=pdfwrite -c "[/CropBox [$llx $lly $urx $ury]" -c " /PAGE pdfmark" -f $FileName.ps;

print $Command;    # print just to check
system($Command);    # execute command

它似乎工作...... :-) 欢迎改进。

我的问题还在:

    可以在没有 Perl 的情况下以某种方式完成吗?只有 Win7、cmd.exe 和 Ghostscript? 有没有办法不将 PS 文件写入我不需要的磁盘?当然,我也可以在之后使用 Perl 脚本将其删除。

【讨论】:

以上是关于Ghostscript:如何将 STDIN 自动裁剪为“边界框”并写入 PDF?的主要内容,如果未能解决你的问题,请参考以下文章

如何将外部字体添加到 ghostscript?

让 Graphic Magick 检测 Ghostscript 以在 Windows 上阅读 pdf。如何将 WINDOWS CMD 上的 Ghostscript 调用名称更改为“gs”?

如何使用 Ghostscript 将 PDF 中的页面替换为另一个页面?

使用 ghostscript 将 Postscript 转换为文本文件

如何使 ghostscript 将 postscript 文件中的单个特定页面输出为 png?

如何将画布部分从 Ghostscript 打印结果裁剪为 PNG