Search code examples
pdfbatch-fileghostscriptpostscript

Using Ghostscript to add page numbers via batch


I am trying to add pages to PDFs in the format page out of total_num_pages, I have about 100K documents to process, so I need to figure out a way to do this with many documents.

I have a batch file that takes 2 inputs: input pdf and output pdf

@echo off
setlocal

REM Check if the correct number of arguments is provided
if "%~1"=="" (
    echo Usage: %0 input.pdf output.pdf
    exit /b 1
)

if "%~2"=="" (
    echo Usage: %0 input.pdf output.pdf
    exit /b 1
)

REM Input and output files
set INPUT_PDF=%~1
set OUTPUT_PDF=%~2

REM Temp file for the page numbering PostScript
set PSF_SCRIPT="C:\Temp\GhostScript\AddPageNum.ps"
    
REM Run Ghostscript to add page numbers
"C:\Program Files\gs\gs9.53.3\bin\gswin64c.exe" -o %OUTPUT_PDF% -sDEVICE=pdfwrite -dBATCH -dNOPAUSE -dQUIET -dPDFFitPage -c "/Times-Roman findfont 12 scalefont setfont" -c "/showpage {showpage /PageCount 1 add def} def" -f %INPUT_PDF% -f %PSF_SCRIPT%

endlocal
echo Page numbers added to %OUTPUT_PDF%

My postscript, AddpageNum.ps

/Times-Roman findfont 12 scalefont setfont
/pageNum 1 def
<<
/EndPage {
 2 eq {
   gsave
   0.95 setgray
   /Times-Roman findfont 12 scalefont setfont
   0.5 setgray
   72 36 moveto
   pageNum 40 string cvs show
   grestore
   /pageNum pageNum 1 add def
   true
 } if
} bind
>> setpagedevice

When running the batchfile in command line

C:\Users\psun0>C:\Temp\GhostScript\AddPageNum.bat input.pdf output.pdf

When I run the batch command, a new prompt comes up and no file is generated.

I don't know how to troubleshoot this as I don't have any PostScript or Ghostscript experience.


Solution

  • Others have given good advice in comments to suggest a file with feedback such as this potential single run.cmd. It should not work in full, but will produce a false output, and allow you to see the errors.

    NOTE GhostScript requires case specific full filenames hence the use of %~f1" and %~f2"

    @echo off
    
    REM Check if the correct number of arguments is provided
    if "%~1"=="" ( echo Usage: %0 input.pdf output.pdf & exit /b 1 )
    if "%~2"=="" ( echo Usage: %0 input.pdf output.pdf & exit /b 1 )
    
    REM Input and output files
    set "INPUT_PDF=%~f1"
    set "OUTPUT_PDF=%~f2"
    
    REM Temp file for the page numbering PostScript
    set "PSF_SCRIPT=C:\Temp\GhostScript\AddPageNum.ps"
        
    REM Run Ghostscript to add page numbers
    "C:\Program Files\gs\gs10.03.1\bin\gswin64c.exe" -o"%OUTPUT_PDF%" -sDEVICE=pdfwrite -dPDFFitPage "%PSF_SCRIPT%" -c "/Times-Roman findfont 12 scalefont setfont" -c "/showpage {showpage /PageCount 1 add def} def" -f "%INPUT_PDF%"
    
    if exist %OUTPUT_PDF% echo Page numbers added to %OUTPUT_PDF%
    

    Most likely output should be something along the lines of

    Loading NimbusRoman-Regular font from %rom%Resource/Font/NimbusRoman-Regular... 3865264 2515446 1741192 449709 1 done.
    Processing pages 1 through 1.
    Page 1
       **** Error: Page drawing error occurred.
                   Could not draw this page at all, page will be missing in the output.
    GPL Ghostscript 10.03.1: Page object was reserved for an Annotation destination, but no such page was drawn, annotation in output will be invalid.
    
    Page numbers added to C:\blah blah\output.pdf
    

    So note the font mentioned is NOT Times which is a "surrogate" font name the attempted font will be Nimbus and if Times does show as forced it will be fontless (not embedded). enter image description here What you need is

    Loading NimbusRoman-Regular font from %rom%Resource/Font/NimbusRoman-Regular... 3865264 2515400 1915392 597811 1 done.
    Processing pages 1 through 1.
    Page 1
    Loading NimbusSans-Bold font from %rom%Resource/Font/NimbusSans-Bold... 4025408 2714730 2136536 804225 2 done.
    Page numbers added to C:\blah blah\output.pdf
    

    with a report with embedded font enter image description here

    This is done by remove all those poor external directions and simplify down to:-

    "C:\Program Files\gs\gs10.03.1\bin\gswin64c.exe" -o"%OUTPUT_PDF%" -sDEVICE=pdfwrite -dPDFFitPage "%PSF_SCRIPT%" -f "%INPUT_PDF%"
    

    And provide a fuller modern AddPageNum.ps so from a similar answer use along these lines.

    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 12 selectfont
          72 36 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