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
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 = 0sizes it to fit the page. - Angle:
ANGLE = -1follows the page diagonal;0is horizontal. - Transparency:
OPACITYfrom0(invisible) to1(solid). - Over or under:
UNDER = truedraws it behind the page content. - Logo:
IMAGEadds a PNG or JPEG at the center of every page.
Add a text watermark #
- Save the script as
add-watermark.js. - Change
TEXTand the other settings at the top of the script, if you want. - 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 with the same arguments works the same.// 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: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_WIDTHof the page width, withOPACITYapplied. - Keep
TEXTto 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.
See also #
- Add page numbers to a PDF — same approach, a number on every page
- Add an image to a PDF — one image on one page
- Batch process PDFs — run the script on many files
- sumatrapdf-tool run — how scripts run
- JavaScript API reference — the objects the script uses