Automate PDF watermarking with Ghostscript
Ghostscript is a powerful, open-source tool for PDF manipulation, offering robust capabilities for watermarking, compression, and document processing. This guide explores how to leverage Ghostscript to add a visible text watermark to every page, then optionally reduce image resolution for a smaller output. Work on copies: PDF rewriting can change forms, annotations, color and signatures.
Install and configure Ghostscript
Install a supported, security-patched Ghostscript package. On Ubuntu and Debian-based systems:
sudo apt-get update
sudo apt-get install ghostscript
For macOS users with Homebrew:
brew install ghostscript
Verify the installation and version:
gs --version
Create a watermark with an EndPage procedure
Ghostscript can run a PostScript EndPage procedure after drawing each PDF page. Unlike a standalone
annotation or a command run after the PDF, this draws the text on every emitted page. Create
watermark.ps in your working directory:
%!PS-Adobe-3.0
<< /EndPage {
2 eq {
pop false
} {
pop
gsave
initgraphics
4 dict begin
/size currentpagedevice /PageSize get def
/label (CONFIDENTIAL) def
size 0 get 2 div size 1 get 2 div translate
/Helvetica-Bold findfont 36 scalefont setfont
/side size aload pop 2 copy gt { exch } if pop def
/factor side 0.8 mul label stringwidth pop div def
factor 1 lt { factor dup scale } if
45 rotate
0.75 setgray
label dup stringwidth pop -2 div 0 moveto show
end
grestore
true
} ifelse
} >> setpagedevice
Resetting the graphics state keeps placement independent of PDF page rotation. The text scales down on narrow pages so the complete watermark stays inside the page.
Apply watermark securely
Run this in a directory without an existing watermarked.pdf. Load the procedure before the
input PDF. Leaving paper-size overrides out preserves the input page dimensions:
gs -dSAFER \
-dBATCH \
-dNOPAUSE \
-sDEVICE=pdfwrite \
-dCompatibilityLevel=1.7 \
-dPDFSETTINGS=/prepress \
-sOutputFile=watermarked.pdf \
-f watermark.ps input.pdf
Optimize PDF compression
Ghostscript offers presets that trade image quality for file size. The result is not guaranteed to be smaller, and this separate command does not add a watermark:
gs -dSAFER \
-dBATCH \
-dNOPAUSE \
-sDEVICE=pdfwrite \
-dCompatibilityLevel=1.7 \
-dPDFSETTINGS=/ebook \
-sOutputFile=compressed.pdf \
input.pdf
Available -dPDFSETTINGS options:
/screen: low-resolution output, with a 72 dpi color/grayscale downsampling target./ebook: medium-resolution output, with a 150 dpi color/grayscale downsampling target./printer: print-oriented output, with a 300 dpi color/grayscale downsampling target./prepress: prepress-oriented settings; not a guarantee that every color value stays unchanged./default: general-purpose output, not a promise to preserve every original setting.
See the Ghostscript PDF output documentation for the complete preset settings. Inspect the output visually before replacing a source document.
Enhanced automation script
Save this as process.sh beside watermark.ps, then run bash process.sh input.pdf new-results.
The output directory must not already exist. A failed stage removes only this invocation's output;
the original PDF and existing result directories remain untouched.
#!/bin/bash
set -euo pipefail
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <input-pdf> <new-output-directory>" >&2
exit 1
fi
INPUT_FILE=$1
OUTPUT_DIR=$2
WATERMARK_FILE=$PWD/watermark.ps
STAMPED_FILE=stamped.pdf
COMPRESSED_FILE=compressed.pdf
[[ $INPUT_FILE = /* ]] || INPUT_FILE=$PWD/$INPUT_FILE
[[ $OUTPUT_DIR = /* ]] || OUTPUT_DIR=$PWD/$OUTPUT_DIR
if [ ! -f "$INPUT_FILE" ]; then
echo "Error: Input PDF not found" >&2
exit 1
fi
if [ ! -f "$WATERMARK_FILE" ]; then
echo "Error: watermark.ps not found in the working directory" >&2
exit 1
fi
if ! mkdir -m 700 -- "$OUTPUT_DIR"; then
echo "Error: Use a new output directory" >&2
exit 1
fi
cleanup() {
local status=$?
if [ "$status" -ne 0 ]; then
rm -f -- "$OUTPUT_DIR/$STAMPED_FILE" "$OUTPUT_DIR/$COMPRESSED_FILE"
rmdir -- "$OUTPUT_DIR"
fi
}
trap cleanup EXIT
cd -P -- "$OUTPUT_DIR"
OUTPUT_DIR=$PWD
process_pdf() {
local output=$1
local settings=$2
shift 2
if ! gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite \
-dCompatibilityLevel=1.7 -dPDFSETTINGS="$settings" \
-sOutputFile="$output" -f "$@"; then
echo "Error: PDF processing failed" >&2
exit 1
fi
}
echo "Adding watermark..."
process_pdf "$STAMPED_FILE" /prepress "$WATERMARK_FILE" "$INPUT_FILE"
echo "Optimizing file size..."
process_pdf "$COMPRESSED_FILE" /ebook "$OUTPUT_DIR/$STAMPED_FILE"
echo "Processing complete: $OUTPUT_DIR/$COMPRESSED_FILE"
Security considerations
When processing PDFs with Ghostscript:
- Keep
-dSAFERenabled; it restricts file access but is not a complete sandbox. - Verify input files before processing
- Use appropriate file permissions for output files
- Keep Ghostscript updated to the latest version for security patches
- Check the page count, dimensions, visible watermark and rendered content after processing.
Run untrusted documents under a restricted operating-system account or isolated container with
time and memory limits. Treat watermark.ps as executable code: keep it trusted and do not insert
unescaped user input into it. A visible watermark does not encrypt a document or prevent copying.
Version compatibility
Use a maintained Ghostscript release and check its installed version. Key compatibility notes:
- PDF compatibility level
1.7is recommended for modern workflows - Existing digital signatures are invalidated when the PDF is rewritten.
- Legacy PostScript workflows remain supported but may require updates
Conclusion
Ghostscript can stamp every PDF page and apply a compression preset in a repeatable workflow. Preserve the input, fail visibly when either stage fails, and inspect the result before publishing it.
For automated document processing at scale, consider exploring Transloadit's document processing services, which handle PDF operations securely and efficiently in the cloud.
