Stamp “Page 3 of 10” (or just “3”) on every page of a PDF with a short script you run with
sumatrapdf-tool run.Available in pre-release 3.7
Save the script below as
add-page-numbers.js and run it on your PDF. At a glance:- Run:
sumatrapdf-tool run add-page-numbers.js in.pdf out.pdf - Text:
FORMATisPage {n} of {total},{n},- {n} -, … - Position:
POSITIONputs the number at the bottom or top, left, center or right;MARGINsets the distance from the edge. - Look:
FONT,FONT_SIZE,COLOR. - Cover page:
SKIP_PAGESleaves the first pages without a number;FIRST_NUMBERsets where counting starts.
Add page numbers #
- Save the script as
add-page-numbers.js. - Change the settings at the top of the script, if you want.
- Run it:
sumatrapdf-tool run add-page-numbers.js in.pdf out.pdf
out.pdf is a copy of in.pdf with a page number on every page. SumatraPDF.exe run with the same arguments works the same.// Add page numbers to every page of a PDF.
// Usage: sumatrapdf-tool run add-page-numbers.js in.pdf out.pdf
// ---- Settings ----
var FORMAT = "Page {n} of {total}" // "{n}" for just the number
var POSITION = "bottom-center" // bottom-left, bottom-center, bottom-right,
// top-left, top-center, top-right
var MARGIN = 24 // distance from the page edge, in points (72 = 1 inch)
var FONT = "Helvetica" // Helvetica, Times-Roman, Courier, Helvetica-Bold, ...
var FONT_SIZE = 10 // in points
var COLOR = [0, 0, 0] // red, green, blue from 0 to 1
var SKIP_PAGES = 0 // leave the first N pages (e.g. a cover) without a number
var FIRST_NUMBER = 1 // number shown on the first numbered page
// ------------------
if (scriptArgs.length != 2) {
print("usage: sumatrapdf-tool run add-page-numbers.js in.pdf out.pdf")
quit(1)
}
var doc = Document.openDocument(scriptArgs[0])
var font = new Font(FONT)
var fontRef = doc.addSimpleFont(font)
var total = FIRST_NUMBER + doc.countPages() - SKIP_PAGES - 1
// open "q" before the page's own content, close it with "Q" after,
// so changes the page makes to the graphics state don't affect our text
var saveState = doc.addStream("q\n")
function textWidth(s) {
var w = 0
for (var i = 0; i < s.length; i++)
w += font.advanceGlyph(font.encodeCharacter(s.charCodeAt(i)), 0)
return w * FONT_SIZE
}
function num(v) {
return Math.round(v * 100) / 100
}
function pdfString(s) {
return "(" + s.replace(/[\\()]/g, "\\$&") + ")"
}
for (var i = SKIP_PAGES; i < doc.countPages(); i++) {
var page = doc.loadPage(i)
var pageObj = page.getObject()
// add the font to the page resources
var res = pageObj.getInheritable("Resources")
if (!res) {
res = doc.newDictionary()
pageObj.put("Resources", res)
}
if (!res.get("Font"))
res.put("Font", doc.newDictionary())
res.get("Font").put("PageNumFont", fontRef)
// page size as you see it (crop box, after rotation)
var b = page.getBounds()
var w = b[2] - b[0], h = b[3] - b[1]
var text = FORMAT.replace("{n}", FIRST_NUMBER + i - SKIP_PAGES).replace("{total}", total)
var tw = textWidth(text)
var x = MARGIN, y = MARGIN
if (POSITION.indexOf("center") >= 0) x = (w - tw) / 2
if (POSITION.indexOf("right") >= 0) x = w - MARGIN - tw
if (POSITION.indexOf("top") >= 0) y = h - MARGIN - FONT_SIZE
// map "bottom-left corner as you see it, y up" to the page's own coordinates;
// handles /Rotate and crop boxes that don't start at 0,0
var m = Matrix.concat([1, 0, 0, -1, b[0], b[3]], Matrix.invert(page.getTransform()))
var stamp = "\nQ q " + m.map(num).join(" ") + " cm " +
"BT /PageNumFont " + FONT_SIZE + " Tf " + COLOR.join(" ") + " rg " +
num(x) + " " + num(y) + " Td " + pdfString(text) + " Tj ET Q\n"
// new page content: q, the old content, Q, page number
var contents = doc.newArray()
contents.push(saveState)
var old = pageObj.get("Contents")
if (old && old.isArray())
old.forEach(function (c) { contents.push(c) })
else if (old)
contents.push(old)
contents.push(doc.addStream(stamp))
pageObj.put("Contents", contents)
}
doc.save(scriptArgs[1], "garbage,compress")
The numbers are part of the page, so they show in every PDF viewer and print. They are real text: you can search for them and select them.
The number is placed on the page as you see it: landscape pages, pages with a rotation (
/Rotate) and cropped pages get it at the bottom of the visible page, reading left to right.Change the text and position #
Edit the settings at the top of the script:
| You want | Setting |
|---|---|
Just the number: 7 |
FORMAT = "{n}" |
- 7 - |
FORMAT = "- {n} -" |
| Number in the bottom right corner | POSITION = "bottom-right" |
| Number at the top | POSITION = "top-center" |
| Closer to the edge | MARGIN = 12 |
| Bigger, bold, gray | FONT_SIZE = 14, FONT = "Helvetica-Bold", COLOR = [0.5, 0.5, 0.5] |
No number on the cover, page 2 shows 1 |
SKIP_PAGES = 1 |
No number on the cover, page 2 shows 2 |
SKIP_PAGES = 1, FIRST_NUMBER = 2 |
| Continue from a previous file, start at 41 | FIRST_NUMBER = 41 |
{total} is the last number printed, so Page {n} of {total} stays right when you skip pages or start at another number.FONT is a standard PDF font: Helvetica, Times-Roman or Courier, or a bold or italic variant such as Helvetica-Bold, Times-Italic, Courier-Oblique, Helvetica-BoldOblique. Standard fonts are not embedded, so the file stays small. Use only Latin characters in FORMAT.Number many PDFs #
Run the script in a loop. In
cmd.exe, to write numbered copies of every PDF in the current folder to a numbered folder:mkdir numbered
for %f in (*.pdf) do sumatrapdf-tool run add-page-numbers.js "%f" "numbered\%f"
More: Batch process PDFs.
Tips #
- Write to a new file. Saving over the input file fails with
cannot remove file. - Keep the original: running the script twice stamps the numbers twice.
- Open
out.pdfin SumatraPDF to check the position before you number many files. - Adding numbers changes the pages, so it breaks existing digital signatures. Number the PDF before you sign it.
- To number only part of a document, split it, number the part, and merge it back.
See also #
- Add a watermark to a PDF — “DRAFT” across every page, same approach
- Batch process PDFs — run the script on many files
- sumatrapdf-tool run — how scripts run
- JavaScript API reference — the objects the script uses
- All cmd-line tools