批量转换和裁剪后记到pdf

时间:2012-01-03 12:00:27

标签: pdf ghostscript postscript eps calibre

我几乎不知道在这个数字世界中生存。

我有很多单页的postscript文件(图形/图像)我希望转换为pdf并自动裁剪到一个窄框。我现在在Windows上(我也使用linux,所以不要犹豫,为linux发布代码)

我过去通过组合Ghostscript gswin32c.exe和Calibre pdfmanipulate.exe获得了成功。这可能是许多人熟悉的方法。

但由于几个原因,这种方法已经充满了问题。

在升级"之后出现了一个问题。到64位gswin64c.exe。 32位版本gswin32c.exe仍可在我的系统上运行,所以我不能抱怨太多。

处理可能编码不当的postscript文件时出现了另一个问题。似乎至少有两个问题,但我不确定哪个(如果有的话)是负责任的,或者两个都是。一个问题是边界框线,例如 %% BoundingBox:135 179 484 587 并不总是放在从顶部开始的第二行。我明白这可能是一个问题。另一个问题是上面的边界框对应于一个"肖像"在Ghostscript中的定位,但裁剪遵循" Landscape"取向。我还没有发现的另一个问题是,对于某些文件来说,裁剪似乎很随机。

所以这是我的32位方法(适用于高质量文件),然后是64位自适应,它不起作用(可能是因为它在我的机器上调用了一些pypdf脚本而不是口径提供的修补脚本,如果我理解https://bugs.launchpad.net/ubuntu/+source/calibre/+bug/800551http://www.mobileread.com/forums/archive/index.php/t-103097.html,但我只是在猜测,并且无论如何都不知道解决方法):

@echo off echo batch processing with Latex ps2pdf followed by Ghostscript gswin64c.exe and Calibre2 pdfmanipulate.exe for %%I in (*.ps,*.eps) do ( "C:\Program Files\MiKTeX 2.9\miktex\bin\x64\ps2pdf" %%I ) for %%I in (*.pdf) do ( "C:\Program Files (x86)\Ghostscript\gs9.00\bin\gswin32c.exe" -dSAFER -dNOPAUSE -dBATCH
-sDEVICE#bbox "%%I" 2> bounding "C:\Program Files (x86)\Calibre2\pdfmanipulate.exe" crop -o "%%~nICropped32.pdf" -b bounding "%%I" pause "C:\Program Files\Ghostscript\gs9.04\bin\gswin64c.exe" -dSAFER -dNOPAUSE -dBATCH
-sDEVICE#bbox "%%I" 2> bounding "C:\Program Files (x86)\Calibre2\pdfmanipulate.exe" crop -o "%%~nICropped64.pdf" -b bounding "%%I" pause )

上述32位方法适用于高质量文件,例如:后记3级由PSTricks或Maple的标准2D绘图驱动程序生成,但不适用于较旧的文件,例如。后记2级(如果那样)由Maple的经典情节驱动程序产生。

我找到了一些此类文件的解决方法。它包括使用(MiKTeX)LaTeX发行版中的epstopdf。它适用于那些Maple经典文件。不幸的是,它并不适用于我几年前用PSTricks和其他软件如Matlab生成的其他一些postscript文件。

所以我需要进行几次转换并选择有效的转换。我想知道你是否会提出让我的生活更轻松的建议。如果我能修复BoundingBox和Portrait / Landscape问题,我应该非常满意。

我提前感谢您的任何建议。 Linux建议是可以接受的。我倾向于寻求一种解决方案,它可以在一次推送" return"键。

当然,我正在寻找一种无损类型的裁剪,一种仅仅是解释边界框,而不是将其转换为(可能)低质量的PDF格式。

编辑:我忘了说。当我将gswin32c / pdfmanipulate应用于高质量的3级postscript文件时,该文件名为" bounding"填写以下信息:

%% BoundingBox:34 128 567 667 %% HiResBoundingBox:34.364390 128.875004 566.054069 666.071980

在上面的示例中,文件已经被裁剪掉了。请注意%% BoundingBox和%% HiResBoundingBox

之间的接近程度

但是应用于低质量等级2(或者它声称是)postscript文件,"边界"文件填写:

%% BoundingBox:189 137 574 467 %% HiResBoundingBox:189.485994 137.843996 573.299983 466.668478

但边界框确实应该是 %% BoundingBox:135 179 484 587 上面的(135 179 484 587)是postscript文件本身提供的边界框(我通过复制粘贴移动到第二行),它与纵向方向上由Ghostview / Ghostscript解释的边界框一致。

但它完全被Ghostscript忽略了......

我不知道189 137 574 467来自哪里 - 这是非常错误的......

编辑2.我想澄清几点,回答肯的问题:

嗨肯,谢谢你的回复,

抱歉,如果我的问题不清楚 - 尽管如此,你似乎已经理解了它的要点 - 让我依次回答你的问题:

  

我不确定您使用2个应用程序的原因,应该可以使用Ghostscript执行整个转换。

我没有找到使用Ghostscript完成所有操作的方法,所以我用另一种方式。我在这里找到了Ghostscript / Calibrate的建议,http://www.mobileread.com/forums/archive/index.php/t-72885.html,以及其他地方,尝试了它,直到最近才起作用。

我并不是说不可能用Ghostscript做到这一切,我只是说我找不到办法。

  
    

"在我升级"之后出现了一个问题。到64位gswin64c.exe"     您还没有说出问题所在,您是否将其报告为错误?如果人们不报告错误,他们就不会得到修复......

  

我在这里提供了描述问题和错误报告的链接:https://bugs.launchpad.net/ubuntu/+source/calibre/+bug/800551http://www.mobileread.com/forums/archive/index.php/t-103097.html, 我的问题完全相同。

  

您似乎在PostScript程序和评论之间存在一些混淆。 PostScript程序中的任何行开头'%'是评论,对程序的运行没有影响。因此,BoundingBox评论根本不会做任何事情。

我愿不同意,如果可以的话。获取一个postscript文件,删除%% Bounding Box,保存并在Ghostview中打开它。 Ghostview抛出错误消息,然后在不使用边界框信息的情况下显示它,例如,一个由很多白色空间围绕而不是被边界框紧紧包围的人物。所以是的,这个评论至少在Ghostview中做了一些事情。删除%% Bounding Box后,如果您随后使用Calibre / pdfmanipulate裁剪pdf,则在%% Bounding Box工作的情况下会错误地裁剪它。所以这个"评论"在显示和裁剪的上下文中非常有用。

  

请注意,不要求它是文件的第二行.....

Adob​​e推荐。引自adobe,

"第二个必需的DSC标题注释提供有关的信息 EPS文件的大小必须存在,以便包含应用程序 正确转换和剪辑EPS文件。这是边界框注释。"

http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf

Adob​​e说"必须。"就个人而言,只要我能从我的eps中产生适当限制的pdf,我就不会那么在乎它。

  

一般情况下,Ghostscript会忽略DSC注释,但是如果将ProcessDSC设置为true,那么它将非常有限地使用它(主要是用于设置页面大小的BoundingBox注释)。

使用pdfmanipulate,它会在正确裁剪的pdf和不正确裁剪的pdf之间产生差异。

  

继续前进。您说您正在使用LaTeX ps2pdf,如果您已经有一个PostScript文件,您可以将其发送到Ghostscript以转换为PDF。我不清楚在这种情况下你究竟使用Ghostscript是什么,只是为了找到页面的真正边界框?

  

我不清楚你的意思是什么?无损'裁剪,如果你裁剪内容你必须清楚地丢失一些东西,即使它只是空白......

我的意思是我不希望裁剪过程“光栅化”#34; (或者无论它叫什么,你都会知道这个术语)整个图像。裁剪出来的文件部分对我没用,所以它并没有太大的损失。作物中的文件部分应与原始文件具有相同的质量。这是一般的想法。

你可以在这里找到关于此的评论,这是我找到有用信息的地方, http://www.charlietanksley.net/philtex/reading-pdfs-on-portables/

  

如果您知道要裁剪的尺寸,它很容易在一次通过中进行转换,

不,我不知道大小,这就是为什么我会花这么长时间让软件为我计算它,这显然不是一件简单的事,因为Ghostscript并且epstopdf并不总是同意最佳作物,一个是正确的一些文件,而不是其他文件,另一个是正确的其他文件,但不适合一些......

  

如果您不知道大小,那么只需使用Ghostscript就可以通过首先提取BoundingBox来完成2次传递。这将获得4个数字,边界框的左下角和右上角(如果我没记错的话)。然后,您创建一个'翻译' PostScript操作将页面内容向下和向左移动(使其从0,0左下角开始)。您还可以创建页面设备请求来设置页面大小,大小由width = right - left和height = top - bottom给出。将原始文件与PostScript操作符一起提供给Ghostscript并选择pdfwrite设备,您将获得一个PDF文件。

如果您有一个方便的话,批处理文件示例会很棒。我已经看到了几个基于pdfwrite的例子,但我没有尝试过这些例子。魔鬼在细节中。

  

就边界框而言,它可能是一个错误,或者可能是文件制作标记,可能使用外部位置的白色墨水。在这种情况下,边界框设备仍将其视为页面内容的一部分。您可能会发现它不是,但设备不能。考虑页面是否首先填充了深色背景,并使用白色墨水勾勒出内容。

这些文件都是用Matlab,Maple,PSTricks等软件创建的,并且不太可能(但显然不是不可能)在%% Bounding Box给出的区域外面会有不可见的白色标记。 / p>

在许多情况下,%% Bounding Box注释包含所需的所有信息,我喜欢Ghostscript或Calibre或pdfwrite或任何人使用该信息。

  

如果不了解更多关于您想要做的事情,并且理想地看到一个或多个有问题的文件,我无法提供全面的解决方案。

这将非常简单,我如何发布一个postscript文件供您查看?它是420千字节。

谢谢Ken,我们希望我们能找到一个可行的解决方案。

编辑3.我发现了问题的很大一部分。

我的postscript文件有以下边界框,非常接近最佳裁剪: %% BoundingBox:135 179 484 587

当我运行Ghostscript gswin64c / gswin32c来计算边界框时,即

for %%I in (*.ps,*.eps) do ("C:\Program Files\Ghostscript\gs9.04\bin\gswin64c.exe" -dSAFER -dNOPAUSE -dBATCH -dAutoRotatePages=/None -sDEVICE#bbox "%%I" 2> bounding)

我明白了:

  

%% BoundingBox:145 189 475 574 %% HiResBoundingBox:145.331574   189.485994 474.155986 573.299983

当我运行ps2pdf后跟Ghostscript gswin64c,即

for %%I in (*.ps,*.eps) do ("C:\Program Files\MiKTeX 2.9\miktex\bin\x64\ps2pdf" %%I)
for %%I in (*.pdf) do ("C:\Program Files\Ghostscript\gs9.04\bin\gswin64c.exe" -dSAFER -dNOPAUSE -dBATCH -dAutoRotatePages=/None -sDEVICE#bbox "%%I" 2> bounding)

我得到以下边界框:

  

%% BoundingBox:189 137 574 467 %% HiResBoundingBox:189.395994   137.843996 573.299983 466.668478

所以问题是ps2pdf从ps到pdf的转换引入了边界框信息的变化,导致裁剪错误。所以用其他东西替换ps2pdf,比如eps2pdf解决了这里的问题。当然还有其他解决方案。如Ken和luser droog所建议的那样,特别有价值的是涉及Ghostcript的解决方案。他们非常有价值(并且优于我的快速修复)建议如下。这样的事情起作用了:

for %%I in (*.eps,*.ps) do ("C:\Program Files\MiKTeX 2.9\miktex\bin\x64\epstopdf" %%I)
for %%I in (*.pdf) do (
"C:\Program Files\Ghostscript\gs9.04\bin\gswin64c.exe" -dSAFER -dNOPAUSE -dBATCH -dAutoRotatePages=/None -sDEVICE#bbox "%%I" 2> bounding
"C:\Program Files (x86)\Calibre2\pdfmanipulate.exe" crop -o "%%~nICropped.pdf" -b bounding "%%I"
)

5 个答案:

答案 0 :(得分:4)

如果只是强制执行,BoundingBox注释将执行您想要的操作,您可以使用文本扫描程序替换对ghostscript的第一次调用。

这是上面脚本的sh版本(不能代表那些Windows路径名!)

for i in *.pdf ; 
do 
    gs -dSAFER -dNOPAUSE -dBATCH -sDEVICE=bbox "$i" 2> bounding ; 
    pdfmanipulate crop -o "${i%.pdf}-cropped.pdf" -b bounding "$i" ; 
done

您可以将其修改为使用grep,如下所示:

for i in *.pdf ; 
do 
    grep '%%BoundingBox' "$i" > bounding ; 
    pdfmanipulate crop -o "${i%.pdf}-cropped.pdf" -b bounding "$i" ; 
done

如果我在Windows上尝试这样做,我会安装cygwin并使用相同的脚本。

答案 1 :(得分:4)

评论中的空间不足以添加此内容,所以我担心我还会发布另一个答案....

由于PDF转换过程的特性,BoundingBox看起来对PDF文件造成伪造的原因。默认情况下,它旋转页面,直到大部分文本是水平的,对于这个文件(并且我假设其他文件具有相同的问题),这导致顺时针旋转90度。

这当然也意味着边界框也会旋转,并且对值的检查表明这就是发生的事情。因此,对于旋转的PDF文件,BoundingBox是正确的

现在,我通过私人电子邮件提供了几个PostScript程序,这就是我所说的:

1pass.ps

这将从源PostScript文件中读取BoundingBox行,并使用它来设置页面大小和偏移量。您通过使用您提供的文件设置'SourceFileName'例如传递要使用的文件的名称:

gs -sDEVICE=pdfwrite -sSourceFileName=classic.ps -o out.pdf 1pass.ps

将生成一个名为out.pdf的文件,该文件是读取BoundingBox的结果,并转换为PDF文件,页面被裁剪为该大小。

%!PS  

%% redefine setpagedevice to prevent changes by the PostScript program  
%% But keep a copy under a different name, so we cna use it.  
/Oldsetpagedevice /setpagedevice load def  
/setpagedevice {pop} bind def  

(File to process is ) print SourceFileName ==  

/SourceFile SourceFileName (r) file def  
/BoxString 65535 string def  
/LLx 0 def  
/LLy 0 def  
/URx 0 def  
/URy 0 def  
/FoundBox false def  

/GetValues {  
  token {                   % read a PostScript token  
    /LLx exch def               % Assume its a number for now  
    token {  
      /LLy exch def  
      token {  
        /URx exch def  
        token {  
          /URy exch def  
          pop                       % Get rid of any remaining string data  
          true              % return success code  
        }{  
          (Failed to read a number from the string) ==  
          false             % return failure code  
        } ifelse  
      }{  
        (Failed to read a number from the string) ==  
        false               % return failure code  
      } ifelse  
    }{  
      (Failed to read a number from the string) ==  
      false                 % return failure code  
    } ifelse  
  } {  
    (Failed to read a number from the string) ==  
    false                   % return failure code  
  } ifelse  
} bind def  

{  
  SourceFile BoxString readline {  
    (%%BoundingBox:) anchorsearch {  
      pop                           %% discard matching string  
      GetValues             %% extract BBox  
      /FoundBox exch def        %% Note success/failure  
      exit                  %% exit this loop  
    } {  
      pop                   %% discard string, no match  
    } ifelse  
  } {  
    (Failed to find a %%BoundingBox comment) ==  
    exit                            %% No more data, exit loop  
  } ifelse  
} loop  

SourceFile closefile            %% close the file  

FoundBox {  
  (LLx = ) print LLx ==  
  (LLy = ) print LLy ==  
  (URx = ) print URx ==  
  (URy = ) print URy ==  
  > Oldsetpagedevice  
  LLx neg LLy neg translate  
  SourceFileName run  
} if  

2pass.ps

这是用于您当前工作的方式,它比1pass.ps有两个优势:

  1. 适用于PDF文件和PostScript文件,以及不包含%%BoundingBox注释的文件。
  2. BoundingBox准确无误。
  3. 它的缺点是你必须处理每个文件两次,一次是获取边界框,一次是创建PDF文件。

    这需要两个参数,包含bbox设备输出的文件名,以及要转换的文件的名称。再次,使用您发送的文件,您将使用它:

    第一个命令:

      gs \
       -sDEVICE=bbox \
        classic.ps 2> bounding.txt
    

    第二个命令:

      gs \
       -sDEVICE=pdfwrite \
       -sBoxFileName=bounding.txt \
       -sPostScriptFileName=classic.ps \
       -o out.pdf \
        2pass.ps
    

    classic.ps的PostScript代码:

    %!PS  
    
    %% redefine setpagedevice to prevent changes by the PostScript program  
    %% But keep a copy under a different name, so we cna use it.  
    /Oldsetpagedevice /setpagedevice load def  
    /setpagedevice {pop} bind def  
    
    (Bounding Box parameters in file ) print BoxFileName ==  
    (File to process is ) print PostScriptFileName ==  
    
    /BoxFile BoxFileName (r) file def  
    /BoxString 256 string def  
    /HiResBoxString 256 string def  
    /LLx 0 def  
    /LLy 0 def  
    /URx 0 def  
    /URy 0 def  
    
    BoxFile BoxString readline  % Read first line from file  
    {  
      /BoxString exch def       % redefine string to be the one we read  
    }{  
      (Encountered EOF before newline reading %%BoundingBox) == flush  
    } ifelse  
    
    BoxFile HiResBoxString readline % Read first line from file  
    {  
      /HiResBoxString exch def      % redefine string to be the one we read  
    }{  
      (Encountered EOF before newline reading %%HiResBoundingBox) == flush  
    } ifelse  
    
    BoxFile closefile               % close the file  
    
    BoxString (%%BoundingBox:) anchorsearch  
    {  
      pop                       % Get rid of the mathcing string  
      token {                   % read a PostScript token  
        /LLx exch def               % Assume its a number  
        token {  
          /LLy exch def  
          token {  
            /URx exch def  
            token {  
              /URy exch def  
              pop                       % Get rid of any remaining string data  
            }{  
              (Failed to read a number from the string) ==  
            } ifelse  
          }{  
            (Failed to read a number from the string) ==  
          } ifelse  
        }{  
          (Failed to read a number from the string) ==  
        } ifelse  
      } {  
        (Failed to read a number from the string) ==  
      } ifelse  
    }{  
      print (does not contain a BoundingBox) ==  
    } ifelse  
    
    (LLx = ) print LLx ==  
    (LLy = ) print LLy ==  
    (URx = ) print URx ==  
    (URy = ) print URy ==  
    
    > Oldsetpagedevice  
    LLx neg LLy neg translate  
    
    PostScriptFileName run  
    

答案 2 :(得分:2)

  

“我过去通过组合Ghostscript gswin32c.exe和Calibre pdfmanipulate.exe获得了成功。这可能是一个   许多人熟悉这种方法。“

我不确定你为什么使用2个应用程序,应该可以只使用Ghostscript执行整个转换。

  

“我升级到64位gswin64c.exe后出现了一个问题”

您还没有说出问题所在,您是否将其报告为错误?如果人们不报告错误,他们就不会得到修复......

您似乎在PostScript程序和评论之间存在一些混淆。 PostScript程序中以'%'开头的任何行都是注释,并且对程序的操作具有 no 效果。因此,BoundingBox注释根本不会做任何事情。

也就是说,有一个约定(Document Structure Convention,简称DSC),它描述了在DSC处理器可以使用的PostScript文件中嵌入注释的方法。有一些规则描述了如何构建程序以使其工作。如果一个PostScript程序开始%!PS-Abode-m.n,其中m和n是整数,那么它声明自己是一个符合DSC的程序,它所遵循的版本是数字'm.n'。在这种情况下,PostScrip tinterpreter不会使用BoundingBox注释,但DSC处理器可能会使用它。请注意,不要求它是文件的第二行.....

一般情况下,Ghostscript会忽略DSC注释,但是如果将ProcessDSC设置为true,那么它将非常有限地使用它(主要是用于设置页面大小的BoundingBox注释)。

继续前进。您说您正在使用LaTeX ps2pdf,如果您已经有一个PostScript文件,您可以将其发送到Ghostscript以转换为PDF。我不清楚在这种情况下你究竟使用Ghostscript是什么,只是为了找到页面的真正边界框?

我不清楚你的'无损'裁剪是什么意思,如果你裁剪的内容你必须清楚地丢失一些东西,即使它只是空白......

如果您知道要裁剪的大小,它很容易在一次传递中进行转换,如果您不知道大小,那么您可以仅使用Ghostscript在2次传递中通过首先提取BoundingBox来完成完成了。这将获得4个数字,边界框的左下角和右上角(如果我没记错的话)。然后你创建一个'翻译' PostScript操作将页面内容向下和向左移动(使其从0,0左下角开始)。您还可以创建页面设备请求来设置页面大小,大小由width = right - left和height = top - bottom给出。将原始文件与PostScript操作符一起提供给Ghostscript并选择pdfwrite设备,您将获得一个PDF文件。

就边界框而言,它可能是一个错误,或者可能是文件制作标记,可能使用外部位置的白色墨水。在这种情况下,边界框设备仍将其视为页面内容的一部分。您可能会看到它不是,但设备不能。考虑页面是否首先填充了深色背景,并使用白色墨水勾勒出内容。

如果不了解更多关于您想要做的事情,并且理想地看到一个或多个有问题的文件,我无法提供全面的解决方案。

答案 3 :(得分:2)

可以在DSC上简单介绍一下,关于Ghostview的观点是正确的,但Ghostview是:

  1. 不是Ghostscript的一部分(虽然可能令人惊讶)
  2. DSC感知应用程序。
  3. 我的评论适用于PostScript语言,意思是解释Ghostscript忽略这些评论的原因。

    关于“第二次要求评论”的观点;它必须存在(对于DSC符合性),它不一定是第二行。虽然听到一些应用程序错误地要求,但我并不会感到惊讶。

    作为一般规则,Ghostscript的pdfwrite PDF输出设备不会将任何内容转换为栅格。有一些罕见的例外,通常涉及不寻常的字体类型或颜色空间,或者在支持透明度之前将具有透明度的PDF转换为PDF版本(例如PDF / A或PDF / X)。

    创建一个PDF文件,根据需要从Ghostscript中裁剪:

     gswin32c ^
      -o out.pdf ^
      -sDEVICE=pdfwrite ^
      -dPAGEWIDTHPOINTS=xx -dPAGEHEIGHTPOINTS=yy ^
      -dFIXEDMEDIA ^
      -c "-x -y translate" ^
      -f input.ps
    

    如果PostScript文件尚未包含此信息,则必须从先前调用的返回BoundingBox计算xx,yy,x和y。鉴于你上面说的话,情况似乎就是这样。

    在一般情况下:

    • xx = urx - llx,
    • yy = ury - lly,
    • x = llx,
    • y = lly

    更好的解决方案可能是编写一个PostScript程序来进行设置,这很容易做到。

    您可以将文件通过电子邮件发送至'ken.sharp AT artifex.com',或使用任何方便的文件传输工具向我发送URL。我最感兴趣的是返回的BoundingBox不是你所期望的......

    我确实查看了上面发布的网址,但我看不到有人描述64位版本的Ghostscript问题。作为最后一个问题,您使用的是哪个版本的Ghostscript?

答案 4 :(得分:0)

关于caliber / pdfmanipulate.exe的答案

caliber已从最近的版本中删除了pdfmanipulate.exe。我发现我必须回到版本0.8.66才能获得pdfmanipulate,我下载了便携版本:calibre-portable-0.8.66.zip