paper-figure-clipper
A small toolbox for getting publication-quality, precisely-bounded crops out of academic PDFs, so paper notes can be image-rich without manual screenshotting.
This skill is the companion to cs-paper-reader: when a note needs to cite a specific figure / table / algorithm / equation, the reader skill invokes this skill to produce the PNG.
When to use this skill
Trigger this skill from inside cs-paper-reader (Mode A or Mode B) whenever the note benefits from showing an artifact verbatim. Concretely:
- The paper's §3 method depends on a non-trivial schematic (architecture diagram, data-flow chart, unit-cell figure — examples from an LLM-compression batch: SliceGPT's slicing diagram, OWL's outlier histogram).
- The paper's §4 results are easier to read as the original table than as transcribed numbers (e.g. multi-column ablations).
- The paper's algorithm pseudocode matters for reproducibility (any boxed
Algorithm N with non-trivial control flow — e.g. SparseGPT's Algorithm 1).
- A specific equation is too complex to faithfully reproduce in Markdown and would be better shown as a rendered crop.
Do NOT use this skill for:
- Rendering an entire page (use
scripts/clip.py page only as last-resort fallback).
- Decorative screenshots — every clip should be cited by name in the note.
Three operation modes (use them in this order)
scan — Run pdffigures2 over the whole PDF; emit a manifest.json listing every figure & table with bbox + caption + pre-rendered PNG + body_refs count. Use this once per paper to enumerate what's available.
~/.codex/skills/paper-figure-clipper/scripts/clip.py scan \
--pdf "<paper.pdf>" \
--out-dir "/tmp/paper-figs/<Method>"
Output path must be /tmp/paper-figs/<Method>/, never <output>/_figs/.... scan emits the full set of figures + manifest.json + side-products; only PNGs actually referenced by the final note get promoted into <output>/_figs/<Method>/ later (see §"Integration with cs-paper-reader" step 4). Writing scan output straight to <output>/ pollutes the cloud-synced deliverable with unused crops.
By default MuPDF re-renders each figure for sharper output (200 DPI). Add --no-rerender to skip and keep only pdffigures2's softer renderer; add --keep-pdffigures2-output to keep both side-by-side.
The manifest includes body_refs per entry — how many times the body text references Figure N / Fig. N / Table N. Sort by body_refs descending to prioritize embedding decisions: figures with body_refs ≥ 3 are almost always central; body_refs = 1 is usually supplementary.
clip — When you want a specific artifact that scan may have missed (algorithm blocks, individual equations, sub-figures), invoke the precise locator:
# Algorithm block (auto-bounded by horizontal rules)
clip.py clip --pdf P --out o.png algorithm --label "Algorithm 1" --page 5
# Figure / Table caption with body expansion
clip.py clip --pdf P --out o.png figure --label "Figure 2"
# Display equation by its (N) label
clip.py clip --pdf P --out o.png equation --label "3"
# Explicit bbox in PDF points (1pt = 1/72 inch)
clip.py clip --pdf P --out o.png bbox --page 4 --x0 54 --y0 100 --x1 300 --y1 250
page — Fallback when neither of the above resolves; renders an entire page at 200 DPI. Always prefer scan or clip over page.
Quality rules (the reason this skill exists)
- No percent-of-page heuristics. Every crop is anchored on a real PDF element: a text rect from
page.search_for, a horizontal rule from page.get_drawings(), a table object from page.find_tables(), or an explicit bbox.
- 200 DPI default. Below that, axis labels become unreadable. Override with
--dpi 300 for camera-ready quality (≈2× file size).
- 4-pt padding default to avoid hairline crops; override
--padding 0 only if you need a perfectly tight bbox.
- The PDF is rendered once, then cropped — never scale a small crop up. This preserves anti-aliasing.
- JSON-first output: every command prints a single JSON line with
ok, page, bbox_pt, dpi, output. Notes can grep these for citation.
- Contact-sheet QA for multiple selected crops. When several selected crops from one paper need visual QA, prefer inspecting one contact sheet in
/tmp/paper-figs/<Method>/qa_contact_sheet.png rather than opening each PNG separately; promote only crops whose visible content matches the intended Figure/Table/Algorithm.
Integration with cs-paper-reader
When cs-paper-reader is in Mode A or Mode B and decides a note needs a figure (see triggers above):
- First time per paper: run
clip.py scan to a temp staging area (/tmp/paper-figs/<Method>/). Read manifest.json to learn which figures exist and their captions.clip.py scan --pdf "<paper.pdf>" --out-dir "/tmp/paper-figs/<Method>"
- For each citation: pick the relevant entry from the manifest, or run
clip.py clip for items the scan missed (also to /tmp/paper-figs/<Method>/).
- While drafting, reference with the final relative path (not the
/tmp/ staging path). Alt text is minimal; the explanation lives as a red-font body block above or below the image (Markdown viewers render alt text as hover tooltips, so anything inside [ ] is invisible to readers on first scroll):<font color="red">**下方算法**</font>:<one to three sentences explaining the key lines the reader should look at>.

See ~/.codex/skills/cs-paper-reader/references/image-embedding-recipes.md §0 for the position rule (schematic → image above with 下图解释, results table → image below with 上表解释, etc.) and the alt-text discipline (label + ≤ 25 字 topic only).
- After the note is finalized: promote only the actually-referenced images to the permanent
<output>/_figs/<Method>/ directory. This avoids polluting the cloud-synced workspace with unused crops.grep -oE '!\[[^]]*\]\(([^)]+)\)' <note.md> | sed -E 's/.*\]\(([^)]+)\)/\1/' | while read img; do
src="/tmp/paper-figs/<Method>/$(basename $img)"
dst="<output>/$img"
mkdir -p "$(dirname "$dst")"
cp "$src" "$dst"
done
Key principle: the cloud-synced directory only contains images that are actually referenced in notes. All intermediate/exploratory crops stay in /tmp/ and are ephemeral.
Naming convention for output PNGs
Use the same schema in both the /tmp staging dir and the final <output>/_figs/ dir so promotion is a flat cp:
/tmp/paper-figs/<Method>/<type>_<label>_p<page>.png # staging (clip.py writes here)
<output>/_figs/<Method>/<type>_<label>_p<page>.png # final (only referenced PNGs end up here)
Examples (illustrative — <Method> is the paper's method/acronym, from any subfield):
_figs/SparseGPT/algorithm_1_p5.png
_figs/OWL/figure_1_p2.png
_figs/SliceGPT/table_3_p7.png
_figs/FLAP/equation_4_p4.png
This keeps file names deterministic so notes can pre-link images with the final relative path (_figs/<Method>/...) before promotion, and the promote step is just a basename-preserving copy.
Limitations to be aware of
pdffigures2 is excellent on standard two-column CS papers (NeurIPS / ICML / ICLR / ACL). On single-column tech reports, accuracy drops — fall back to clip mode.
- Figures rendered as embedded PNG/JPEG with low source resolution will not improve at higher DPI. The clip will just enlarge pixels; check the source PDF first.
clip equation assumes the equation has a numbered (N) label on the right margin. Unnumbered equations need explicit bbox mode.
clip algorithm assumes the algorithm is bounded by horizontal rules (standard algorithm / algorithm2e LaTeX style). Non-rule-bounded pseudocode (e.g. ICML's algorithmic package without algorithm floats, ICLR templates using only top/bot section rules, or pseudo-code typeset inside a figure env) will fail with {"ok": false, "error": "no horizontal rules above/below anchor on page N ..."} and exit code 1. This is by design — silently returning a caption-only crop was a worse failure mode. When you hit this error, fall back to clip bbox (via the R4.5 4-candidate salvage recipe in references/recipe-book.md).
See also
references/locator-design.md — full design of the four locators (search / rules / figure-expansion / table-finder).
references/recipe-book.md — copy-pasteable command snippets for the common cases.
~/.codex/skills/cs-paper-reader/SKILL.md — the upstream paper-reading skill that triggers this one.
1---2name: paper-figure-clipper3description: Precisely extract figures, tables, algorithms, equations, or arbitrary regions from research-paper PDFs, with bbox-anchored cropping (not percent-of-page heuristics). Companion to `cs-paper-reader` for image-rich note-taking.4---56# paper-figure-clipper78A small toolbox for getting **publication-quality, precisely-bounded** crops out of academic PDFs, so paper notes can be image-rich without manual screenshotting.910This skill is the companion to `cs-paper-reader`: when a note needs to cite a specific figure / table / algorithm / equation, the reader skill invokes this skill to produce the PNG.1112## When to use this skill1314Trigger this skill from inside `cs-paper-reader` (Mode A or Mode B) whenever the note benefits from showing an artifact verbatim. Concretely:1516- The paper's **§3 method** depends on a non-trivial schematic (architecture diagram, data-flow chart, unit-cell figure — examples from an LLM-compression batch: SliceGPT's slicing diagram, OWL's outlier histogram).17- The paper's **§4 results** are easier to read as the original table than as transcribed numbers (e.g. multi-column ablations).18- The paper's **algorithm pseudocode** matters for reproducibility (any boxed `Algorithm N` with non-trivial control flow — e.g. SparseGPT's `Algorithm 1`).19- A **specific equation** is too complex to faithfully reproduce in Markdown and would be better shown as a rendered crop.2021Do NOT use this skill for:22- Rendering an entire page (use `scripts/clip.py page` only as last-resort fallback).23- Decorative screenshots — every clip should be cited by name in the note.2425## Three operation modes (use them in this order)26271. **`scan`** — Run `pdffigures2` over the whole PDF; emit a `manifest.json` listing every figure & table with bbox + caption + pre-rendered PNG + `body_refs` count. Use this once per paper to enumerate what's available.2829 ```bash30 ~/.codex/skills/paper-figure-clipper/scripts/clip.py scan \31 --pdf "<paper.pdf>" \32 --out-dir "/tmp/paper-figs/<Method>"33 ```34 **Output path must be `/tmp/paper-figs/<Method>/`**, never `<output>/_figs/...`. `scan` emits the full set of figures + `manifest.json` + side-products; only PNGs actually referenced by the final note get promoted into `<output>/_figs/<Method>/` later (see §"Integration with cs-paper-reader" step 4). Writing scan output straight to `<output>/` pollutes the cloud-synced deliverable with unused crops.35 By default MuPDF re-renders each figure for sharper output (200 DPI). Add `--no-rerender` to skip and keep only pdffigures2's softer renderer; add `--keep-pdffigures2-output` to keep both side-by-side.3637 The manifest includes `body_refs` per entry — how many times the body text references `Figure N` / `Fig. N` / `Table N`. **Sort by `body_refs` descending to prioritize embedding decisions**: figures with `body_refs ≥ 3` are almost always central; `body_refs = 1` is usually supplementary.38392. **`clip`** — When you want a specific artifact that `scan` may have missed (algorithm blocks, individual equations, sub-figures), invoke the precise locator:4041 ```bash42 # Algorithm block (auto-bounded by horizontal rules)43 clip.py clip --pdf P --out o.png algorithm --label "Algorithm 1" --page 54445 # Figure / Table caption with body expansion46 clip.py clip --pdf P --out o.png figure --label "Figure 2"4748 # Display equation by its (N) label49 clip.py clip --pdf P --out o.png equation --label "3"5051 # Explicit bbox in PDF points (1pt = 1/72 inch)52 clip.py clip --pdf P --out o.png bbox --page 4 --x0 54 --y0 100 --x1 300 --y1 25053 ```54553. **`page`** — Fallback when neither of the above resolves; renders an entire page at 200 DPI. Always prefer `scan` or `clip` over `page`.5657## Quality rules (the reason this skill exists)5859- **No percent-of-page heuristics.** Every crop is anchored on a real PDF element: a text rect from `page.search_for`, a horizontal rule from `page.get_drawings()`, a table object from `page.find_tables()`, or an explicit bbox.60- **200 DPI default.** Below that, axis labels become unreadable. Override with `--dpi 300` for camera-ready quality (≈2× file size).61- **4-pt padding default** to avoid hairline crops; override `--padding 0` only if you need a perfectly tight bbox.62- **The PDF is rendered once, then cropped** — never scale a small crop up. This preserves anti-aliasing.63- **JSON-first output**: every command prints a single JSON line with `ok`, `page`, `bbox_pt`, `dpi`, `output`. Notes can grep these for citation.64- **Contact-sheet QA for multiple selected crops.** When several selected crops from one paper need visual QA, prefer inspecting one contact sheet in `/tmp/paper-figs/<Method>/qa_contact_sheet.png` rather than opening each PNG separately; promote only crops whose visible content matches the intended Figure/Table/Algorithm.6566## Integration with cs-paper-reader6768When `cs-paper-reader` is in Mode A or Mode B and decides a note needs a figure (see triggers above):69701. **First time per paper**: run `clip.py scan` to a **temp staging area** (`/tmp/paper-figs/<Method>/`). Read `manifest.json` to learn which figures exist and their captions.71 ```bash72 clip.py scan --pdf "<paper.pdf>" --out-dir "/tmp/paper-figs/<Method>"73 ```742. **For each citation**: pick the relevant entry from the manifest, or run `clip.py clip` for items the scan missed (also to `/tmp/paper-figs/<Method>/`).753. **While drafting, reference with the final relative path** (not the `/tmp/` staging path). Alt text is minimal; the explanation lives as a red-font body block above or below the image (Markdown viewers render alt text as hover tooltips, so anything inside `[ ]` is invisible to readers on first scroll):76 ```markdown77 <font color="red">**下方算法**</font>:<one to three sentences explaining the key lines the reader should look at>.7879 80 ```81 See `~/.codex/skills/cs-paper-reader/references/image-embedding-recipes.md` §0 for the position rule (schematic → image above with `下图解释`, results table → image below with `上表解释`, etc.) and the alt-text discipline (label + ≤ 25 字 topic only).824. **After the note is finalized**: promote only the actually-referenced images to the permanent `<output>/_figs/<Method>/` directory. This avoids polluting the cloud-synced workspace with unused crops.83 ```bash84 grep -oE '!\[[^]]*\]\(([^)]+)\)' <note.md> | sed -E 's/.*\]\(([^)]+)\)/\1/' | while read img; do85 src="/tmp/paper-figs/<Method>/$(basename $img)"86 dst="<output>/$img"87 mkdir -p "$(dirname "$dst")"88 cp "$src" "$dst"89 done90 ```9192**Key principle**: the cloud-synced directory only contains images that are actually referenced in notes. All intermediate/exploratory crops stay in `/tmp/` and are ephemeral.9394## Naming convention for output PNGs9596Use the same schema in both the `/tmp` staging dir and the final `<output>/_figs/` dir so promotion is a flat `cp`:9798```99/tmp/paper-figs/<Method>/<type>_<label>_p<page>.png # staging (clip.py writes here)100<output>/_figs/<Method>/<type>_<label>_p<page>.png # final (only referenced PNGs end up here)101```102103Examples (illustrative — `<Method>` is the paper's method/acronym, from any subfield):104- `_figs/SparseGPT/algorithm_1_p5.png`105- `_figs/OWL/figure_1_p2.png`106- `_figs/SliceGPT/table_3_p7.png`107- `_figs/FLAP/equation_4_p4.png`108109This keeps file names deterministic so notes can pre-link images with the **final** relative path (`_figs/<Method>/...`) before promotion, and the promote step is just a basename-preserving copy.110111## Limitations to be aware of112113- `pdffigures2` is excellent on standard two-column CS papers (NeurIPS / ICML / ICLR / ACL). On single-column tech reports, accuracy drops — fall back to `clip` mode.114- Figures rendered as **embedded PNG/JPEG** with low source resolution will not improve at higher DPI. The clip will just enlarge pixels; check the source PDF first.115- `clip equation` assumes the equation has a numbered `(N)` label on the right margin. Unnumbered equations need explicit `bbox` mode.116- `clip algorithm` assumes the algorithm is bounded by horizontal rules (standard `algorithm` / `algorithm2e` LaTeX style). Non-rule-bounded pseudocode (e.g. ICML's `algorithmic` package without `algorithm` floats, ICLR templates using only top/bot section rules, or pseudo-code typeset inside a `figure` env) will fail with `{"ok": false, "error": "no horizontal rules above/below anchor on page N ..."}` and exit code 1. **This is by design** — silently returning a caption-only crop was a worse failure mode. When you hit this error, fall back to `clip bbox` (via the R4.5 4-candidate salvage recipe in `references/recipe-book.md`).117118## See also119120- `references/locator-design.md` — full design of the four locators (search / rules / figure-expansion / table-finder).121- `references/recipe-book.md` — copy-pasteable command snippets for the common cases.122- `~/.codex/skills/cs-paper-reader/SKILL.md` — the upstream paper-reading skill that triggers this one.