Ghostscript用于PDF:如何将4页适配到1页?

3

我需要使用Ghostscript将多个PDF文件合并成一个PDF文件。我需要你帮助我确定要使用的命令,以便编辑输出页面,使其适合将输入文件的4页合并为输出文件的1页。

目前我使用的命令如下:

gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite \
   -sOutputFile=ALL.pdf plot_1.pdf plot_n.pdf

输入文件为A4横向格式,输出的格式必须保持一致。

感谢大家,Cristian


请注意,我被迫使用Ghostscript——因为我正在公司的服务器上执行此操作,可能无法访问所有选项/文件(但我确实有Ghostscript)。谢谢。 - Cristian
你的命令本来也不会起作用,因为你在“=”字符周围留有空格... - Kurt Pfeifle
2个回答

3
这是我之前写的一个程序,用于从PDF文件中进行2-up排版。这不是通用的PostScript程序,它只能与Ghostscript一起使用(因为它利用了一些GS内部功能),并且只能在输入为PDF而非PostScript时使用。
您需要修改它以进行4-up排版。不要担心可怕的许可证问题,那只是因为我从其他地方复制了样板。
您可能想假设所有页面的大小和方向都相同,不像这段代码允许不同的方向。
%!PS
% Copyright (C) 2011 Artifex Software, Inc.  All rights reserved.
% 
% This software is provided AS-IS with no warranty, either express or
% implied.
% 
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
% 
% For more information about licensing, please refer to
% http://www.ghostscript.com/licensing/. For information on
% commercial licensing, go to http://www.artifex.com/licensing/ or
% contact Artifex Software, Inc., 101 Lucas Valley Road #110,
% San Rafael, CA  94903, U.S.A., +1(415)492-9861.
%
% Make a PDF file '2-up'
% This program deliberately does NOT attempt to preserve metadata
% such as DEST links, bookmarks and so forth as these will be
% mostly incorrect after imposition.
%
% usage: gs -dNODISPLAY -sFile=____.pdf [-dVerbose] 2-up.ps

%
% Make, and open, a working dictionary to store stuff
%
/PDF_2UPDict 20 dict dup begin def  

%
% Check the parameters to see they are present and of the correct type
%

/Usage {
  (  usage: gs -dNODISPLAY -q -sFile=____.pdf [-dVerbose] 2-up.ps\n) =
  flush
  quit
} bind def

/File where not {
  (\n   *** Missing source file. \(use -sFile=____.pdf\)\n) =
  Usage
} {
  pop
}ifelse

/Verbose where not {
  /Verbose false def
}{
 pop /Verbose true def
} ifelse

%%
%% This code is copied from pdf_main.ps, pdfshowpage_finish
%% sadly that routine always calls showpage, and we want that
%% to be under our control, so we have to duplicate the code
%% here. Not only that but it uses GS extensions which aren't
%% available outside of startup, so some things it simply can't
%% replicate. As a result some of the error handling is less
%% good.
%%
%% I plan to extend the PDF interpreter with two new
%% routines, pdfnoshowpage_finish and then have both
%% that and pdfshowpage_finish call pdfoptionalshowpage_finish
%% which will take a boolean determining whether to actually
%% call the showpage. At that time we'll alter this code.
%%
/draw_page_content {    % <pagedict> pdfshowpage_finish -
   save /PDFSave exch store
   /PDFdictstackcount countdictstack store
   /PDFexecstackcount count 2 sub store
   (before exec) VMDEBUG

   % set up color space substitution (this must be inside the page save)
   pdfshowpage_setcspacesub

        % Display the actual page contents.
   8 dict begin
   /BXlevel 0 def
   /BMClevel 0 def
   /OFFlevels 0 dict def
   /BGDefault currentblackgeneration def
   /UCRDefault currentundercolorremoval def
        %****** DOESN'T HANDLE COLOR TRANSFER YET ******
   /TRDefault currenttransfer def
  matrix currentmatrix 
  2 dict
  dictbeginpage setmatrix
  /DefaultQstate qstate store

  count 1 sub /pdfemptycount exch store
        % If the page uses any transparency features, show it within
        % a transparency group.
  dup pageusestransparency dup /PDFusingtransparency exch def {
    % Show the page within a PDF 1.4 device filter.
    0 .pushpdf14devicefilter {
      /DefaultQstate qstate store       % device has changed -- reset DefaultQstate
      % If the page has a Group, enclose contents in transparency group.
      % (Adobe Tech Note 5407, sec 9.2)
      dup /Group knownoget {
        1 index /CropBox pget {
          /CropBox exch
        } {
          1 index get_media_box pop /MediaBox exch
        } ifelse
        oforce_elems normrect_elems fix_empty_rect_elems 4 array astore .beginformgroup 
        showpagecontents
        .endtransparencygroup
      } {
        showpagecontents
      } ifelse
    } stopped {
      % abort the transparency device 
      .abortpdf14devicefilter
      /DefaultQstate qstate store   % device has changed -- reset DefaultQstate
      stop
    } if .poppdf14devicefilter
    /DefaultQstate qstate store % device has changed -- reset DefaultQstate
  } {
    showpagecontents
  } ifelse
  .free_page_resources
  % todo: mixing drawing ops outside the device filter could cause
  % problems, for example with the pnga device.

  end           % scratch dict
  % Some PDF files don't have matching q/Q (gsave/grestore) so we need
  % to clean up any left over dicts from the dictstack

  PDFdictstackcount //false
  { countdictstack 2 index le { exit } if
    currentdict /n known not or
    end
  } loop 

  pop
  count PDFexecstackcount sub { pop } repeat
  Repaired      % pass Repaired state around the restore
  PDFSave restore
  currentglobal pdfdict gcheck .setglobal
  .setglobal
  /Repaired exch def
} bind def

%%
%% First we open the PDF file
%%
File dup (r) file runpdfbegin pop
process_trailer_attrs

%%
%% FInd out how many pages are in teh PDF file
%%
/PDFPageCount pdfpagecount def

Verbose {(PageCount is ) print PageCount ==} if

%
% Set up our bookkeeping
%
% First get the size of the page from page 1 of the PDF file
% We assume that all PDF pages are the same size.
%
1 pdfgetpage get_any_box 
exch pop dup 2 get exch 3 get
/PDFHeight exch def
/PDFWidth exch def
PDFWidth PDFHeight gt {
/PDFLandscape true def
}{
/PDFLandscape false def
}ifelse

Verbose{
(PDFHeight is ) print PDFHeight ==
(PDFWidth is ) print PDFWidth ==
(PDFLandscape is ) print PDFLandscape ==
} if

%
% Now get the page size of the current device. We will fit
% the PDF pages onto this using rotation and scaling as
% required.
%
currentpagedevice /PageSize get
dup 0 get /PageWidth exch def
1 get /PageHeight exch def
PageWidth PageHeight gt {
/PageLandscape true def
}{
/PageLandscape false def
}ifelse

Verbose{
(PageHeight is ) print PageHeight ==
(PageWidth is ) print PageWidth ==
(PageLandscape is ) print PageLandscape ==
} if

%
% Now figure out how best to fit the pages 2-up
%
PageLandscape PDFLandscape and {
  %% Both landscape
  /ScaleY PageHeight PDFWidth div
  /ScaleX PageWidth 2 div PDFHeight div
  ScaleX ScaleY lt {
    /Scale ScaleX def
  } {
    /Scale ScaleY def
  }ifelse
  /Rotate 90
  /OriginYTx PDFHeight neg def
  /OriginXTx 0 def
  /PageYTx PDFHeight def
  /PageXTx 0 def
}{
  PageLandscape {
    %% Page is landscape, PDF is portrait
    /ScaleY PageHeight PDFHeight div def
    /ScaleX PageWidth 2 div PDFWidth div def
    ScaleX ScaleY lt {
      /Scale ScaleX def
    } {
      /Scale ScaleY def
    }ifelse
    /Rotate 0 def
    /OriginXTx 0 def
    /OriginYTx 0 def
    /PageXTx PDFWidth def
    /PageYTx 0 def
  }{
    PDFLandscape {
      %% PDF is landscape, Page is portrait
      /ScaleY PageHeight 2 div PDFHeight div def
      /ScaleX PageWidth PDFWidth div def
      ScaleX ScaleY lt {
        /Scale ScaleX def
      } {
        /Scale ScaleY def
      }ifelse
      /Rotate 0 def
      /OriginXTx 0 def
      /OriginYTx PDFHeight def
      /PageXTx 0 def
      /PageYTx PDFHeight neg def
    } {
      %% Both portrait
      /ScaleY PageHeight 2 div PDFWidth div def
      /ScaleX PageWidth PDFHeight div def
      ScaleX ScaleY lt {
        /Scale ScaleX def
      } {
        /Scale ScaleY def
      }ifelse
      /Rotate 90 def
      /OriginXTx 0 def
      /OriginYTx PDFHeight neg def
      /PageYTx 0 def
      /PageXTx PDFWidth def
    } ifelse
  }ifelse
} ifelse

Verbose{
(ScaleX is ) print ScaleX ==
(ScaleY is ) print ScaleY ==
(Scale is ) print Scale ==
(Rotate is ) print Rotate ==
(OriginXTx is ) print OriginXTx ==
(OriginYTx is ) print OriginYTx ==
(PageXTx is ) print PageXTx ==
(PageYTx is ) print PageYTx ==
} if

%
% Starting at 0, count by 2, and stop at the last page, however
% account for the fact that the number of pages in the PDF
% file may not be even, in which case draw a final empty
%page
%
0 2 PDFPageCount 1 sub PDFPageCount 2 mod add {
                                                             %% loop counter
  save                                                       %% save the state
    exch                                                     %% exch the save state and the loop counter
    Rotate rotate                                            %%
    Scale Scale scale                                        %% set up our calculated CTM
    OriginXTx OriginYTx translate                            %%
    save                                                     %% and save this too
      exch                                                   %% swap the save state and the loop counter
      dup 1 add                                              %% copy the loop counter, then add 1, stack: -save- -save- loop loop+1
      Verbose {(Drawing page ) print dup ==} if
      0 0 PageWidth PageHeight rectclip                      %% clip the page contents to the page size (in case of bleeds)
      pdfgetpage                                             %% get the page from the PDF file, stack: -save- -save- loop -dict-
      dup /Page exch store                                   %% save a copy of the page dict inside itself
      pdfshowpage_init                                       %% initialise the page
      draw_page_content                                      %% se above, draws the graphical objects, stack -save- -save- loop
      exch                                                   %% swap back teh save object, stack: -save- loop -save-
    restore                                                  %% restore back to our calculated CTM
    PageXTx PageYTx translate                                %% Move to draw page 2
    2 add dup PDFPageCount gt {                              %% If we have to draw an extra page, and this is it
      Verbose {(Drawing extra page ) print dup ==} if
      pop showpage                                           %% pop the spare loop and draw an empty page, stack: -save-
    }{
      Verbose {(Drawing page ) print dup ==} if
      0 0 PageWidth PageHeight rectclip
      pdfgetpage
      dup /Page exch store
      pdfshowpage_init     % <pagedict>
      draw_page_content
      showpage
    } ifelse
  restore                                                    %% restore back to the original CTM
} for

//runpdfend exec                                             %% End the PDF file

end                                                          %% our working dictioanry

非常感谢。我今天会尝试一下。Cristian - Cristian
很棒。正是我在寻找的。谢谢分享! - ulidtko

-1

使用Ghostscript本身无法实现您想要的目标。没有内置参数可以在输出中进行2-up或4-up页面的排版。

但是,可以编写一个PostScript程序来实现这一点,然后将此程序与原始PDF一起提供给Ghostscript,然后它可以输出结果。

我记得@KenS(他是Ghostscript开发人员,可能很快也会注意到这个问题)以前发布过类似的PostScript程序。如果您在这里搜索(或使用类似的参数),可能可以找到它。


你可能可以使用ghostpdl/gs/lib/gsnup.ps,但我实际上还没有尝试过。 - KenS
最简单的方法是什么?使用ghostpdl/gs/lib/gsnup.ps(第一种选择)还是将pdf文件转换为ps(使用ghostscript?),然后在ps中重新编辑页面,最后将最终版本保存为pdf(第二种选择)? - Cristian
@KenS:啊,我忘了gsnup.ps......无论如何,运行 gs -o out.pdf -sDEVICE=pdfwrite gsnup.ps -f in.pdf 会导致缩小页面内容,其中内容被推到右下象限。所以它进行了缩放,但没有nup。 - Kurt Pfeifle

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