How to Add a Footer to the Bottom of Each Page of a Postscript or PDF File in Linux

How can I add a footer to the bottom of each page of a postscript or pdf file in linux?

ESP Ghostscript is O-o-o-o-old. Don't use it any more unless you absolutely, absolutely cannot avoid it. It was a fork of the original Ghostscript which used by CUPS for a while. (And after some problems between developers where resolved, more recent versions of CUPS now also use the GPL Ghostscript again...)

Newer GPL Ghostscript versions are here: http://www.ghostscript.com/releases/

Also, -o out.pdf is only a shorthand for -dBATCH -dNOPAUSE -sOutputFile=outpdf. So you should try this. (The -dNOPAUSE part relieves you from hitting <return> for every page advance....).

Lastly, don't expect the full range of documentation being provided by a third party man gs page. Rather refer to the original Ghostscript documentation for the version you use, the most important parts being:

  • current development branch: Readme.htm + Use.htm + Ps2pdf.htm
  • 9.00 release: Readme.htm + Use.htm + Ps2pdf.htm
  • 8.71 release: Readme.htm + Use.htm + Ps2pdf.htm

Update: Ghostscript has moved to Git (instead of Subversion) for their source code repository. Therefor the following links have changed, repeatedly:

  • current development branch: Readme.htm + Use.htm + Ps2pdf.htm
  • current development branch: Readme.htm + Use.htm + Ps2pdf.htm

Postscript add footer on the last page only

You need to know how many pages are in the PostScript program, if you don't know that, then you can't add the footer on the 'last' page. If your file is DSC-compliant it will tell you how many pages are in it.

How to add page numbers to Postscript/PDF

This might be a solution:

  1. convert postscript to pdf using ps2pdf
  2. create a LaTeX file and insert the pages using the pdfpages package (\includepdf)
  3. use pagecommand={\thispagestyle{plain}} or something from the fancyhdr package in the arguments of \includepdf
  4. if postscript output is required, convert the pdflatex output back to postscript via pdf2ps

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

Its not terribly hard to count pages in PostScript. I expect you know most of the following already, but I'm going to take baby steps for the benefit of anyone else who comes across this later.

We start by creating a dictionary all our own where we can store stuff. We make sure this is defined in 'userdict' so we can always find it (userdict, like systemdict, is always available). THe name chosen should be nicely unique to prevent any other PostScript or PDF prgrams overwriting it!

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)

There are more efficient ways to do this, but that's an easy method to describe and follow. From this point until we close the PostScript interpreter (or restore it back to an earlier state) userdict will contain a dictionary called My_working_Dict which will have a key called PageCount. The value associated with PageCount will be 0 initially, but we can change that.

You've defined an EndPage procedure like this:

<<
/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

Now when the EndPage procedure is called, there are two numbers on the stack, the topmost number is the 'reason code' and the next number is the count of previous showpage executions. Now you would think (reasonably) you could use that count for your page count, but unfortunately it gets reset to 0 on every 'setpagedevice' call, and the PDF interpreter calls setpagedevice on every page, because its possible for every page in a PDF file to be a adiffrent size, and setpagedevice is how we change the page size.

When we return from the EndPage procedure we must push a boolean on the stack which is either 'true' (send the page to the output) or 'false' (throw it away and do nothing).

So, your procedure tests the reason code to see why EndPage has been called. If its not '2' (device is being deactivated) then its either copypage or showpage, so you draw your desired additions on the page. If it is 2 then we just pop the count of pages and return 'false' so that we don't try to emit an extra page.

If it is 2 then you set the colour to RGB black (you could do 0 setgray instead) find the font NimbusSans-Bold, scale it to 24 point and set it as the current font. You then move to the position x=240, y = 2 (0,0 is bottom left and units are points, 1/72nd of an inch) and draw the text 'Static Footer' (NB parentheses are string delimiters in PostScript)

Then you move to the position x=240, y=773 and draw the text 'Static Header'.

You then redundantly set the colour again, you don't need to do that, it stays the same until you change it, and again find the font NimbusSans-Bold, this time scaling it to 12 points and selecting it as the current font. Finally you move to the position x=2, y=24 and draw the text 'Page '.

So all you need to do is extend that EndPage procedure so that it picks up the count of pages from our dictionary, converts it to a string, and draws the resulting string.

Something like :

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

You would then execute Ghostscript with :

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

Now I haven't actually tried this code, so bugs are entirely possible.

[Update 2]

This program is basically the same, but stores the variable in a dicitonary in global VM, stored in globaldict, in order to defeat 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

I have tried this with the suppled example and it works for me.

ghostscript header over pdf file

You need to add the code to make marks on the page as an EndPage procedure, otherwise you first make the marks on the 'page', then write the content of the PDF file. I assume you want to do that the other way around, first write the content of the PDF file then write the additional content afterwards.

You should also set the colour you want the text to be in, otherwise it will use the current colour, which might be white. 0 setgray 0 0 0 setrgbcolor or 0 0 0 1 setcmykcolor will set the current colour to black.

So something like:

-c "<</EndPage {0 setgray /Times 20 selectfont 453 482 moveto (544) show}>> setpagedevice" -f

should probably work well enough.

As regards the warning about Arial-ItalicMT this means your PDF file uses that font, but does not include it. You must either:

  1. Accept the substitution offered by Ghostscript (Helvetica-Oblique)
  2. Supply a different substitute
  3. Supply the real font

In order to do either of 2 or 3 you will need to modify fontmap.GS or cidfmap, depending on whether its a font or a CIDFont which is missing.

Add corrected code:

-sDEVICE=pdfwrite -sOutputFile=\temp\out.pdf -c "<</EndPage {2 ne {0.5 setgray /Times 20 selectfont 453 48 moveto (Test) show pop true}{pop false} ifelse} >> setpagedevice" -f

can I use Ghostscript to overlay a text (fax) header onto a PDF and/or TIFF?

I would use another commandline tool combined with Ghostscript for this task. This tool is pdftk.exe. Then use a 3 step approach:

  1. The task of Ghostscript would be to create an (otherwise empty) page with the header text:

    gswin64c.exe ^
    -o header.pdf ^
    -sDEVICE=pdfwrite ^
    -c "/Courier findfont 12 scalefont setfont" ^
    -c "50 765 moveto (header text) show showpage"
  2. The task of pdftk would be to overlay (stamp or background) the PDF file with the text header over the original PDF:

    pdftk.exe goofy.pdf background header.pdf output goofy-with-header.pdf
    or

    pdftk.exe goofy.pdf stamp header.pdf output goofy-with-header.pdf
  3. The last step is to employ Ghostscript again in order to create your final TIFF output:

    gswin64c.exe ^
    -dPDFFitPage ^
    -o goofy-with-header.tif ^
    -sDEVICE=tiffg3 ^
    goofy-with-header.pdf

How can I make a program overlay text on a postscript file?

Ok, the example file you linked to is well behaving (and has not re-defined the showpage operator).

So I'm now assuming the following:

  1. All your .ps files are similar to your example file.
  2. All your .ps files are 1 page only (like .eps files).
  3. All your filenames are like constructed as gnp-NNN.ps (like gnp-544.ps is).
  4. The number you want to appear in the top right corner is NNN from the filename.

I also assume you have Ghostscript installed, and it is the most recent version, 8.71. I'm currently on Windows -- if you're on Linux/Unix, just replace gswin32c.exe by gs and all line endings ^ by \.

Test PDF Output

Now try this command first:

gswin32c.exe ^
-o gnp-with-number-544.pdf ^
-sDEVICE=pdfwrite ^
-g5030x5320 ^
-c "/Helvetica-Italic findfont 15 scalefont setfont 453 482 moveto (544) show" ^
-f gnp-544.ps

and see if the resulting gnp-with-number-544.pdf looks like you want it.

The -c "..." is used to pass PostScript snippets to Ghostscript and make it process these together with the main .ps file which has then to follow as next parameter, with -f ....

You can modify parameters:

  • Replace the /Helvetica-Italic fontname by /Helvetica, /Courier, /Times, Helvetica-Bold or whatever you've available and prefer.
  • Or modify the fontsize: 15 here means 15 points (in PDF 72 points == 1 inch).
  • Or change the position: 453 482 moves the PostScript currentpoint to "453 points to the left, 482 points to the top" (with the oringin 0,0 set to the bottom left corner).
  • Or tweak the media size: -g5030x5320 gives you 503x532 points (due to the default resolution of 720 dpi used by -sDEVICE=pdfwrite.
  • Or set the string being printed to (File No. 544) show or whatever you want.

You could also add quite a few parameters to tweak the quality of the output file (resolution, font embedding etc.), but these ones will do for now.

Change to PostScript Output

Should you need PostScript output for some reason instead of PDF, change the command like this:

gswin32c.exe ^
-o gnp-with-number-544.ps ^
-sDEVICE=ps2write ^
-g5030x5320 ^
-c "/Helvetica-Italic findfont 15 scalefont setfont 453 482 moveto (544) show" ^
-f gnp-544.ps

Batch-convert Lots of Files

Now, how to batch-convert this? For this last step, I'm assuming:

  1. your NNN numbering scheme is not using leading 0s.
  2. your range of files to be converted is gnp-1.ps..gnp-1000.ps.
  3. you want PDF output with the numbers added, not PS.

On Windows, create an addnumbers-make-pdf.bat file with this content:

gswin32c.exe ^
-o gnp-with-number-%1.pdf ^
-sDEVICE=pdfwrite ^
-g5030x5320 ^
-c "/Helvetica-Italic findfont 15 scalefont setfont 453 482 moveto (%1) show" ^
-f gnp-%1.ps

Now run this command in a cmd.exe console:

for /l %i in (1,1,100) do (addnumbers-make-pdfvim.bat %i)

On Linux, create an addnumbers-make-pdf.sh Bash shell script with this content:

#!/bin/bash
gs \
-o gnp-with-number-${1}.pdf \
-sDEVICE=pdfwrite \
-g5030x5320 \
-c "/Helvetica-Italic findfont 15 scalefont setfont 453 482 moveto (${1}) show" \
-f gnp-${1}.ps

Now run this command in a shell:

for i in $(seq 1 1000); do addnumbers-make-pdf.sh ${i} ; done

Update:

Hah!, it even works ;-) I just tested it on Windows. Here is a screenshot with the original and the overlayed file (as PDFs):

alt text



Related Topics



Leave a reply



Submit