如何使用 Ghostscript 将具有自己覆盖背景的页码添加到 PDF?

How to add page numbers with their own overlay background to PDF using Ghostscript?

我想找到一种方法如何在具有杂色背景的 PDF 页面上添加叠加页码。 问题是,如果我使用像这样的常规方法:

(pagecount.ps)

globaldict
/MyPageCount 1 put

<<
   /EndPage
   {
     exch pop 0 eq dup
     {
       /Helvetica 12 selectfont
       MyPageCount =string
       cvs dup stringwidth pop
       currentpagedevice

       /PageSize get 0 get exch
       sub 460 sub 810
       moveto show
       globaldict

       /MyPageCount MyPageCount 1 add put
     } if
   } bind
>> setpagedevice

然后gs -dNOSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -o output.pdf -sDEVICE=pdfwrite -f pagecount.ps input.pdf

它可以工作,但由于页面上的杂色背景,现在几乎看不到以前在空白页面上看到的页码。

所以我想在数字周围绘制一些白色的小基板 以掩盖它们在页面上占据的区域,但数字本身是可见的

一个想法是使用注释 \Rect:

(pagecount.ps, 最初取自 How to add page numbers to Postscript/PDF)

globaldict
/MyPageCount 1 put

<<
   /EndPage
   {
     newpath [
       /Rect
       [ 20 dup moveto (Link on page1) false charpath pathbbox
       2 add 4 1 roll 2 add 4 1 roll 2 sub 4 1 roll 2 sub 4 1 roll
     newpath ]

     exch pop 0 eq dup
     {
       /Helvetica 12 selectfont
       MyPageCount =string
       cvs dup stringwidth pop
       currentpagedevice

       /PageSize get 0 get exch
       sub 460 sub 810
       moveto show
       globaldict

       /MyPageCount MyPageCount 1 add put
     } if
   } bind
>> setpagedevice

但它只生成一个白页。

如何使用 Ghostscript 在 PDF 页面上绘制带有自己小背景的页码?

UPD: 我根据建议更新了 pagecount.ps,现在我在正确的位置有了正确的小背景,但页码停止绘制 (Error: /nocurrentpoint in /--.endpage--)。

pagecount.ps:

globaldict
/MyPageCount 1 put

<<
   /EndPage
   {
     exch pop 0 eq dup
     {
       /Helvetica 12 selectfont
       MyPageCount =string
       cvs dup stringwidth pop
       currentpagedevice

       /PageSize get 0 get exch
       sub 460 sub 810
       moveto                              % move to text drawing position

       % lines new to pagecount.ps

       dup                                 % duplicate string on the stack
       true charpath flattenpath pathbbox  % consume the string and put coordinates of bounding box to stack

       newpath                             % start drawing bounding box
       3 index 3 index moveto              % copy llx and lly to the top of stack and move to them
       3 index 1 index lineto              % copy llx and ury to the top of stack and draw line to them
       1 index 1 index lineto              % copy urx and ury to the top of stack and draw line to them
       1 index 3 index lineto              % copy urx and lly to the top of stack and draw line to them
       3 index 3 index lineto              % copy llx and lly to the top of stack and draw line to them
       closepath

       pop pop pop pop                     % remove coordinates of bounding box from stack

       gsave
       1 0 0 setrgbcolor
       fill
       grestore
       stroke

       % end of new lines

show
       globaldict

       /MyPageCount MyPageCount 1 add put
     } if
   } bind
>> setpagedevice

UPD 2: 我更新了 pagecount.ps 以修复 (Error: /nocurrentpoint in /--.endpage--)。我还删除了填充矩形后的 stroke 命令。 现在我有 Error: /typecheck in /--.endpage-- Operand stack: 0 true 595 (1)

pagecount.ps:

globaldict
/MyPageCount 1 put

<<
   /EndPage
   {
     exch pop 0 eq dup
     {
       /Helvetica 12 selectfont
       MyPageCount =string
       cvs dup stringwidth pop
       currentpagedevice

       /PageSize get 0 get exch
       sub 460 sub 810
       moveto                              % move to text drawing position

       % lines new to pagecount.ps

       dup                                 % duplicate string on the stack
       true charpath flattenpath pathbbox  % consume the string and put coordinates of bounding box to stack

       newpath                             % start drawing bounding box
       3 index 3 index moveto              % copy llx and lly to the top of stack and move to them
       3 index 1 index lineto              % copy llx and ury to the top of stack and draw line to them
       1 index 1 index lineto              % copy urx and ury to the top of stack and draw line to them
       1 index 3 index lineto              % copy urx and lly to the top of stack and draw line to them
       3 index 3 index lineto              % copy llx and lly to the top of stack and draw line to them
       closepath

       pop pop pop pop                     % remove coordinates of bounding box from stack

       gsave
       1 0 0 setrgbcolor
       fill
       grestore

       % end of new lines


       currentpagedevice

       /PageSize get 0 get exch
       sub 460 sub 810
       moveto show
       globaldict

       /MyPageCount MyPageCount 1 add put
     } if
   } bind
>> setpagedevice

终于成功了。

我制作了 2 个脚本变体。第一个只是页码,其中数字背景模糊,因此杂乱无章的 pdf 内容不会降低数字的可读性。

一些说明: sub 460 sub 710以下与页码文本框有关,分别代表x和y坐标。

1 0 0 setrgbcolor fill 与文本框覆盖背景颜色(此处为红色)相关。

1 0 0 setrgbcolor 5 setlinewidth 与文本框覆盖轮廓颜色及其宽度相关

0 0 0 setrgbcolor 与数字颜色相关

/MyPageCount 16 put 与起始页码相关

见:

globaldict
/MyPageCount 16 put

<<
   /EndPage
   {
     exch pop 0 eq dup
     {

/Helvetica 12 selectfont

MyPageCount =string
       cvs
  dup stringwidth pop
       currentpagedevice


       /PageSize get 0 get exch
       sub 460 sub 710


       moveto                              % move to text drawing position

       % lines new to pagecount.ps

       dup                                 % duplicate string on the stack


       true charpath flattenpath pathbbox  % consume the string and put coordinates of bounding box to stack


       newpath                             % start drawing bounding box
       3 index 3 index moveto              % copy llx and lly to the top of stack and move to them
       3 index 1 index lineto              % copy llx and ury to the top of stack and draw line to them
       1 index 1 index lineto              % copy urx and ury to the top of stack and draw line to them
       1 index 3 index lineto              % copy urx and lly to the top of stack and draw line to them
       3 index 3 index lineto              % copy llx and lly to the top of stack and draw line to them
       closepath

                            % remove coordinates of bounding box from stack

       gsave

       1 0 0 setrgbcolor


       fill grestore 1 0 0 setrgbcolor
 5 setlinewidth stroke
pop pop pop pop

       0 0 0 setrgbcolor


dup stringwidth pop
       currentpagedevice

       /PageSize get 0 get exch
       sub 460 sub 710
       moveto



       % end of new lines


 show

       globaldict

       /MyPageCount MyPageCount 1 add put
     } if
   } bind
>> setpagedevice

第二种是编号方案,其中数字有辅助文本,并且都有模糊的背景。

参见:

userdict begin
%%EndProlog

%%BeginSetup
% The following encodes a few useful Unicode glyphs, if only a few are needed.
% Based on 
% Usage: /Times-Roman /Times-Roman-Uni UniVec new-font-encoding

/new-font-encoding { <<>> begin
    /newcodesandnames exch def
    /newfontname exch def
    /basefontname exch def
    /basefontdict basefontname findfont def     % Get the font dictionary on which to base the re-encoded version.
    /newfont basefontdict maxlength dict def    % Create a dictionary to hold the description for the re-encoded font.
    basefontdict
        { exch dup /FID ne                      % Copy all the entries in the base font dictionary to the new dictionary except for the FID field.
            { dup /Encoding eq
                { exch dup length array copy    % Make a copy of the Encoding field.
                    newfont 3 1 roll put }
                { exch newfont 3 1 roll put }
                ifelse
            }
            { pop pop }                         % Ignore the FID pair.
            ifelse
        } forall
    newfont /FontName newfontname put           % Install the new name.
    newcodesandnames aload pop                  % Modify the encoding vector. First load the new encoding and name pairs onto the operand stack.
    newcodesandnames length 2 idiv
        { newfont /Encoding get 3 1 roll put}
        repeat                                  % For each pair on the stack, put the new name into the designated position in the encoding vector.
    newfontname newfont definefont pop          % Now make the re-encoded font description into a POSTSCRIPT font.
                                                % Ignore the modified dictionary returned on the operand stack by the definefont operator.
end} def

/Helvetica /Helvetica-Uni [
    16#43  /afii08941        % ASCII 43 = C
    16#44  /club             % ASCII 44 = D
    16#45  /afii00208        % ASCII 45 = E


] new-font-encoding

/Helv
<<
   /FontType 0
   /FontMatrix [ 1 0 0 1 0 0 ]
   /FDepVector [
      /Helvetica findfont        % this is Font0
      /Helvetica-Uni findfont    % this is Font1
      ]
   /Encoding [ 0 1 ]
   /FMapType 3
>> definefont pop

globaldict
/MyPageCount 16 put



<<
   /EndPage
   {
     exch pop 0 eq dup
     {

/Helv 12 selectfont

(7[=11=]1C7[=11=]07[=11=]1D7[=11=]07[=11=]1E7[=11=]0Page )
MyPageCount =string
       cvs concatstrings
  dup stringwidth pop
       currentpagedevice


       /PageSize get 0 get exch
       sub 460 sub 710


       moveto                              % move to text drawing position

       % lines new to pagecount.ps

       dup                                 % duplicate string on the stack


       true charpath flattenpath pathbbox  % consume the string and put coordinates of bounding box to stack


       newpath                             % start drawing bounding box
       3 index 3 index moveto              % copy llx and lly to the top of stack and move to them
       3 index 1 index lineto              % copy llx and ury to the top of stack and draw line to them
       1 index 1 index lineto              % copy urx and ury to the top of stack and draw line to them
       1 index 3 index lineto              % copy urx and lly to the top of stack and draw line to them
       3 index 3 index lineto              % copy llx and lly to the top of stack and draw line to them
       closepath

                            % remove coordinates of bounding box from stack

       gsave

       1 0 0 setrgbcolor


       fill grestore 1 0 0 setrgbcolor
 5 setlinewidth stroke
pop pop pop pop

       0 0 0 setrgbcolor


dup stringwidth pop
       currentpagedevice

       /PageSize get 0 get exch
       sub 460 sub 710
       moveto



       % end of new lines


 show

       globaldict

       /MyPageCount MyPageCount 1 add put
     } if
   } bind
>> setpagedevice

如您所见,我可以插入 [₤♣―第 16 页](通过使用 /afii08941、/club、/afii00208),其中 [...16] 在每个页面上按升序自动生成。

我使用了特殊符号,从 userdict begin>> definefont pop 的行就是为了这个目的。如果页码中不需要特殊符号,您可以删除这些行。

这里是 table 符号,用于将一些助记符、拉丁文、希腊文、西里尔文和阿拉伯文符号插入 Postscript 输出(在我的例子中是 PDF) https://root.cern/doc/v622/AdobeGlyphList_8h_source.html

特殊符号也需要remapping scheme,你得给它写个定义:

/Helvetica /Helvetica-Uni [
    16#43  /afii08941        % ASCII 43 = C
    16#44  /club             % ASCII 44 = D
    16#45  /afii00208        % ASCII 45 = E

然后你可以将特殊符号放在 Postscript 堆栈的顶部,并将它们与页码计数器连接起来:

(7[=13=]1C7[=13=]07[=13=]1D7[=13=]07[=13=]1E7[=13=]0Page )
MyPageCount =string
       cvs concatstrings

7[=88=]1C7[=89=]0变成₤,7[=88=]1D7[=89=]0变成了♣。 CD 粗体。

注意 gsave/grestore 在这里是如何工作的。

KenS 在评论中问我为什么在脚本中使用 stroke。 事实证明,我的页码看起来像这样没有笔画:

也就是说,在这种情况下,模糊背景是 close-fitting 并且数字本身和杂色 PDf 之间没有边距,并且数字在某些点上似乎与暗杂色的东西交织在一起。

所以 stroke 在我们的辅助文本和数字周围画了一个边框。在我的例子中,边框与辅助背景具有相同的颜色(红色),因此它们看起来更具可读性。

但是如果去掉gsave/grestorestroke将不会生效,生成的图片看起来和之前的一样。 5 setlinewidth 设置 5 磅的宽度。

P.S.

寻找原始答案脚本错误的关键是使用调试 pstack 运算符。

确定最后一个工作版本然后在其第一行后插入pstack并修改脚本以检查一些有风险的操作符。如果有效,则将 pstack 移到第 2 行之后并添加另一个有风险的运算符。一旦您的脚本被破坏,比较 pstack 工作版本和 non-workings 脚本版本的输出。