# Add page numbers to a PDF

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](https://www.sumatrapdfreader.org/prerelease)**

**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:** `FORMAT` is `Page {n} of {total}`, `{n}`, `- {n} -`, ...
- **Position:** `POSITION` puts the number at the bottom or top, left, center or right; `MARGIN` sets the distance from the edge.
- **Look:** `FONT`, `FONT_SIZE`, `COLOR`.
- **Cover page:** `SKIP_PAGES` leaves the first pages without a number; `FIRST_NUMBER` sets where counting starts.

## Add page numbers

1. Save the script as `add-page-numbers.js`.
2. Change the settings at the top of the script, if you want.
3. 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](Tools.md) with the same arguments works the same.

```js
// 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](Batch-process-PDFs.md).

## 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.pdf` in 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](Split-a-PDF.md), number the part, and [merge](Merge-PDFs.md) it back.

## See also

- [Add a watermark to a PDF](Add-a-watermark-to-PDF.md) — "DRAFT" across every page, same approach
- [Batch process PDFs](Batch-process-PDFs.md) — run the script on many files
- [sumatrapdf-tool run](Tool-run.md) — how scripts run
- [JavaScript API reference](Tool-run-javascript-reference.md) — the objects the script uses
- [All cmd-line tools](Tools.md)
