PDF去水印超能力 (v1.0.3)
Overview
This skill removes watermarks from PDF files using three strategies, tried in order:
- Artifact-based removal (lossless, surgical): Strips
/Artifact <</Subtype/Watermark...>> BDC ... EMCblocks from page content streams. Only content streams that actually contain watermark blocks are modified; sibling streams on the same page stay intact. Does not touch page text at all. - Text-based removal (redaction): Searches for the watermark text string via PyMuPDF and applies redaction annotations. Auto-detection uses a high-confidence rule (large font, light colour, or many occurrences) so body text is never redacted by accident.
- Image-based removal (color detection + inpainting): For scanned/rasterized PDFs where the watermark is baked into page images. Detects watermark-colored pixels and inpaints them using OpenCV.
No-rework guarantee (v1.0.2): every strategy is saved to a temp file and verified before it becomes the final output. If a strategy fails verification (watermark remains, body text changed, or the file is corrupt), the next strategy runs automatically. The output file is never overwritten by a failed attempt. If all strategies fail, no output file is produced and the error is reported.
The skill auto-detects the watermark type and picks the best strategy. The user can override with --text or --force-image.
When to Use
Trigger this skill for requests such as:
- "Remove the watermark from this PDF."
- "Delete the diagonal text watermark from these PDFs."
- "Make a clean copy of the watermarked PDF."
- Batch processing: "Remove watermarks from all PDFs in this folder."
- "帮我去掉这个PDF的水印。" / "批量去除PDF上的水印。"
- "Create a reusable script/skill to remove 'XXX' watermark from PDFs."
Dependencies
- Python 3.x
pymupdf(PyMuPDF) —pip install pymupdfnumpy,opencv-python-headless— only needed for image-based removal (Strategy 3)
Workflow
- Auto-detect watermark type. Check the first few pages for:
-/Artifact ... /Subtype /Watermark ... BDC ... EMCblocks → Strategy 1
- Searchable watermark text → Strategy 2 (high-confidence rule: large font ≥18pt, light colour, or ≥5 occurrences; body text is never redacted by accident)
- Single full-page image XObject (scanned PDF) → Strategy 3 - Apply the strategy to all pages, save to a temp file, and verify before promoting:
- No artifact blocks remain; watermark text is gone; body text is unchanged (artifact strategy requires byte-identical text); image streams still decode. - Promote or fallback: on verification failure, the next strategy runs automatically; on success, the temp file is promoted to the final output. The original file is never touched, and a failed attempt never overwrites a good output.
- Save the promoted file with
garbage=4, deflate=True, clean=True.
Implementation Details
Strategy 1: Artifact-based removal (preferred)
Watermark blocks in the content stream:
/Artifact <</Subtype/Watermark/Type/Pagination>>BDC
q
/KSPE186 gs
0.707107 0.707107 -0.707107 0.707107 X Y cm
/KSPX185 Do
Q
EMC
Regex: /Artifact\s*<<[^>]*?/Subtype\s*/Watermark[^>]*?>>\s*BDC.*?EMC (DOTALL). Remove all matches from each page's content stream via doc.update_stream(xref, cleaned_bytes).
v1.0.2 fix: only content streams that actually contain watermark blocks are modified; sibling content streams on the same page are left intact (previous versions cleared xrefs[1:], which could delete unrelated content).
Strategy 2: Text-based removal
Auto-detection scans spans in the first pages and matches against a watermark word list (English: DRAFT, CONFIDENTIAL, SAMPLE, WATERMARK, COPY; Chinese: 样章, 内部资料, 仅供试读, 仅供试学, 试读版, 版权所有, 翻版必究, 严禁外传, 内部使用). A word is only treated as a watermark when it has at least one of:
- font size ≥ 18pt (large watermark text)
- very light colour (near-white/grey, sum of RGB > 600)
- ≥ 5 occurrences on sampled pages
This prevents redacting body text that merely contains a common word like "COPY" twice. ASCII words match with word boundaries; Chinese words match as substrings.
for page in doc:
for rect in page.search_for(watermark_text):
page.add_redact_annot(rect)
page.apply_redactions(images=pymupdf.PDF_REDACT_IMAGE_NONE)
Strategy 3: Image-based removal (scanned PDFs)
For PDFs where each page is a single rasterized image (common in "标清" / standard-definition scans), the watermark is baked into the pixels. Removal steps:
- Extract the image XObject from each page via
doc.extract_image(xref). - Detect watermark pixels by color distance to the watermark color. If
--wm-coloris not given, auto-detect the dominant saturated non-background colour (S > 60, V 40–250) from the image. - Dilate the mask with a 7×7 elliptical kernel (1 iteration) to cover stroke edges.
- Inpaint using
cv2.inpaint(img, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA). - Update the XObject stream with an explicit
/Filter /FlateDecode,/ColorSpace /DeviceRGB,/BitsPerComponent 8dictionary (doc.xref_set_key), so the rewritten stream always decodes correctly.
v1.0.2 fix: the XObject stream is now rewritten with explicit Filter / ColorSpace / BitsPerComponent / Width / Height keys. The previous version wrote raw RGB bytes without these keys, which could corrupt the PDF in some viewers.
Key parameters:
- --wm-color R,G,B: specify a custom watermark color; otherwise auto-detected per page
- --force-image: skip artifact/text detection and go straight to image inpainting
- Color distance threshold: 35 (tight, to minimize false positives)
- Inpaint radius: 3 pixels
- Minimum pixels to trigger a page: 50
Scripts
scripts/remove_pdf_watermark.py— standalone CLI script implementing all three strategies.- Single file:
python remove_pdf_watermark.py input.pdf - Custom output:
python remove_pdf_watermark.py input.pdf -o clean.pdf - Batch:
python remove_pdf_watermark.py folder/*.pdf -o output_folder/ - Text fallback:
python remove_pdf_watermark.py input.pdf --text "DRAFT" - Image mode with custom color:
python remove_pdf_watermark.py input.pdf --force-image --wm-color 133,94,119
Run with --help for full usage.
Safety Notes
- Always write output to a new file; never overwrite the original PDF by default.
- Image-based removal (Strategy 3) re-encodes page images. File size may increase slightly. Quality is preserved with FlateDecode (lossless) compression.
- After removal, verify that non-watermark content is preserved. For artifact removal, compare extracted text before and after. For image removal, visually inspect sample pages.
- The default watermark color (RGB 133,94,119) is a purple/magenta commonly used by online course providers. For any other watermark, use
--wm-colorto specify the exact color, or analyze the PDF first to detect the watermark color.
Parameter Reference
| Parameter | Purpose | Example |
|---|---|---|
-o PATH |
Output file (single) or output folder (batch) | -o clean.pdf |
--text "STR" |
Force Strategy 2 with an explicit watermark string | --text "DRAFT" |
--force-image |
Skip detection, go straight to Strategy 3 | --force-image |
--wm-color R,G,B |
Custom watermark color for Strategy 3 | --wm-color 133,94,119 |
Run python remove_pdf_watermark.py --help for the full list.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Watermark still there after Strategy 1 | Watermark is a plain text object, not a tagged artifact | Retry with --text "<watermark text>" |
| Nothing removed on a scanned PDF | Watermark baked into page pixels | Use --force-image |
| Image mode removes too little | Default color does not match | Sample the exact color and pass --wm-color |
| Image mode removes too much | Color threshold too loose | Use a more precise --wm-color instead of a near-black/near-white value |
| Output file is larger | Strategy 3 re-encodes page images | Expected; compression is lossless (FlateDecode) |
| Script reports it cannot open the file | PDF is encrypted or permission-restricted | Decrypt it first; this skill does not bypass PDF permissions |
| Text confirmed removed but a faint ghost remains | Anti-aliased edge pixels | Increase inpaint coverage, or accept it — it is usually invisible in print |
Choosing a Strategy Deliberately
The auto-detection is right most of the time, but you can be faster and safer by choosing directly:
- The PDF came from Word / LaTeX / a report generator → the watermark is almost certainly a tagged artifact. Strategy 1. Text stays byte-identical.
- You can select the watermark text with the cursor → Strategy 2 with
--text. - The page is one big image (you cannot select anything) → Strategy 3. This is the only one that re-encodes images, so verify a few pages afterwards.
Rule of thumb: always try the least destructive strategy first. Strategy 1 never touches page content; Strategy 3 rewrites pixels and is the only one that can slightly soften an image.
中文使用速查
# 单文件,输出到新文件(最常用)
python scripts/remove_pdf_watermark.py 输入.pdf -o 干净.pdf
# 整文件夹批量,输出到指定目录
python scripts/remove_pdf_watermark.py 文件夹/*.pdf -o 输出目录/
# 扫描件(水印烤进像素),指定水印颜色
python scripts/remove_pdf_watermark.py 输入.pdf --force-image --wm-color 133,94,119
# 已知水印文字,直接按文字清除
python scripts/remove_pdf_watermark.py 输入.pdf --text "样章"
依赖安装:pip install pymupdf;扫描件模式另需 pip install numpy opencv-python-headless。
请仅处理您拥有合法权利或已获授权的文档。
Changelog
v1.0.3 (2026-11-01)
- 头像更新:替换上架物料头像(剪贴板图片,512×512 PNG)。
- 脚本流程与 v1.0.2 一致(级联验证、防返工、防误删、防误伤正文、图像流修复、跨盘安全)。
v1.0.2 (2026-11-01)
- No-rework pipeline: every strategy saves to a temp file and is verified (watermark gone, body text unchanged, images decode) before being promoted to the final output; failed strategies cascade to the next one, and a failed attempt never overwrites a good output.
- Artifact removal fix: only content streams that actually contain watermark blocks are modified; sibling streams on the same page are no longer cleared.
- Text auto-detection hardened: watermark words must have a high-confidence signal (font ≥18pt, light colour, or ≥5 occurrences); body text containing common words (COPY, SAMPLE...) is never redacted. ASCII words match with word boundaries.
- Image stream fix: rewritten XObjects now carry explicit
/Filter /FlateDecode,/ColorSpace /DeviceRGB,/BitsPerComponent 8,/Width,/Heightkeys so the PDF stays valid. - Cross-drive safe: temp files are created in the output directory; promotion uses
shutil.moveas fallback. - Chinese watermark word list added (样章 / 内部资料 / 仅供试读 …).
v1.0.1 (2026-10-28)
- Renamed slug to
pdf-watermark-removal-superpower(English translation of PDF去水印超能力); published to SkillHub. - Added author field
我帮你做.