unmark
Remove pen/pencil markings from a PDF - fully offline. No AI, no network. Your files never leave your machine.
pip install unmark
unmark erase "marked.pdf" # -> "marked clean.pdf"
erase - the main feature
Strips the markings and hands back a clean PDF. It picks the right path automatically:
- Digital annotations (marks added in GoodNotes / Preview / Adobe, etc.) → deleted losslessly; the original page is restored exactly, text stays text.
- Marks baked into a scan → colour ink (red/blue/green pen) is removed by
channel-dominance detection that leaves the printed text untouched. Options:
--pencil(light-grey graphite),--lines(experimental underline erase).
unmark erase "scan.pdf" out.pdf --pencil # also remove light pencil
unmark erase "scan.pdf" --sat 10 # remove more colour (may leave less)
unmark erase "notes.pdf" --lossless # PNG pages instead of JPEG
What erase can and can't do (honest)
| mark | result |
|---|---|
| digital annotation | ✅ removed perfectly (lossless) |
| red / blue / green pen | ✅ removed; text preserved |
| light-grey pencil | ⚙️ removed with --pencil |
| dark pen / heavy pencil at the same tone as the print | ❌ cannot be removed offline - it carries no colour or brightness difference from the real letters. Only AI or manual erasing gets those. |
reflow - experimental
⚠️ Experimental.
reflowOCRs the printed text (offline, via RapidOCR) and re-typesets a fresh PDF. On clean, single-column papers it's decent. On dense, two-column, or heavily marked papers it is unreliable - it misreads circled option markers, over-splits questions, and leftover dark handwriting bleeds into the text. Always proofread. Offline OCR simply can't match a vision model on messy scans; that's a hard ceiling, not a bug.
pip install "unmark[ocr]" # adds the offline OCR engine
unmark reflow "scan.pdf" --columns 1 # single-column works best
unmark reflow "scan.pdf" --pages 1-4 # limit to some pages
How it works
erase: PyMuPDF renders each page → numpy colour/pencil masking → repackaged as a
compact greyscale PDF (or the annotation layer is simply deleted).
reflow: colour-clean → split columns → RapidOCR → parse → re-typeset (Story API).
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unmark-0.1.0.tar.gz.
File metadata
- Download URL: unmark-0.1.0.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d007c8e511bd23a1f132662a99d5ed1a92c1b5c79e77897c5bc9e3b36b995c03
|
|
| MD5 |
8fbb1efa6f34a9084da6fe0248bbe0cf
|
|
| BLAKE2b-256 |
e4dcf37b022aa8ece0524ebbdf8e0cc612a7edce76c8868f7e26023de9939960
|
File details
Details for the file unmark-0.1.0-py3-none-any.whl.
File metadata
- Download URL: unmark-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
33bbbbdab49517ddf068b3613e7c75e853f32a109f445896f5076b74136c7226
|
|
| MD5 |
8bce2f105721a58f0857e444c4446300
|
|
| BLAKE2b-256 |
fa44186f10efa9aafaf852dcaaed35b0062b706a65d3c6b0f7f237444ea81bc2
|