# Add a watermark to a PDF

Put a large, semi-transparent "DRAFT" or "CONFIDENTIAL" across every page of a PDF, or a logo image, 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-watermark.js` and run it on your PDF.** At a glance:

- **Run:** `sumatrapdf-tool run add-watermark.js in.pdf out.pdf`
- **Text:** `TEXT`, `FONT`, `COLOR`; `FONT_SIZE = 0` sizes it to fit the page.
- **Angle:** `ANGLE = -1` follows the page diagonal; `0` is horizontal.
- **Transparency:** `OPACITY` from `0` (invisible) to `1` (solid).
- **Over or under:** `UNDER = true` draws it behind the page content.
- **Logo:** `IMAGE` adds a PNG or JPEG at the center of every page.

## Add a text watermark

1. Save the script as `add-watermark.js`.
2. Change `TEXT` and the other settings at the top of the script, if you want.
3. Run it:

```
sumatrapdf-tool run add-watermark.js in.pdf out.pdf
```

`out.pdf` is a copy of `in.pdf` with the watermark on every page. [SumatraPDF.exe run](Tools.md) with the same arguments works the same.

```js
// Add a semi-transparent text or image watermark to every page of a PDF.
// Usage: sumatrapdf-tool run add-watermark.js in.pdf out.pdf

// ---- Settings ----
var TEXT = "DRAFT"          // "" for no text
var FONT = "Helvetica-Bold" // Helvetica, Times-Roman, Courier, Times-Bold, ...
var FONT_SIZE = 0           // in points; 0 fits the text across the page
var COLOR = [1, 0, 0]       // red, green, blue from 0 to 1
var ANGLE = -1              // degrees counter-clockwise; -1 follows the page diagonal
var IMAGE = ""              // path of a PNG or JPEG logo, drawn at the center; "" for none
var IMAGE_WIDTH = 0.4       // image width as a fraction of the page width
var OPACITY = 0.3           // 0 is invisible, 1 is solid
var UNDER = false           // true puts the watermark under the page content
// ------------------

if (scriptArgs.length != 2) {
	print("usage: sumatrapdf-tool run add-watermark.js in.pdf out.pdf")
	quit(1)
}

var doc = Document.openDocument(scriptArgs[0])
var font = new Font(FONT)
var fontRef = doc.addSimpleFont(font)
var gsRef = doc.addObject({ Type: "ExtGState", ca: OPACITY, CA: OPACITY })
var image = IMAGE ? new Image(IMAGE) : null
var imageRef = image ? doc.addImage(image) : null
var saveState = doc.addStream("q\n")

function num(v) {
	return Math.round(v * 1000) / 1000
}

function pdfString(s) {
	return "(" + s.replace(/[\\()]/g, "\\$&") + ")"
}

function textWidth(s, size) {
	var w = 0
	for (var i = 0; i < s.length; i++)
		w += font.advanceGlyph(font.encodeCharacter(s.charCodeAt(i)), 0)
	return w * size
}

// add a resource to the page, e.g. addResource(pageObj, "Font", "WmFont", fontRef)
function addResource(pageObj, type, name, ref) {
	var res = pageObj.getInheritable("Resources")
	if (!res) {
		res = doc.newDictionary()
		pageObj.put("Resources", res)
	}
	if (!res.get(type))
		res.put(type, doc.newDictionary())
	res.get(type).put(name, ref)
}

for (var i = 0; i < doc.countPages(); i++) {
	var page = doc.loadPage(i)
	var pageObj = page.getObject()
	addResource(pageObj, "ExtGState", "WmGS", gsRef)

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

	// map "center of the page 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 center = Matrix.concat([1, 0, 0, -1, b[0] + w / 2, b[1] + h / 2],
		Matrix.invert(page.getTransform()))

	var s = "q /WmGS gs\n"
	if (TEXT) {
		addResource(pageObj, "Font", "WmFont", fontRef)
		var angle = ANGLE == -1 ? Math.atan2(h, w) * 180 / Math.PI : ANGLE
		var size = FONT_SIZE
		if (!size) {
			// the biggest size at which the rotated text fits in 80% of the page
			var c = Math.abs(Math.cos(angle * Math.PI / 180))
			var sn = Math.abs(Math.sin(angle * Math.PI / 180))
			var tw = textWidth(TEXT, 1), th = 0.7 // text height (capitals), at size 1
			size = 0.8 * Math.min(w / (tw * c + th * sn), h / (tw * sn + th * c))
		}
		var m = Matrix.concat(Matrix.rotate(angle), center)
		s += "q " + m.map(num).join(" ") + " cm BT /WmFont " + num(size) + " Tf " +
			COLOR.join(" ") + " rg " + num(-textWidth(TEXT, size) / 2) + " " +
			num(-0.35 * size) + " Td " + pdfString(TEXT) + " Tj ET Q\n"
	}
	if (image) {
		addResource(pageObj, "XObject", "WmImage", imageRef)
		var iw = IMAGE_WIDTH * w
		var ih = iw * image.getHeight() / image.getWidth()
		var m = Matrix.concat([iw, 0, 0, ih, -iw / 2, -ih / 2], center)
		s += "q " + m.map(num).join(" ") + " cm /WmImage Do Q\n"
	}
	s += "Q\n"

	// new page content: watermark, then the old content, or
	// q, the old content, Q, then watermark
	var contents = doc.newArray()
	if (UNDER)
		contents.push(doc.addStream(s))
	else
		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)
	if (!UNDER)
		contents.push(doc.addStream("\nQ " + s))
	pageObj.put("Contents", contents)
}

doc.save(scriptArgs[1], "garbage,compress")
```

The watermark is centered on the page as you see it, including landscape pages, pages with a rotation (`/Rotate`) and cropped pages. With `FONT_SIZE = 0` the text is as big as fits in 80% of each page, so it scales with the page size.

## Change the look

Edit the settings at the top of the script:

| You want                          | Setting                                      |
| --------------------------------- | -------------------------------------------- |
| Different text                    | `TEXT = "CONFIDENTIAL"`                      |
| Gray instead of red               | `COLOR = [0.5, 0.5, 0.5]`                    |
| Lighter                           | `OPACITY = 0.15`                             |
| Horizontal                        | `ANGLE = 0`                                  |
| Always 45 degrees                 | `ANGLE = 45`                                 |
| Fixed size, e.g. 72 points        | `FONT_SIZE = 72`                             |
| Serif font                        | `FONT = "Times-Bold"`                        |

`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`. Standard fonts are not embedded, so the file stays small. Use only Latin characters in `TEXT`.

## Put the watermark under the content

Set `UNDER = true`. The page's text and pictures are drawn on top of the watermark, so it never covers anything.

Note: Anything opaque on the page hides the watermark under it: images, colored backgrounds, and the whole page of a scanned document. For scans, keep `UNDER = false`.

## Add a logo or image watermark

Set `IMAGE` to the path of a PNG or JPEG file:

```js
var TEXT = ""               // "" for no text
var IMAGE = "C:\\Pictures\\logo.png"
var IMAGE_WIDTH = 0.4       // image width as a fraction of the page width
```

- The image is centered, `IMAGE_WIDTH` of the page width, with `OPACITY` applied.
- Keep `TEXT` to get both the text and the image.
- Transparent areas of a PNG stay transparent.
- Write `\\` for each `\` in the path, or use `/`: `"C:/Pictures/logo.png"`.

## Tips

- Write to a new file. Saving over the input file fails with `cannot remove file`.
- Keep the original: the watermark becomes part of the page content and there is no easy way to take it out.
- The watermark text is real text: it shows up when you search or copy text from the page.
- Adding a watermark changes the pages, so it breaks existing digital signatures. Add it before you sign.
- To watermark many files, run the script in a loop: see [Batch process PDFs](Batch-process-PDFs.md).

## See also

- [Add page numbers to a PDF](Add-page-numbers-to-PDF.md) — same approach, a number on every page
- [Add an image to a PDF](Add-an-image-to-a-PDF.md) — one image on one page
- [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
