Install
$ agentstack add skill-neuromechanist-research-skills-scientific-figure ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Scientific Figure
Compose publication-quality multi-panel figures at exact journal dimensions (mm/pt), validate font sizes against journal minima before export, and write PDF/PNG that the journal will accept without resize.
Why this skill exists
The previous react-pdf workflow had two structural failures:
- Composition did not respect physical size. Flexbox layout shifted dimensions in subtle ways during render.
- Fonts shrank below readable thresholds. When content overflowed, react-pdf uniformly scaled the entire figure, sometimes pushing axis labels under 4 pt.
This skill replaces that workflow. Panels are placed at exact mm coordinates, text is preserved as SVG `` elements so font sizes are inspectable before export, and the validator rescales individual panels rather than the whole figure when a font minimum is violated.
Pipeline
1. Plan 2. Build elements 3. Compose 4. Validate fonts 5. Export
(journal (matplotlib/seaborn (svgutils, at (per-element pt vs (Inkscape if
size, panel per panel; SVG out; exact mm/pt; journal minimum; present;
grid) optional icons) text preserved) rescale panel if cairosvg
below minimum) otherwise)
Every step uses on-the-fly execution via uv run --with so no permanent installs are required for the skill itself.
Step 1: Plan the figure
Before generating anything, fix the following:
- Target journal — sets the canvas width. See
references/journal-specs.mdfor the full table; the four most common are summarized below. - Panel grid — 1x1, 1x2, 2x2, wide-top + sub-panels, or freeform mm coordinates.
- Color palette — pick from
references/color-palettes.mdand reuse across all panels.
Journal width / font minimum cheat sheet
| Journal | 1 column | 2 column | Min body font | Notes | |---|---|---|---|---| | Nature | 89 mm | 183 mm | 5 pt | 8 pt for panel labels | | Science | 55 mm | 120 mm | 6 pt | Myriad/Helvetica preferred | | Cell | 85 or 112 mm | 174 mm | 6 pt | Max 225 mm tall | | PNAS | 87 mm | 180 mm | 6 pt | No label below 2 mm tall |
The validator in step 4 enforces these. Pick the journal early so the validator can warn early.
Step 2: Build the elements
Each panel is generated independently as an SVG. Common sources:
- Plots: matplotlib/seaborn, saved with
bbox_inches='tight', transparent=True, format='svg'. See the[[plot-styling]]skill for the library decision tree and SciencePlots recipes. - Icons: transparent PNGs from the
[[transparent-icons]]skill. - Schematics: for new Python-driven work use
[[svg-primitives]](auto-fit boxes, validated arrows, layered z-order); for hand-authored SVG patterns see[[svg-figure]].
Save each element to a working directory (typically panels/), then compose them in step 3.
Step 3: Compose the figure
The composer is built around svgutils (MIT license; uv run --with svgutils). It places panels at exact mm coordinates and preserves text as inspectable SVG `` elements.
Two ways to compose: the Figure helper in scripts/compose.py (most cases) and direct svgutils.compose for full control.
Recipe A: helper (recommended)
uv run --with svgutils --with lxml python scripts/compose.py panels-config.json -o figure.svg
panels-config.json schema:
{
"width_mm": 183,
"height_mm": 120,
"journal": "nature",
"panels": [
{"id": "A", "src": "panels/spectrum.svg", "x_mm": 0, "y_mm": 0, "scale": 0.5, "label": "A"},
{"id": "B", "src": "panels/topomap.svg", "x_mm": 92, "y_mm": 0, "scale": 0.5, "label": "B"},
{"id": "C", "src": "panels/timecourse.svg", "x_mm": 0, "y_mm": 60, "scale": 1.0, "label": "C", "width_mm": 183}
]
}
Panel labels (A, B, C) are placed at top-left of each panel in 12 pt bold sans-serif.
Recipe B: direct svgutils (full control)
from svgutils.compose import Figure, SVG, Panel, Text
fig = Figure(
"183mm", "120mm",
Panel(
SVG("panels/spectrum.svg").scale(0.5),
Text("A", 5, 15, size=12, weight="bold"),
).move(0, 0),
Panel(
SVG("panels/topomap.svg").scale(0.5),
Text("B", 5, 15, size=12, weight="bold"),
).move(92, 0),
)
fig.save("figure.svg")
See references/composition-workflow.md for the patterns (panel scaling, label placement, scale bars, multi-panel grid utilities).
Step 4: Validate fonts before export
This is the step that prevents the journal-rejection scenario. Run:
uv run --with lxml python scripts/validate_fonts.py figure.svg --journal nature
The validator parses every ` and element with a font-size`, walks the accumulated transform stack to compute the effective font size at the final physical dimensions, and reports anything below the journal minimum. Output is JSON:
{
"svg": "figure.svg",
"journal": "nature",
"minimum_pt": 5.0,
"checked_count": 47,
"skipped_count": 0,
"issue_count": 1,
"issues": [
{
"text": "Frequency (Hz)",
"specified_pt": 9.0,
"effective_pt": 4.5,
"scale_x": 0.5,
"scale_y": 0.5,
"minimum_pt": 5.0,
"tag_id": ""
}
]
}
skipped_count counts text elements where no font-size could be resolved (CSS class selectors, inherited styles). Exit codes: 0 clean, 1 issues found, 2 script error (malformed SVG, missing file).
If a panel is the culprit (its .scale() is too small), three remedies:
- Rescale that panel up (and other panels down) instead of accepting the small text.
- Increase the source plot's font size so that even at panel scale 0.5 it still passes (e.g., 12 pt source → 6 pt at 0.5 scale, which passes Science 6 pt minimum).
- Switch to a larger canvas (e.g., upgrade 1-col to 1.5-col).
See references/font-validation.md for the full mechanics and rationale.
Step 5: Export to PDF/PNG
uv run --with cairosvg python scripts/export.py figure.svg --out figure.pdf --dpi 300
export.py detects Inkscape on $PATH at runtime. When present, Inkscape produces the highest-fidelity PDF (text remains text, fonts subsetted). When absent, the script falls back to cairosvg with a stderr warning that text without an installed font may be converted to paths or skipped.
Installing Inkscape (one-time, recommended)
brew install inkscape # macOS
sudo apt install inkscape # Debian/Ubuntu
brew install inkscape is a single line and a ~200 MB one-time cost; once installed the script auto-detects it. The cairosvg fallback works without Inkscape but produces lower-fidelity PDFs when journal-required fonts are not installed.
Caption guidelines
Generate a figure caption that:
- Starts with a concise title (bold, one sentence).
- Describes each panel: "(A) Description. (B) Description. (C) Description."
- Defines all abbreviations on first use (e.g., "electroencephalography (EEG)").
- States sample sizes, statistical tests, and error bar meanings.
- States scale bar values if present.
Quality assurance
The [[figure-qa]] agent proactively runs on the composed SVG to check geometric correctness, alignment, color-palette compliance, and label legibility. The agent invokes validate_fonts.py for the font-size pass under the hood and the SVG branch's palette and geometry checks alongside.
Additional resources
references/composition-workflow.md— svgutils patterns and idiomsreferences/font-validation.md— pt minimum mechanics, transform-stack mathreferences/journal-specs.md— full table of journal dimensions and font rulesreferences/color-palettes.md— colorblind-safe palettes (Wong, Okabe-Ito, viridis, Crameri)scripts/compose.py— svgutils composer (CLI + library)scripts/validate_fonts.py— font-size validatorscripts/export.py— Inkscape/cairosvg exporterexamples/two-column-figure.py— end-to-end working example (matplotlib panels → compose → validate → export)
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: neuromechanist
- Source: neuromechanist/research-skills
- License: BSD-3-Clause
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.