1

I want to find a way how to add overlay page numbers on PDF pages with motley background. The problem is that if I use a usual approach like this:

(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

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

It works but page numbers previously seen on blank pages are hardly visible now due to motley background stuff on pages.

So I want some little white substrate to be drawn around numbers to obscure the area they occupy on a page but with the numbers themselves being visible.

One idea was to use annotations with \Rect:

(pagecount.ps, originally taken from 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

But it produces only a white page.

How to make page numbers with their own little background be drawn on PDF pages using Ghostscript?

UPD: I updated pagecount.ps according to advices and now I have right little background in right place but page numbers stopped to get drawn (Error: /nocurrentpoint in /--.endpage--).

New 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: I updated pagecount.ps to fix (Error: /nocurrentpoint in /--.endpage--). I also removed stroke command afted filling the rectangle. Now I have Error: /typecheck in /--.endpage-- Operand stack: 0 true 595 (1)

New 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
bimjhi
  • 311
  • 2
  • 11
  • You cannot create an annotation in PDF by simply defining /Rect. If you want to add an annotation to a PDF file you will need to use the pdfmark operator. To be honest your EndPage procedure looks broken and I would expect it to throw an error, perhaps it does and that's why you are getting a white page. You can use 'charpath flattenpath pathbbox' to get the co-ordinates of a rectangle enclosing the text. Fill that with white and **then** draw the text. By the way '=string' is Ghostscript-specific, that won't work with any other PostScript interpreter. – KenS Apr 20 '22 at 12:37
  • @KenS Thank you for your comment, Ken. But I'm confused by what is the order of coordinates of `charpath flattenpath pathbbox` when they are put on stack. – bimjhi Apr 20 '22 at 17:05
  • The PostScript Language Reference Manual is your friend for all things PostScript and can be downloaded free. You will have to Google for it, Adobe keep moving it on their website It's a well written specification, all the operators are covered as well as all the graphics operations. The result of pathbbox is llx lly urx ury. – KenS Apr 20 '22 at 18:12
  • @KenS Thank you so much Ken! Now I have a bounding box drawn in right place where a number is supposed to appear. I got this result by duplicating the string on the stack, then by issuing `true charpath flattenpath pathbbox` and by drawing a rectangle. But now I have `Error: /nocurrentpoint in /--.endpage--` and numbers themselves are not drawn. My new pagecount.ps is updated in the first post. – bimjhi Apr 21 '22 at 11:15
  • After you stroke the rectangle (whyy fill **and** stroke it ?) you have not issued any moveto or similar operation. So there is no current point, so you get a nocurrentpoint error. – KenS Apr 21 '22 at 13:43
  • UPD 2: I updated pagecount.ps to fix `(Error: /nocurrentpoint in /--.endpage--)`. I also removed stroke command afted filling the rectangle. Now I have `Error: /typecheck in /--.endpage-- Operand stack: 0 true 595 (1)` Please see new pagecount.ps ` currentpagedevice /PageSize get 0 get exch sub 460 sub 810 moveto show globaldict` are new lines now – bimjhi Apr 21 '22 at 13:52
  • Check the PLRM, page 427 says that "The procedure must return a boolean value" You've got a '0' in there, as well as a bunch of other stuff which may or may not be relevant. If you want to program in PostScript you are going to need to learn the basics of the language. – KenS Apr 21 '22 at 13:57
  • @KenS Finally I've made it working. Maybe you have notions. Anyway thanks! – bimjhi Apr 25 '22 at 18:32
  • You could easily the rectangle just modifying the co-ordinates, rather than the heavyweight stroke. Your method for getting 'unusual' glyphs is not reliable. Not all fonts will have glyphs with a given name, sometimes the glyph is not present, sometimes it has a different name. Your code doesn't seem to check whether a glyph is present in the CharStrings dictionary before using it. It won't work for anything but type 1/CFF fonts. The use of octal 377 (hex 0xFF) is I believe incorrect. You are drawing 0xFF before each of the glyphs you want (you cannot use 2 byte encoding with a type 1 font) – KenS Apr 25 '22 at 18:56

1 Answers1

1

Finally it works.

I've made 2 variants of the script. The 1st one is just page numbering where numbers have obscuring background so that motley pdf stuff could not make the numbers less readable.

Some clarification: sub 460 sub 710 below are related to page number text box and stand for x and y coordinates, respectively.

1 0 0 setrgbcolor fill related to textbox overlay background colour (red here).

1 0 0 setrgbcolor 5 setlinewidth related to textbox overlay contour colour and to its width

0 0 0 setrgbcolor related to numbers colour

/MyPageCount 16 put related to start page number

See: enter image description here

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

The second one is a numbering scheme where numbers have auxilliary text and it all has obscuring background.

See: enter image description here

userdict begin
%%EndProlog

%%BeginSetup
% The following encodes a few useful Unicode glyphs, if only a few are needed.
% Based on https://stackoverflow.com/questions/54840594/show-unicode-characters-in-postscript
% 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

(\377\001C\377\000\377\001D\377\000\377\001E\377\000Page )
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

As you can see, I could insert [₤♣―Page 16] (by using /afii08941, /club, /afii00208) where [...16] is generated automatically on each page in increasing order.

I used special symbols and the lines from userdict begin to >> definefont pop serve for this aim. You may remove these lines if you do not need special symbols in page numbering.

Here is the table of symbols to insert some mnemonics, latin, greek, cyrillic and arabic symbols into Postscript output (PDFs in my case) https://root.cern/doc/v622/AdobeGlyphList_8h_source.html

Special symbols also need remapping scheme, you have to write a definition for it:

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

Then you can put special symbols on the top of Postscript stack and concatenate them with page numbering counter:

(\377\001C\377\000\377\001D\377\000\377\001E\377\000Page )
MyPageCount =string
       cvs concatstrings

\377\001C\377\000 becomes ₤, \377\001D\377\000 becomes ♣. CD in bold.

Note how gsave/grestore work here.

KenS asked me in comments why I had used stroke in my script. It turns out that my page numbering looks like this without stroke: enter image description here

That is, in this case, obsuring background is close-fitting and there are no margins between the numbers themselves and motley PDf stuff and numbers may seem to be interflowing into dark motley stuff in some points.

So stroke draws a border around our auxilliary text and numbers. In my case, the border has the same color (red) as the auxilliary background so they look more readable.

But if you remove gsave/grestore, stroke will not take effect and the resulting picture will look as the previous one. 5 setlinewidth sets a width of 5 pt's.

P.S.

The key in finding bugs of the original answer script was to use debugging pstack operator.

Determine the last working edition then insert pstack after its 1st line and modify the script to check some risky operators. If it works, then move pstack after the 2st line and add another risky operator. Once your script is broken, compare pstack output from working and non-workings editions of your script.

bimjhi
  • 311
  • 2
  • 11