38

If you've got a large document (500 pages+) in Postscript and want to add page numbers, does anyone know how to do this?

Cimbali
  • 11,012
  • 1
  • 39
  • 68
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • 1
    Override the `showpage` operator as shown in [this answer](http://stackoverflow.com/a/6620599/477035) to an essentially [duplicate question](http://stackoverflow.com/q/4766755/477035) – RedGrittyBrick Sep 27 '13 at 09:46
  • See [Alan Munn's very nice solution on TeX SE](https://tex.stackexchange.com/a/18762/3406). – Faheem Mitha Mar 04 '16 at 18:52

15 Answers15

27

Based on rcs's proposed solution, I did the following:

Converted the document to example.pdf and ran pdflatex addpages, where addpages.tex reads:

\documentclass[8pt]{article}
\usepackage[final]{pdfpages}
\usepackage{fancyhdr}

\topmargin 70pt
\oddsidemargin 70pt

\pagestyle{fancy}
\rfoot{\Large\thepage}
\cfoot{}
\renewcommand {\headrulewidth}{0pt}
\renewcommand {\footrulewidth}{0pt}

\begin{document}
\includepdfset{pagecommand=\thispagestyle{fancy}}
\includepdf[fitpaper=true,scale=0.98,pages=-]{example.pdf}
% fitpaper & scale aren't always necessary - depends on the paper being submitted.
\end{document}

or alternatively, for two-sided pages (i.e. with the page number consistently on the outside):

\documentclass[8pt]{book}
\usepackage[final]{pdfpages}
\usepackage{fancyhdr}

\topmargin 70pt
\oddsidemargin 150pt
\evensidemargin -40pt

\pagestyle{fancy}
\fancyhead{} 
\fancyfoot{} 
\fancyfoot[LE,RO]{\Large\thepage}

\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0pt}

\begin{document}
\includepdfset{pages=-,pagecommand=\thispagestyle{fancy}}
\includepdf{target.pdf}
\end{document}

Easy way to change header margins:

% set margins for headers, won't shrink included pdfs
% you can remove the topmargin/oddsidemargin/evensidemargin lines
\usepackage[margin=1in,includehead,includefoot]{geometry}
Seamus Abshere
  • 8,326
  • 4
  • 44
  • 61
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
18

you can simply use

pspdftool

in this way:

pspdftool 'number(x=-1pt,y=-1pt,start=1,size=10)' input.pdf output.pdf

see these two examples (unnumbered and numbered pdf with pspdftool)

unnumbered pdf

http://ge.tt/7ctUFfj2

numbered pdf

http://ge.tt/7ctUFfj2

with this as the first command-line argument:

number(start=1, size=40, x=297.5 pt, y=10 pt)
Dingo
  • 2,619
  • 1
  • 22
  • 32
17

I used to add page numbers to my pdf using latex like in the accepted answer.

Now I found an easier way: Use enscript to create empty pages with a header containing the page number, and then use pdftk with the multistamp option to put the header on your file.

This bash script expects the pdf file as it's only parameter:

#!/bin/bash
input="$1"
output="${1%.pdf}-header.pdf"
pagenum=$(pdftk "$input" dump_data | grep "NumberOfPages" | cut -d":" -f2)
enscript -L1 --header='||Page $% of $=' --output - < <(for i in $(seq "$pagenum"); do echo; done) | ps2pdf - | pdftk "$input" multistamp - output $output
Dario Seidl
  • 4,140
  • 1
  • 39
  • 55
  • 2
    warning for other users: only since build 1.43 pdftk has *multistamp* feature - regarding to code I can suggest output="${1%03d.pdf}-header.pdf" to have zero equalization – Dingo Jan 31 '12 at 21:43
  • 1
    Multistamp is great. Enscript however is not very flexible for this use case - I tried to get the line numbers centered in the footer, with the font I want etc... What I ended up doing is creating 1000 pages with my word processor, with auto line numbers in the footer. Then I just multistamp it on my files. – Chris Lercher Oct 07 '12 at 14:16
  • It doesn't work for me. Bucle does not iterate, I've tried hardcoding $pagenum value, but nothing. – xsubira Nov 11 '14 at 17:11
  • For me dump_data give a line for each page and seq gives an error. With an additional "tail -n1" it worked for me: pagenum=$(pdftk "$input" dump_data | grep "NumberOfPages" | cut -d":" -f2 | tail -n1) – Weidenrinde Dec 23 '16 at 17:48
  • Could you please explain this a bit for beginners like me. – rahim.nagori Aug 19 '20 at 12:45
  • `brew install pdftk-java` for macOS. – HappyFace Jun 11 '21 at 12:27
  • How do we move the position of the headers? – HappyFace Jun 11 '21 at 12:27
16

I was looking for a postscript-only solution, using ghostscript. I needed this to merge multiple PDFs and put a counter on every page. Only solution I found was an old gs-devel posting, which I heavily simplified:

%!PS
% add page numbers document bottom right (20 units spacing , harcoded below)
% Note: Page dimensions are expressed in units of the default user space (72nds of an inch).
% inspired by https://www.ghostscript.com/pipermail/gs-devel/2005-May/006956.html

globaldict /MyPageCount 1 put % initialize page counter

% executed at the end of each page. Before calling the procedure, the interpreter
% pushes two integers on the operand stack:
% 1. a count of previous showpage executions for this device
% 2. a reason code indicating the circumstances under which this call is being made:
%    0: During showpage or (LanguageLevel 3) copypage
%    1: During copypage (LanguageLevel 2 only)
%    2: At device deactivation
% The procedure must return a boolean value specifying whether to transmit the page image to the
% physical output device.
<< /EndPage {
  exch pop % remove showpage counter (unused)
  0 eq dup { % only run and return true for showpage
    /Helvetica 12 selectfont % select font and size for following operations
    MyPageCount =string cvs % get page counter as string
    dup % need it twice (width determination and actual show)
    stringwidth pop % get width of page counter string ...
    currentpagedevice /PageSize get 0 get % get width from PageSize on stack
    exch sub 20 sub % pagewidth - stringwidth - some extra space
    20 moveto % move to calculated x and y=20 (0/0 is the bottom left corner)
    show % finally show the page counter
    globaldict /MyPageCount MyPageCount 1 add put % increment page counter
  } if
} bind >> setpagedevice

If you save this to a file called pagecount.ps you can use it on command line like this:

gs \
  -dBATCH -dNOPAUSE \
  -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress \
  -sOutputFile=/path/to/merged.pdf \
  -f pagecount.ps -f input1.pdf -f input2.pdf

Note that pagecount.ps must be given first (technically, right before the the input file which the page counting should start with).

If you don't want to use an extra .ps file, you can also use a minimized form like this:

gs \
  -dBATCH -dNOPAUSE \
  -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress \
  -sOutputFile=/path/to/merged.pdf \
  -c '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 20 sub 20 moveto show globaldict /MyPageCount MyPageCount 1 add put } if } bind >> setpagedevice' \
  -f input1.pdf -f input2.pdf

Depending on your input, you may have to use gsave/grestore at the beginning/end of the if block.

Jakob
  • 630
  • 6
  • 11
  • 1
    This is excellent, thank you. I hacked on it for a bit, thinking that it would be possible to get rid of /MyPageCount and simply use the page number the interpreter puts on the stack, but that doesn't work for at least some PDFs; with those, the "device deactivation" seems to be called after *each page*, and *then* showpage is called with a never-incrementing page count. – jcomeau_ictx May 12 '20 at 14:37
  • 1
    @jcomeau_ictx yes, I also wondered why the "count of previous showpage executions for this device" always stayed at `1` (IIRC), but never came around the reason and it did not matter enough for further investigation. There are [SO answers](https://stackoverflow.com/a/30426517/6735159) that seem to use it successfully. – Jakob May 12 '20 at 21:56
  • 2
    If in addition you wish to display the total page number: `gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -o "$output" -c "globaldict /MyPageCount 1 put /concatstrings { exch dup length 2 index length add string dup dup 4 2 roll copy length 4 -1 roll putinterval } bind def << /EndPage {exch pop 0 eq dup {/Helvetica 12 selectfont MyPageCount =string cvs ( / $npages) concatstrings dup stringwidth pop currentpagedevice /PageSize get 0 get exch sub 20 sub 20 moveto show globaldict /MyPageCount MyPageCount 1 add put } if } bind >> setpagedevice" -f input1.pdf` – caram Feb 15 '21 at 14:24
  • 1
    ... where `npages` is defined as: `npages="$(qpdf --show-npages input1.pdf)"` – caram Feb 15 '21 at 14:25
  • Nice. It works fine, and the resulting pdf still has internal links. I think that the last command requires a `\` before the last line. – Eric Duminil Jun 24 '21 at 19:08
  • 1
    @EricDuminil I guess you mean that there's backslash \ missing, right? Thanks, fixed that. – Jakob Jul 08 '21 at 12:34
  • @Jakob: Exactly. I forgot to escape the escape character. :D Thanks. – Eric Duminil Jul 08 '21 at 12:37
  • If the stuff of your PDF is motley and bare page numbers look 'interflowing' into background, you can try this solution: https://i.stack.imgur.com/Y2GwO.gif https://stackoverflow.com/questions/71937645/ – bimjhi Apr 25 '22 at 19:45
  • The script without using the .ps file was very useful to dynamically set the page start number. Thank you. – slv007 Jun 16 '22 at 02:45
15

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
rcs
  • 67,191
  • 22
  • 172
  • 153
  • I think this is a great idea, but it I haven't got it working. The page numbers don't insert *over* the \includepdf pages. – Brian M. Hunt Oct 21 '09 at 20:58
  • 1
    Have you used something like `\includepdf[pages=-,pagecommand={\thispagestyle{plain}}]{document.pdf}`? – rcs Oct 21 '09 at 21:02
  • 1
    This is essentially what Alan Munn does in [his solution](https://tex.stackexchange.com/a/18762/3406), minus the PS stuff. – Faheem Mitha Mar 04 '16 at 19:04
5

Further to captaincomic's solution, I've extended it to support the starting of page numbering at any page.

Requires enscript, pdftk 1.43 or greater and pdfjam (for pdfjoin utility)

#!/bin/bash
input="$1"
count=$2
blank=$((count - 1))
output="${1%.pdf}-header.pdf"
pagenum=$(pdftk "$input" dump_data | grep "NumberOfPages" | cut -d":" -f2)
(for i in $(seq "$blank"); do echo; done) | enscript -L1 -B --output - | ps2pdf - > /tmp/pa$$.pdf
(for i in $(seq "$pagenum"); do echo; done) | enscript -a ${count}- -L1 -F Helvetica@10 --header='||Page $% of $=' --output - | ps2pdf - > /tmp/pb$$.pdf
pdfjoin --paper letter --outfile /tmp/join$$.pdf /tmp/pa$$.pdf /tmp/pb$$.pdf &>/dev/null
cat /tmp/join$$.pdf | pdftk "$input" multistamp - output "$output"
rm /tmp/pa$$.pdf
rm /tmp/pb$$.pdf
rm /tmp/join$$.pdf

For example.. place this in /usr/local/bin/pagestamp.sh and execute like:

pagestamp.sh doc.pdf 3

This will start the page number at page 3.. useful when you have coversheets, title pages and table of contents, etc.

The unfortunate thing is that enscript's --footer option is broken, so you cannot get the page numbering at the bottom using this method.

Bob
  • 51
  • 1
  • 1
5

I liked the idea of using pspdftool (man page) but what I was after was page x out of y format and the font style to match the rest of the page.

To find out about the font names used in the document:

$ strings input.pdf | grep Font

To get the number of pages:

$ pdfinfo input.pdf | grep "Pages:" | tr -s ' ' | cut -d" " -f2

Glue it together with a few pspdftool commands:

$ in=input.pdf; \
out=output.pdf; \
indent=30; \
pageNumberIndent=49; \
pageCountIndent=56; \
font=LiberationSerif-Italic; \
fontSize=9; \
bottomMargin=40; \
pageCount=`pdfinfo $in | grep "Pages:" | tr -s ' ' | cut -d" " -f2`; \
pspdftool "number(x=$pageNumberIndent pt, y=$bottomMargin pt, start=1, size=$fontSize, font=\"$font\")" $in tmp.pdf; \
pspdftool "text(x=$indent pt, y=$bottomMargin pt, size=$fontSize, font=\"$font\", text=\"page \")" tmp.pdf tmp.pdf; \
pspdftool "text(x=$pageCountIndent pt, y=$bottomMargin pt, size=$fontSize, font=\"$font\", text=\"out of $pageCount\")" tmp.pdf $out; \
rm tmp.pdf;

Here is the result:

enter image description here

Johnny Baloney
  • 3,409
  • 1
  • 33
  • 37
  • pspdftools allows you to pass in a string to format the page number, so you can do this in one shot: `pspdftool "number(x=$pageNumberIndent pt, y=$bottomMargin pt, start=1, size=$fontSize, font=\"$font\", text=\"page %d out of $pageCount\")" $in $out` – Diab Jerius Dec 07 '20 at 22:03
3

Oh, it's a long time since I used postscript, but a quick dip into the blue book will tell you :) www-cdf.fnal.gov/offline/PostScript/BLUEBOOK.PDF

On the other hand, Adobe Acrobat and a bit of javascript would also do wonders ;)

Alternatively, I did find this: http://www.ghostscript.com/pipermail/gs-devel/2005-May/006956.html, which seems to fit the bill (I didn't try it)

brinxmat
  • 1,191
  • 1
  • 10
  • 13
3

You can use the free and open source pdftools to add page numbers to a PDF file with a single command line.

The command line you could use is (on GNU/Linux you have to escape the $ sign in the shell, on Windows it is not necessary):

pdftools.py --input-file ./input/wikipedia_algorithm.pdf --output ./output/addtext.pdf --text "\$page/\$pages" br 1 1 --overwrite

Regarding the --text option:

  • The first parameter is the text to add. Some placeholders are available. $page stands for the current page number, while $pages stands for the total number of pages in the PDF file. Thus the option so formulated would add something like "1/10" for the first page of a 10-page PDF document, and so on for the following pages
  • The second parameter is the anchor point of the text box. "br" will position the bottom right corner of the text box
  • The third parameter is the horizontal position of the anchor point of the text box as a percentage of the page width. Must be a number between 0 and 1, with the dot . separating decimals
  • The fourth parameter option is the vertical position of the anchor point on the text box as a percentage of the page height. Must be a number between 0 and 1, with the dot . separating decimals

Disclaimer: I'm the author of pdftools

robertspierre
  • 3,218
  • 2
  • 31
  • 46
  • thank you! I tried this but it only shows / without putting the page numbers... it is like the $page $pages variables dont populate... – AAA Jul 18 '20 at 14:52
  • @AAA If you are on GNU/Linux, you have to escape the `$` sign from the shell, so you have to write `\$` and not just `$`: call it `pdftools.py [...] --text \$page/\$pages [...]` – robertspierre Jan 25 '21 at 11:00
  • with single quotes, shell doesn't interpolate. `'$page / $pages'` – usretc May 16 '21 at 10:29
  • But how to move the `br` so it's not **right down against the corner**? I've tried `br 5 5`, `br -10 -10` -- it goes off page or doesn't show up – usretc May 16 '21 at 10:31
  • 2
    @usretc what is the end result you are trying to achieve? You can also open an issue on the GitHub page of pdftools. `br 0.9 0.9` will put the bottom right corner of the text box you are adding 90% from the left margin of the page and 90% from the top margin of the page – robertspierre May 16 '21 at 22:15
  • The two numbers after `br` are between 0 and 1 – robertspierre May 16 '21 at 22:16
  • that was exactly it, somehow I got stuck thinking about absolute units – usretc May 16 '21 at 22:43
  • Also for some reason the `temp` directory is not removed automatically after a successful run. – Dima Chubarov Dec 01 '21 at 06:15
  • 1
    @DmitriChubarov can you open an issue on [github](https://github.com/raffaem/pdftools/issues) and type the exact command line you used? I tested it again on my system and I cannot find the temp directory after a successful run – robertspierre Dec 01 '21 at 08:31
  • Ah, sorry, I was running with `--debug` flag. Thank you for the hint. – Dima Chubarov Dec 02 '21 at 04:52
  • @DmitriChubarov yeah that would explain it – robertspierre Dec 02 '21 at 16:39
1

I am assuming you are looking for a PS-based solution. There is no page-level operator in PS that will allow you to do this. You need to add a footer-sort of thingy in the PageSetup section for each page. Any scripting language should be able to help you along.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
1

I tried pspdftool (http://sourceforge.net/projects/pspdftool).

I eventually got it to work, but at first I got this error:

pspdftool: xreftable read error

The source file was created with pdfjoin from pdfjam, and contained a bunch of scans from my Epson Workforce as well as generated tag pages. I couldn't figure out a way to fix the xref table, so I converted to ps with pdf2ps and back to pdf with pdf2ps. Then I could use this to get nice page numbers on the bottom right corner:

pspdftool 'number(start=1, size=20, x=550 pt, y=10 pt)' input.pdf output.pdf

Unfortunately, it means that any text-searchable pages are no longer searchable because the text was rasterized in the ps conversion. Fortunately, in my case it doesn't matter.

Is there any way to fix or empty the xref table of a pdf file without losing what pages are searchable?

  • ``Is there any way to fix or empty the xref table of a pdf file without losing what pages are searchable?`` -> you should open a new question for this – robertspierre Dec 01 '21 at 08:37
1

I took captaincomic's solution and added support for filenames containing spaces, plus giving some more informations about the progress

#!/bin/bash
clear
echo
echo This skript adds pagenumbers to a given .pdf file.
echo 
echo This skript needs the packages pdftk and enscript
echo if not installed the script will fail.
echo use the command sudo apt-get install pdftk enscript
echo to install.
echo 
input="$1"
output="${1%.pdf}-header.pdf"
echo input file is $input
echo output file will be $output
echo 
pagenum=$(pdftk "$input" dump_data | grep "NumberOfPages" | cut -d":" -f2)
enscript -L1 --header='||Page $% of $=' --output - < <(for i in $(seq "$pagenum"); do echo; done) | ps2pdf - | pdftk "$input" multistamp - output "$output"
echo done.
corpsman
  • 11
  • 2
0

I wrote the following shell script to solve this for LaTeX beamer style slides produced with inkscape (I pdftk cat the slides together into the final presentation PDF & then add slide numbers using the script below):

#!/bin/sh

# create working directory
tmpdir=$(mktemp --directory)

# read un-numbered beamer slides PDF from STDIN & create temporary copy
cat > $tmpdir/input.pdf

# get total number of pages
pagenum=$(pdftk $tmpdir/input.pdf dump_data | awk '/NumberOfPages/{print $NF}')

# generate latex beamer document with the desired number of empty but numbered slides
printf '%s' '
\documentclass{beamer}
\usenavigationsymbolstemplate{}
\setbeamertemplate{footline}[frame number]
\usepackage{forloop}
\begin{document}
 \newcounter{thepage}
  \forloop{thepage}{0}{\value{thepage} < '$pagenum'}{
    \begin{frame}
    \end{frame}
  }
\end{document}
' > $tmpdir/numbers.tex

# compile latex file into PDF (2nd run needed for total number of pages) & redirect output to STDERR
pdflatex -output-directory=$tmpdir numbers.tex >&2 && pdflatex -output-directory=$tmpdir numbers.tex >&2

# add empty numbered PDF slides as background to (transparent background) input slides (page by
# page) & write results to STDOUT
pdftk $tmpdir/input.pdf multibackground $tmpdir/numbers.pdf output -

# remove temporary working directory with all intermediate files
rm -r $tmpdir >&2

The script reads STDIN & writes STDOUT printing diagnostic pdflatex output to STDERR.

So just copy-paste the above code in a text file, say enumerate_slides.sh, make it executable (chmod +x enumerate_slides.sh) & call it like this:

./enumerate_slides.sh < input.pdf > output.pdf [2>/dev/null]

It should be easy to adjust this to any other kind of document by adjusting the LaTeX template to use the proper documentclass, paper size & style options.

edit: I replaced echo by $(which echo) since in ubuntu symlinks /bin/sh to dash which overrides the echo command by a shell internal interpreting escape sequences by default & not providing the -E option to override this behaviour. Note that alternatively you could escape all \ in the LaTeX template as \\.

edit: I replaced $(which echo) by printf '%s' since in zsh, which echo returns echo: shell built-in command instead of /bin/echo. See this question for details why I decided to use printf in the end.

Community
  • 1
  • 1
mschilli
  • 1,884
  • 1
  • 26
  • 56
0

I have used LibreOffice Draw for this. Adding a page number field is easy using Insert->Field->Page Number. And then you can copy-and-paste this field to other pages; fortunately the position is not changed and the copy-and-paste can be done quickly with down arrow key and Ctrl+V. Worked for me for a 30 page article. Maybe prone to errors for a 500+ one!

Ehsan88
  • 3,569
  • 5
  • 29
  • 52
-1

Maybe pstops (part of psutils) can be used for this?

Filip Korling
  • 615
  • 3
  • 7