From oh-my-claudecode-research
Converts Keynote/PowerPoint slides into cropped vector PDFs for LaTeX and low-res PNGs for outlines, automating figure placement and link insertion.
How this skill is triggered — by the user, by Claude, or both
Slash command
/oh-my-claudecode-research:cropfigThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<Purpose>
<Use_When>
/sync command needs to embed figure-only PNGs into an outline document at each result section.<Do_Not_Use_When>
| Variable | Default | Used by | Purpose |
|---|---|---|---|
DECK_FILE | (required) | func 1, 2, 3 | Path to a .key or .pptx deck. Step 1 + 2 read it directly; step 3 derives <deck_parent>/pdf/ and <deck_parent>/png/ from it. |
MANUSCRIPT_DIR | paper | func 3 | LaTeX manuscript root. Cropped PDFs land in <MANUSCRIPT_DIR>/figures/figureNN.pdf. Matches /omcr-setup's default. |
OUTLINE_FILE | outline.md | func 3 | Outline markdown file. PNGs land in <dirname(OUTLINE_FILE)>/figures/figureNN.png; image links are inserted into the outline itself. |
RESULT_PATTERN | ^### Result (\d+) | func 3 | Regex with a single capture group = figure number. Used to find result headings in the outline. |
CROPFIG_PROBE_DPI | 100 | func 2 | DPI used for the probe rasterization that drives crop-bound detection. Higher = slower but slightly tighter crops. |
CROPFIG_PNG_DPI | 120 | func 2 | Output DPI for the outline PNGs. Raise for sharper outline review (and larger docx); lower for slimmer docx. |
Output locations.
<dirname($DECK_FILE)>/
├── <deck>.key|pptx # the deck itself
├── pdf/figureNN.pdf # func 2 writes (manuscript-grade vector)
└── png/figureNN.png # func 2 writes (outline-grade raster)
$MANUSCRIPT_DIR/
└── figures/figureNN.pdf # func 3 copies here for the .tex
dirname($OUTLINE_FILE)/
├── $OUTLINE_FILE # func 3 inserts/updates  links
└── figures/figureNN.png # func 3 copies here for the markdown
These defaults match /omcr-setup's conventions (Manuscript dir: paper/, Outline file: outline.md, Result pattern: ^### Result (\d+)). See wiki/Configuration.md for the CLAUDE.md Research-stack schema.
On first invocation with no DECK_FILE and no project Deck file in CLAUDE.md, ask the user for the deck path then offer to persist it to the project's CLAUDE.md.
export_deck.py)Run the exporter with a temp staging directory:
STAGE=$(mktemp -d)
python3 "$CLAUDE_PLUGIN_ROOT"/skills/cropfig/export_deck.py "$DECK_FILE" "$STAGE"
Dispatch inside export_deck.py — each format uses its native app to avoid cross-format import drift:
.key → macOS Keynote via AppleScript → single deck PDF..pptx → macOS PowerPoint via AppleScript when /Applications/Microsoft PowerPoint.app is present; otherwise LibreOffice headless (soffice --convert-to pdf).The single deck PDF is then split into figure01.pdf, figure02.pdf, … using PyMuPDF. These per-slide PDFs are vector (text and lines stay sharp at any zoom) and feed Step 2.
Guard: if export fails (missing tool, permission denied, headless env), surface the error and stop.
crop_figures.py)python3 "$CLAUDE_PLUGIN_ROOT"/skills/cropfig/crop_figures.py "$STAGE"
rm -rf "$STAGE"
This writes to <dirname($DECK_FILE)>/pdf/ and <dirname($DECK_FILE)>/png/. To write elsewhere, pass positional output dirs:
python3 "$CLAUDE_PLUGIN_ROOT"/skills/cropfig/crop_figures.py "$STAGE" "$PDF_OUT" "$PNG_OUT"
For each slide PDF, the script:
CROPFIG_PROBE_DPI probe and runs find_crop_bounds from crop_bounds.py (band-classification heuristic on top + bottom; tight-bbox on remaining content).pdf/figureNN.pdf.CROPFIG_PNG_DPI → png/figureNN.png next to the deck.Both outputs share the same crop decision because the PNG is a downsampled view of the manuscript PDF.
upload_figures.py)python3 "$CLAUDE_PLUGIN_ROOT"/skills/cropfig/upload_figures.py
Reads from <deck_parent>/pdf/ and <deck_parent>/png/ (so func 2 must have run first). Does two things:
3a. Manuscript copy. For each figureNN.pdf, copies to $MANUSCRIPT_DIR/figures/figureNN.pdf. The .tex is not modified — \includegraphics{figures/figureNN} is author content.
3b. Outline embed. For each figureNN.png:
<dirname($OUTLINE_FILE)>/figures/figureNN.png.$RESULT_PATTERN. For each match, the captured N is the figure number; insert (or replace, if previously inserted by this script) the link  immediately after the heading (skipping blank lines).The embed is idempotent — re-running replaces the existing link line with the same content rather than appending a new one. Result heading without a matching figure is reported as missing; figure with no matching result heading is reported as orphan.
## Cropfig run — YYYY-MM-DD
- Deck: $DECK_FILE (N slides)
- Manuscript PDFs: <dirname($DECK_FILE)>/pdf/ (N files)
- Outline PNGs: <dirname($DECK_FILE)>/png/ (N files, $CROPFIG_PNG_DPI DPI)
- Top label removed: A / N
- Bottom caption removed: B / N
Upload:
- $MANUSCRIPT_DIR/figures/: N PDF(s) copied
- $OUTLINE_FILE: M heading(s) updated (missing: ..., orphan: ...)
Slides where heuristic skipped a side (still tight-bboxed):
- <filename> — <which side>
If Step 1 failed, report only that and skip the rest. Steps 2 and 3 can run independently if a prior staging dir or PDF/PNG dir already exists.
<Output_Contract>
| Path | Owner | Content | Used by |
|---|---|---|---|
<dirname($DECK_FILE)>/pdf/figureNN.pdf | func 2 | cropped vector PDF (no top label, no bottom caption, tight-bbox); page CropBox set | source of truth — func 3 reads here for the manuscript copy |
<dirname($DECK_FILE)>/png/figureNN.png | func 2 | rasterized view of the same cropped PDF at CROPFIG_PNG_DPI | source of truth — func 3 reads here for the outline copy |
$MANUSCRIPT_DIR/figures/figureNN.pdf | func 3 (copy) | identical to the source PDF | \includegraphics{figures/figureNN} in the .tex |
<dirname($OUTLINE_FILE)>/figures/figureNN.png | func 3 (copy) | identical to the source PNG |  in the outline |
$OUTLINE_FILE | func 3 (modifies) | image link inserted after each result heading | outline rendering (GitHub, VS Code preview, Pandoc → docx) |
Filenames mirror across the four image locations so callers can pair figureNN.pdf with figureNN.png. The staging dir from Step 1 is ephemeral and deleted at the end of the run; no uncropped intermediate is persisted.
</Output_Contract>
<Failure_Modes>
| Symptom | Cause | Action |
|---|---|---|
ModuleNotFoundError: fitz | PyMuPDF not installed | python3 -m pip install pymupdf |
ModuleNotFoundError: numpy / PIL | bare environment | python3 -m pip install numpy pillow |
ERROR: .key export requires macOS + Keynote | running on non-macOS with a .key deck | run on macOS, or re-save the deck as .pptx and retry |
Keynote export failed: ... | Keynote not installed, deck password-protected, or AppleScript permission denied | grant System Settings → Privacy & Security → Automation → Terminal/your shell → Keynote |
PowerPoint export failed: ... | PowerPoint not licensed/signed in, deck password-protected, or AppleScript permission denied | grant System Settings → Privacy & Security → Automation → Terminal/your shell → Microsoft PowerPoint. Failures are no longer silently swallowed — there is no auto-fallback when PowerPoint IS installed. |
PowerPoint export failed: ... -9074 | Office sandbox refused to write to the staging path | the script already stages under ~/Library/Caches/cropfig/ to satisfy Office sandbox; if you see this anyway, check that ~/Library/Caches/ is writable and Office has been granted Full Disk Access if your deck is on a network volume |
LibreOffice (soffice) not on PATH | Linux/PowerPoint-not-installed path with no LibreOffice | brew install --cask libreoffice (mac) / apt install libreoffice (linux) |
| Top label or bottom caption NOT removed on a slide | band thinner than MIN_LABEL_HEIGHT=30 rows, or contains a colored element (heuristic treats it as figure content) | accept — tight-bbox still removes whitespace; don't globally lower the threshold to catch one slide |
| Cropped PDF still shows label when opened in some viewers | viewer ignores CropBox and renders MediaBox | use a viewer that honors CropBox (Preview, Acrobat, most LaTeX rasterizers do); the PNG path is unaffected |
| Output PNG looks blurry in docx | CROPFIG_PNG_DPI too low | raise to 150 / 180 (file sizes grow proportionally) |
func 3 reports headings without matching figure: #N | outline has more ### Result N headings than the deck has slides for | either add the missing slide to the deck, or trim the outline heading; func 3 will not invent a figure |
func 3 reports orphan figures: figureN | deck has more slides than the outline has result headings | extend the outline with ### Result N, or accept the orphan if those slides are non-result (concept, schematic, appendix) |
WARN (3b copy): PNG source dir not found | func 2 has not run yet, or wrote to a different location | run func 1 + 2 first; verify <dirname($DECK_FILE)>/png/ exists |
</Failure_Modes>
- `SKILL.md` — this file. - `export_deck.py` — func 1: deck → per-slide vector PDFs. Dispatches by extension: `.key` → Keynote AppleScript, `.pptx` → PowerPoint AppleScript when installed, else LibreOffice headless. Splits the resulting deck PDF via PyMuPDF. Stages under `~/Library/Caches/cropfig/` on macOS so Office sandbox can write. - `crop_figures.py` — func 2: per-slide PDF → cropped PDF (vector, manuscript) + raster PNG (outline). Both share one crop decision. - `upload_figures.py` — func 3: copies cropped PDFs to `$MANUSCRIPT_DIR/figures/`, copies cropped PNGs to `/figures/`, inserts/updates `` links in the outline after each `$RESULT_PATTERN` match. Idempotent. - `crop_bounds.py` — pure heuristic module. `find_top_cut`, `find_bottom_cut`, `find_crop_bounds(arr)` — used by `crop_figures.py`. Tune constants at the top if your slide-deck layout changes (most likely tweak: `MIN_LABEL_HEIGHT` for shorter labels).npx claudepluginhub youngeun1209/oh-my-claudecode-research --plugin oh-my-claudecode-researchGenerates slide deck images from content with configurable styles, slide counts, and audience targeting. Can output outlines, prompts, images, or merge into PDF/PPTX.
Generates professional slide deck images from Markdown, text, or URLs with 16 visual styles, supports CJK/Latin mixed text, branding overlays, and PPTX/PDF export.
Creates slide decks for scientific presentations in PowerPoint and LaTeX Beamer, offering structure, design templates, timing guidance, and visual validation. Use for conferences, seminars, defenses.