A lean, layout-aware Python library for programmatically extracting PDF annotations (highlights, strikeouts, underlines, carets, sticky notes) and resolving comment threads. Built on PyMuPDF.
- Native text matching — uses PyMuPDF's C-engine
clipextraction per markup quad, so multi-line highlights don't over-capture words from skipped lines (and no slow Python overlap loops). - Reading-order aware — relies on PyMuPDF's geometric
sort=Trueinstead of fragileblock_noordering. - Comment threading — resolves reply trees via the native
annot.irt_xrefproperty (/IRT), structuring comments as parent → replies. - Caret mapping — detects caret insertions and emits
[Caret before|after "<word>"]with markers (^/==^==) at the insertion point. - Point-annotation proximity — binds sticky notes / carets to the nearest word within a bounded distance (no spurious binding to far-away text).
- Line-end dehyphenation — joins hyphenated words split across line breaks.
- Encrypted PDFs — explicit fail-fast with an optional
password=argument. - Clean JSON —
contextis plain text;context_highlightedcarries the==marker==formatting. Dates are ISO 8601. - CLI —
pdf-comments extract file.pdf -o out.json.
# with uv (recommended)
uv pip install pdf-comment-extractor
# or with pip
pip install pdf-comment-extractorRequires Python ≥ 3.10 and PyMuPDF ≥ 1.24.0 (installed automatically).
from pdf_comment_extractor import extract_comments, save_json, save_markdown
# Extract with threads resolved (pass password= for encrypted PDFs)
threads = extract_comments("paper.pdf")
save_json(threads, "comments.json")
save_markdown(threads, "paper.pdf", "comments.md")
for annot in threads:
print(annot.page, annot.type, "→", annot.target_text)
for reply in annot.replies:
print(" reply by", reply.author, ":", reply.comment)# Pretty JSON to stdout
pdf-comments extract paper.pdf
# Write JSON + a markdown report
pdf-comments extract paper.pdf -o comments.json -m comments.md
# Encrypted PDF
pdf-comments extract paper.pdf --password hunter2 -o comments.json
# Disable hyphen-join across line breaks
pdf-comments extract paper.pdf --no-dehyphenate -o comments.jsonpdf_comment_extractor/
├── pdf_comment_extractor/
│ ├── __init__.py # Public API
│ ├── core.py # Extraction + thread resolution engine
│ ├── models.py # Annotation dataclass
│ ├── reporter.py # JSON & Markdown exporters
│ └── cli.py # `pdf-comments` CLI
├── tests/
│ ├── test_extractor.py
│ ├── response_to_reviewers.pdf
│ └── prohome.pdf
├── pyproject.toml
├── LICENSE
└── README.md
uv sync # install dev deps (ruff, pytest)
uv run pytest -q # run tests
uv run ruff check . # lint- Reading order:
sort=Trueis geometric and improves on rawblock_noordering, but is not a guaranteed column-aware reader for complex multi-column layouts. - Dehyphenation: legitimate hyphenated compounds (e.g. "well-known") split across lines may lose their hyphen, since this cannot be resolved reliably without a dictionary.
MIT — see LICENSE.