Remove image-based watermarks from PDF files by surgically deleting the drawing instructions from content streams and removing XObject resource references. This produces clean results with no visible artifacts (no white/black rectangles, no color mismatches).
The removal process targets the root cause — the PDF content stream instructions that draw the watermark image — rather than covering or replacing the image:
q...cm.../Name Do...Q drawing instructionThis approach leaves zero visual artifacts because the watermark simply never gets drawn.
Before running the script, determine the watermark's characteristics:
gamma.app)Execute scripts/remove_watermark.py:
bash
python scripts/remove_watermark.py <input.pdf> <output.pdf> [options]
Common options:
OptionDefaultDescription--x-threshold700X position threshold — images with x0 > this value are candidates--min-w / --max-w100 / 200Width range for watermark detection--min-h / --max-h20 / 50Height range for watermark detection--remove-linksoffAlso remove link annotations--link-domainallOnly remove links containing this domain
Gamma watermark example:
bash
python scripts/remove_watermark.py input.pdf output.pdf --remove-links --link-domain gamma.app
Custom position watermark (e.g. top-left):
bash
python scripts/remove_watermark.py input.pdf output.pdf --x-threshold 0 --min-w 50 --max-w 300 --min-h 10 --max-h 80
Open the output PDF and check:
ProblemCauseSolutionWatermark still visiblePosition/size doesn't match detection thresholdsAdjust --x-threshold, --min-w/max-w, --min-h/max-h
Why not overlay/replace? Three approaches that DON'T work well:
delete_image API — Only clears image data, doesn't remove the XObject reference or content stream Do instruction; renders as blank/colored rectangleThe correct approach (used by this skill): Delete the Do instruction from the content stream
and the /Name xref R entry from the XObject dictionary. The watermark image data becomes an
orphaned object that gets garbage-collected on save.
pip install pymupdf)