Extract numbered figures and tables from a local academic-paper PDF into named PNG files by detecting caption blocks and cropping the associated page regions. Use when a paper note needs inline figures or tables, when raw page images are not useful, or when the user asks to extract figure-level assets from a PDF.
Installs into .claude/skills of the current project.
Are you the author of Paper Figures?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/alfredzhang98-paper-figures)
---
name: paper-figures
description: Extract numbered figures and tables from a local academic-paper PDF into named PNG files by detecting caption blocks and cropping the associated page regions. Use when a paper note needs inline figures or tables, when raw page images are not useful, or when the user asks to extract figure-level assets from a PDF.
---
# Paper Figures
Run the bundled extraction script with the interpreter and paths rendered by the repository installer:
```bash
"{{PAPER_FIGURES_PYTHON}}" "{{PAPER_FIGURES_SCRIPT}}" \
"<paper.pdf>" "<output-directory>" [options]
```
Before running it, verify that `{{PAPER_FIGURES_PYTHON}}` and `{{PAPER_FIGURES_SCRIPT}}` were rendered and that both paths exist. If either placeholder remains literal, stop and tell the user to rerun the repository installer.
For an `ai-wiki` paper note, use:
```bash
"{{PAPER_FIGURES_PYTHON}}" "{{PAPER_FIGURES_SCRIPT}}" \
"<paper.pdf>" \
"{{AI_WIKI_DIR}}/_attachments/paper-figures/<paper-slug>"
```
`{{AI_WIKI_DIR}}` is installer-rendered and defaults to `0ai_wiki`. The paper slug must match the paper-note filename without `.md`.
## Options
- `--zoom 2.0` — render zoom; 2.0 is approximately 144 DPI and is the default.
- `--pages 1,4,9` — additionally render selected 1-indexed pages as `page_N_full.png`.
- `--figures-only` — extract figure captions only.
- `--tables-only` — extract table captions only.
- `--max-fig-height-ratio 0.85` — cap the inferred crop height as a fraction of page height.
`--figures-only` and `--tables-only` are mutually exclusive.
## Output contract
The script writes:
- `figN.png` for each detected figure caption.
- `tableN.png` for each detected table caption.
- `page_N_full.png` for requested full pages.
- A JSON manifest on stdout with `pdf`, `out_dir`, `figures`, `tables`, `full_pages`, and `warnings`. Each asset entry includes the file name, source page, caption when applicable, pixel dimensions, and the crop `rect` in PDF coordinates.
Capture or inspect the manifest. Confirm that an asset exists and its crop is plausible before embedding it in a note.
## Warnings — read these first
`warnings` is the one part of the output the script can vouch for. It is empty in the normal case; anything in it names a crop that is wrong, and both checks are exact rather than heuristic:
- **`figN.png also contains figM`** — the crop reached past its own figure and took a neighbour's caption with it. Caption detection is reliable; inferring how far a figure extends above its caption is not, and this is what that failure looks like from the inside. An asset entry also carries `"swallowed": [...]`.
- **`the body refers to figN ... but no caption was detected`** — the caption block did not parse, so no crop was produced at all. Without this the manifest simply has fewer entries than the paper has figures and nothing says so.
Warnings also go to stderr, because a warning buried in a long manifest is a warning nobody reads.
**Size is not a usable substitute for either check.** Measured across three papers, crops that had genuinely swallowed a neighbour were only 1.65–1.68× the median crop height — inside the normal spread — and a table truncated to its first rows measured 0.96× the median, i.e. invisible.
**When a warning fires, do not re-run the detector.** Render the page with `--pages N` and crop by coordinates; the inference that produced the bad rect will produce it again.
**Silence is not a clean bill of health.** Truncation (a crop cut short, losing the last rows of a table) and overrun into body text produce no warning, because neither puts another caption inside the rect. Still open every crop you intend to embed.
## Detection model
Caption matching accepts blocks beginning with `Figure N`, `Fig. N`, or `Table N`.
- For a figure, the script looks above its caption. It prefers overlapping raster-image bounds, falls back to vector drawing bounds, and uses nearby text as a boundary.
- For a table, it looks below its caption and uses horizontal rules plus nearby paragraph text to estimate the table body.
- The height cap prevents a failed inference from silently consuming nearly the whole page.
- Duplicate numbers on later pages receive a `-p<page>` suffix.
This is a heuristic, not a semantic figure parser. Always verify important crops.
## Failure recovery
| Symptom | Likely cause | Recovery |
|---|---|---|
| Crop is a thin strip or contains only the caption | caption placement differs from the assumed layout | render the source page with `--pages N`, inspect it, then crop manually |
| Figure top is missing | a nearby text fragment became the upper boundary | render the full page; a larger height cap helps only when the inferred boundary is otherwise correct |
| Table body is missing | the paper places table captions below tables | render the full page and crop manually |
| No captions detected | scanned PDF, unusual labels, or broken text extraction | the script renders the first three pages as a fallback; request the relevant pages explicitly |
| Output is too small | render zoom is insufficient | rerun with a larger `--zoom`, considering memory and file size |
Do not silently replace a failed crop with an unverified full-page image.
## Embed in Obsidian
Place each verified asset in the section where it supports the explanation:
```markdown

```
Use interpretive alt text rather than repeating the printed caption. Keep the paper note's figure mapping table synchronized with the actual files.
## Dependencies and rights
The script requires Python, PyMuPDF, and Pillow in the installer-created environment. PyMuPDF and Pillow retain their own licenses; the repository license does not replace them.
Paper figures can be copyrighted even when the PDF is accessible. Use extracted assets for the user's research notes and do not republish them without appropriate permission or a valid exception.