首页 > 解决方案 > 使用 Ghostscript,如何在合并的 PDF 文件上添加页码

问题描述

在相当锁定的裸机 Linux 机器上仅使用 Ghostscript,我需要将三个现有 PDF 组合成一个新 PDF,在新 PDF 的每一页上放置一个静态页眉,在新 PDF 的每一页上放置一个静态页脚,并且为每一页编号(例如“第 1257 页”)。

我有前三个问题的解决方案。开箱即用的 Ghostscript 可以轻松地将多个 PDF 合并为一个新的 PDF。在 -c 命令行选项中修改 PostScript,我可以将静态页眉和页脚添加到新 PDF。我还不能做的是让它把页码放在新的 PDF 上。

这是我现在拥有的复杂命令行:

gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=final.pdf -c "<</EndPage {2 ne {200 0 0 setrgbcolor /NimbusSans-Bold 24 selectfont 240 2 moveto (Static Footer) show 240 773 moveto (Static Header) show 0 0 0 setrgbcolor /NimbusSans-Regular 12 selectfont 2 24 moveto (Page ) show true}{pop false} ifelse} >> setpagedevice" -f title.pdf parta.pdf partb.pdf

删除静态页眉和页脚部分会得到一个稍微简单的命令行:

gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=final.pdf -c "<</EndPage {2 ne {0 0 0 setrgbcolor /NimbusSans-Regular 12 selectfont 2 24 moveto (Page ) show true}{pop false} ifelse} >> setpagedevice" -f title.pdf parta.pdf partb.pdf

我尝试了很多方法来显示页码,但它们要么使 Ghostscript 崩溃,要么只是在每页上显示相同的页码。唯一的其他复杂情况是新的 PDF 将在 1,000 到 2,000 页之间。

我需要的是一个很好的代码示例,说明如何使 PostScript 在 PDF 的每一页上显示递增的页码。

标签: pdfghostscript

解决方案


在 PostScript 中计算页数并不难。我希望您已经知道以下大部分内容,但是为了以后遇到此问题的其他人,我将采取一些小步骤。

我们首先创建一个我们自己的字典,我们可以在其中存储东西。我们确保这是在 'userdict' 中定义的,所以我们总能找到它(userdict,像 systemdict,总是可用的)。选择的名称应该是独一无二的,以防止任何其他 PostScript 或 PDF 程序覆盖它!

userdict begin                  %% Make userdict the current dictionary
/My_working_Dict                %% Create a name for our dict (choose something unique)
10 dict                         %% create a 10-element dictionary
def                             %% def takes a key and value and puts them in the current dictionary
My_working_Dict begin           %% make our working dictionary the current one
/PageCount                      %% Make a name for our counter
0                               %% 0, this will be the initial value
def                             %% define PageCount as 0 in the current dictionary
end                             %% close the current dictionary (My_working_Dict)
end                             %% close the current dictionary (userdict)

有更有效的方法可以做到这一点,但这是一种易于描述和遵循的方法。从这一点开始,直到我们关闭 PostScript 解释器(或将其恢复到较早的状态),userdict 将包含一个名为 My_working_Dict 的字典,该字典将具有一个名为 PageCount 的键。与 PageCount 关联的值最初将为 0,但我们可以更改它。

您已经定义了一个 EndPage 过程,如下所示:

<<
  /EndPage {
    2 ne {
      200 0 0 setrgbcolor
      /NimbusSans-Bold 24 selectfont
      240 2 moveto 
      (Static Footer) show
      240 773 moveto
      (Static Header) show
      0 0 0 setrgbcolor
      /NimbusSans-Regular 12 selectfont
      2 24 moveto
      (Page ) show
      true
    }
    {
      pop
      false
    } ifelse
  }
>> setpagedevice

现在,当调用 EndPage 过程时,堆栈上有两个数字,最上面的数字是“原因代码”,下一个数字是先前显示页面执行的计数。现在您会(合理地)认为您可以使用该计数作为您的页数,但不幸的是,它在每次“setpagedevice”调用时重置为 0,并且 PDF 解释器在每一页上调用 setpagedevice,因为它可能适用于 PDF 中的每一页文件的大小不同,而 setpagedevice 是我们更改页面大小的方式。

当我们从 EndPage 过程返回时,我们必须将一个布尔值压入堆栈,它要么是“真”(将页面发送到输出),要么是“假”(把它扔掉,什么也不做)。

因此,您的程序测试原因码以查看调用 EndPage 的原因。如果它不是“2”(设备被停用),那么它的复制页或展示页,所以你在页面上绘制你想要的添加。如果是 2,那么我们只需弹出页面数并返回“false”,这样我们就不会尝试发出额外的页面。

如果它是 2,那么您将颜色设置为 RGB 黑色(您可以这样做0 setgray)找到字体 NimbusSans-Bold,将其缩放到 24 磅并将其设置为当前字体。然后移动到位置 x=240, y = 2(0,0 位于左下角,单位是点,1/72 英寸)并绘制文本“静态页脚”(NB 括号是 PostScript 中的字符串分隔符)

然后移动到位置 x=240, y=773 并绘制文本“Static Header”。

然后你再次冗余设置颜色,你不需要这样做,它保持不变,直到你改变它,再次找到字体 NimbusSans-Bold,这次将它缩放到 12 点并选择它作为当前字体。最后,您移动到位置 x=2, y=24 并绘制文本“Page”。

因此,您需要做的就是扩展 EndPage 过程,以便它从我们的字典中获取页数,将其转换为字符串,并绘制结果字符串。

就像是 :

userdict begin                  %% Make userdict the current dictionary
/My_working_Dict                %% Create a name for our dict (choose something unique)
10 dict                         %% create a 10-element dictionary
def                             %% def takes a key and value and puts them in the current dictionary
My_working_Dict begin           %% make our working dictionary the current one
/PageCount                      %% Make a name for our counter
0                               %% 0, this will be the initial value
def                             %% define PageCount as 0 in the current dictionary
end                             %% close the current dictionary (My_working_Dict)
end                             %% close the current dictionary (userdict)

<<
  /EndPage {
    2 ne {
      0 0 0 setrgbcolor
      /NimbusSans-Bold 24 selectfont
      240 2 moveto 
      (Static Footer) show
      240 773 moveto
      (Static Header) show
      0 0 0 setrgbcolor
      /NimbusSans-Regular 12 selectfont
      2 24 moveto
      (Page ) show

      userdict /My_working_Dict get   %% get My_working_dict from userdict (leaves My_working_dict on the operand stack
      dup                             %% duplicate the dictionary reference
      /PageCount get                  %% get PageCount from the dictionary on the stack
      1 add                           %% add one to the count
      /PageCount                      %% put the key on the stack
                                      %% stack now holds << >> int /PageCount
                                      %% where << >> is a reference to My_working_Dict, int is our new value for PageCount, and /PageCount is the key name we are using
      exch                            %% swap the topmost two stack items
                                      %% stack is now << >> /PageCount int
      put                             %% puts the top two items on the stack into the dictionary which is third on the stack.

      256 string                      %% Temporary string to hold the count
      userdict /My_working_Dict get   %% get My_working_dict from userdict (leaves My_working_dict on the operand stack
      /PageCount get                  %% get PageCount from the dictionary on the stack
      exch
      cvs                             %% Convert the top object on the stack into a string, storing the result in the second object down, whic must be a string
      show                            %% draw the string on the page using the current colour and font.
      true
    }
    {
      pop
      false
    } ifelse
  }
>> setpagedevice

然后,您将使用以下命令执行 Ghostscript:

gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=final.pdf modifyPDF.ps title.pdf parta.pdf partb.pdf

现在我还没有真正尝试过这段代码,所以完全有可能出现错误。

[更新 2]

该程序基本相同,但将变量存储在全局 VM 中的字典中,存储在 globaldict 中,以防止保存/恢复。

globaldict begin                %% Make globaldict the current dictionary
currentglobal true setglobal    %% We need to make the VM allocation mode for the dictionary global
/My_working_Dict                %% Create a name for our dict (choose something unique)
10 dict                         %% create a 10-element dictionary
def                             %% def takes a key and value and puts them in the current dictionary
setglobal           %% put the VM allocation mode back
globaldict /My_working_Dict     %% Get the dictionary from globaldict
get begin                       %% make our working dictionary the current one
/PageCount                      %% Make a name for our counter
0                               %% 0, this will be the initial value
def                             %% define PageCount as 0 in the current dictionary
end                             %% close the current dictionary (My_working_Dict)
end                             %% close the current dictionary (globaldict)

<<
  /EndPage {
    2 ne {
      0 0 0 setrgbcolor
      /NimbusSans-Bold 24 selectfont
      240 2 moveto 
      (Static Footer) show
      240 773 moveto
      (Static Header) show
      0 0 0 setrgbcolor
      /NimbusSans-Regular 12 selectfont
      2 24 moveto
      (Page ) show

      globaldict /My_working_Dict get %% get My_working_dict from globaldict (leaves My_working_dict on the operand stack
      dup                             %% duplicate the dictionary reference
      /PageCount get                  %% get PageCount from the dictionary on the stack
      1 add                           %% add one to the count
      /PageCount                      %% put the key on the stack
                                      %% stack now holds << >> int /PageCount
                                      %% where << >> is a reference to My_working_Dict, int is our new value for PageCount, and /PageCount is the key name we are using
      exch                            %% swap the topmost two stack items
                                      %% stack is now << >> /PageCount int
      put                             %% puts the top two items on the stack into the dictionary which is third on the stack.
      globaldict /My_working_Dict get %% get My_working_dict from globaldict (leaves My_working_dict on the operand stack
      /PageCount get                  %% get PageCount from the dictionary on the stack
      256 string                      %% Temporary string to hold the count
      globaldict /My_working_Dict get %% get My_working_dict from globaldict (leaves My_working_dict on the operand stack
      /PageCount get                  %% get PageCount from the dictionary on the stack
      exch
      cvs                             %% Convert the top object on the stack into a string, storing the result in the second object down, whic must be a string
      show                            %% draw the string on the page using the current colour and font.
      true
    }
    {
      pop
      false
    } ifelse
  }
>> setpagedevice

我已经用补充的例子试过了,它对我有用。


推荐阅读