使用 Ghostscript,如何将页码放在合并的 PDF 文件上

Using Ghostscript, How Do I Put Page Numbers on Combined PDF Files

在相当锁定的裸机 Linux 机器上仅使用 Ghostscript,我需要将三个现有 PDF 组合成一个新 PDF,在新 PDF 的每一页上放置一个静态页眉,放置一个静态页脚在新 PDF 的每一页上,并为每一页编号(例如 "Page 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 的每一页上显示递增的页码。

在 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 过程时,堆栈上有两个数字,最上面的数字是 'reason code',下一个数字是上一个 showpage 执行的计数。现在您会认为(合理地)您可以将该计数用于您的页数,但不幸的是它在每次 'setpagedevice' 调用时重置为 0,并且 PDF 解释器在每一页上调用 setpagedevice,因为它可能用于每一页一个不同大小的 PDF 文件,setpagedevice 是我们改变页面大小的方法。

当我们从 EndPage 过程 return 时,我们必须在堆栈上压入一个布尔值,它是 'true'(将页面发送到输出)或 'false'(将其丢弃什么都不做)。

因此,您的过程会测试原因代码以查看调用 EndPage 的原因。如果它不是“2”(设备正在停用),那么它要么是复制页面,要么是显示页面,因此您可以在页面上绘制所需的添加内容。如果它是 2 那么我们只弹出页面计数和 return 'false' 这样我们就不会尝试发出额外的页面。

如果它是 2,那么您将颜色设置为 RGB 黑色(您可以改为 0 setgray)找到字体 NimbusSans-Bold,将其缩放为 24 点并将其设置为当前字体。然后移动到位置 x=240, y = 2(0,0 是左下角,单位是点,1/72 英寸)并绘制文本 'Static Footer'(注意括号是 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]

这个程序基本一样,只是把变量存储在global VM中的一个dicitonary,存储在globaldict中,为了打败save/restore.

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

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