# minimax-pdf A Claude skill for creating and editing visually polished PDFs. Three routes. One design system. Tokens flow from content analysis through every renderer. ## Quick start ```bash bash scripts/make.sh check # verify deps bash scripts/make.sh fix # auto-install missing deps bash scripts/make.sh demo # → demo.pdf ``` --- ## Route A: CREATE — generate a new PDF ```bash bash scripts/make.sh run \ --title "Q3 Strategy Review" \ --type "proposal" \ --author "Strategy Team" \ --date "October 2025" \ --content content.json \ --out report.pdf ``` **`--type` options:** | Type | Palette | Cover pattern | Google Fonts (cover) | |---|---|---|---| | `report` | Deep ink, teal accent | `fullbleed` | Playfair Display / IBM Plex Sans | | `proposal` | Near-black, amber accent | `split` | Syne / Nunito Sans | | `resume` | White, navy accent | `typographic` | DM Serif Display / DM Sans | | `portfolio` | Deep violet, coral accent | `atmospheric` | Fraunces / Inter | | `academic` | Warm white, navy accent | `typographic` | EB Garamond / Source Sans 3 | | `general` | Dark slate, blue accent | `fullbleed` | Outfit / Outfit | | `minimal` | Near-white, red accent | `minimal` | Cormorant Garamond / Jost | | `stripe` | Dark navy, amber accent | `stripe` | Barlow Condensed / Barlow | | `diagonal` | Dark blue, teal accent | `diagonal` | Montserrat / Montserrat | | `frame` | Warm cream, brown accent | `frame` | Cormorant / Crimson Pro | | `editorial` | White, red accent | `editorial` | Bebas Neue / Libre Franklin | | `magazine` | Warm linen, deep navy accent | `magazine` | Playfair Display / EB Garamond | | `darkroom` | Deep navy, steel blue accent | `darkroom` | Playfair Display / EB Garamond | | `terminal` | Near-black, neon green accent | `terminal` | Space Mono | | `poster` | White, near-black accent | `poster` | Barlow Condensed / Courier Prime | **content.json block types:** ```json [ {"type": "h1", "text": "Section Title"}, {"type": "h2", "text": "Subsection"}, {"type": "h3", "text": "Sub-subsection"}, {"type": "body", "text": "Paragraph. Supports bold and italic."}, {"type": "bullet", "text": "Unordered list item"}, {"type": "numbered","text": "Ordered list item — counter auto-resets between lists"}, {"type": "callout", "text": "Key insight or highlighted finding"}, {"type": "table", "headers": ["Col A", "Col B"], "rows": [["a", "b"], ["c", "d"]] }, {"type": "image", "path": "chart.png", "caption": "Figure 1: optional caption"}, {"type": "code", "text": "def hello():\n print('world')"}, {"type": "math", "text": "\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}", "label": "(1)"}, {"type": "divider"}, {"type": "caption", "text": "Table 1: standalone caption label"}, {"type": "pagebreak"}, {"type": "spacer", "pt": 16} ] ``` --- ## Route B: FILL — fill form fields in an existing PDF ```bash # See what fields the PDF has bash scripts/make.sh fill --input form.pdf --inspect # Fill fields bash scripts/make.sh fill \ --input form.pdf \ --out filled.pdf \ --values '{"FirstName": "Jane", "Agree": "true", "Country": "US"}' # Or from a JSON file bash scripts/make.sh fill --input form.pdf --out filled.pdf --data values.json ``` Field value rules: - `text` → any string - `checkbox` → `"true"` or `"false"` - `dropdown` → must match a choice value shown by `--inspect` - `radio` → must match a radio value shown by `--inspect` --- ## Route C: REFORMAT — apply design to an existing document ```bash bash scripts/make.sh reformat \ --input source.md \ --title "Annual Report" \ --type "report" \ --author "Research Team" \ --out output.pdf ``` Supported input: `.md` `.txt` `.pdf` `.json` --- ## Architecture ``` SKILL.md ← Claude entry point, route table design/design.md ← Aesthetic system (read before CREATE/REFORMAT) scripts/ make.sh ← Unified CLI palette.py ← metadata → tokens.json [CREATE, REFORMAT] cover.py ← tokens.json → cover.html [CREATE, REFORMAT] render_cover.js ← cover.html → cover.pdf [CREATE, REFORMAT] render_body.py ← tokens + content → body.pdf [CREATE, REFORMAT] merge.py ← cover + body → final.pdf [CREATE, REFORMAT] fill_inspect.py ← PDF → field list [FILL] fill_write.py ← PDF + values → filled PDF [FILL] reformat_parse.py ← doc → content.json [REFORMAT] ``` Design tokens (`tokens.json`) flow from `palette.py` to every renderer — cover and body are always visually consistent. ## Dependencies | Tool | Used by | Install | |---|---|---| | Python 3.9+ | all `.py` scripts | system | | `reportlab` | `render_body.py` | `pip install reportlab` | | `pypdf` | fill, merge, reformat | `pip install pypdf` | | Node.js 18+ | `render_cover.js` | system | | `playwright` + Chromium | `render_cover.js` | `npm install -g playwright && npx playwright install chromium` | ## License MIT ## Document types | `--type` | Mood | Cover pattern | Cover fonts | |---|---|---|---| | `report` | Authoritative | `fullbleed` | Playfair Display / IBM Plex Sans | | `proposal` | Confident | `split` | Syne / Nunito Sans | | `resume` | Clean | `typographic` | DM Serif Display / DM Sans | | `portfolio` | Expressive | `atmospheric` | Fraunces / Inter | | `academic` | Scholarly | `typographic` | EB Garamond / Source Sans 3 | | `general` | Neutral | `fullbleed` | Outfit | | `minimal` | Restrained | `minimal` | Cormorant Garamond / Jost | | `stripe` | Bold | `stripe` | Barlow Condensed / Barlow | | `diagonal` | Dynamic | `diagonal` | Montserrat | | `frame` | Classical | `frame` | Cormorant / Crimson Pro | | `editorial` | Editorial | `editorial` | Bebas Neue / Libre Franklin | Cover fonts load via Google Fonts `@import` at render time — no local caching. Body pages always use system fonts (Times / Helvetica) via ReportLab. ## content.json schema ```json [ {"type": "h1", "text": "Section Title"}, {"type": "h2", "text": "Subsection"}, {"type": "h3", "text": "Sub-subsection"}, {"type": "body", "text": "Paragraph text. Supports bold and italic."}, {"type": "bullet", "text": "Unordered list item"}, {"type": "numbered","text": "Ordered list item — auto-numbered, counter resets between lists"}, {"type": "callout", "text": "Highlighted insight or key finding"}, {"type": "table", "headers": ["Column A", "Column B", "Column C"], "rows": [["row1a", "row1b", "row1c"], ["row2a", "row2b", "row2c"]] }, {"type": "image", "path": "chart.png", "caption": "Figure 1: Sales by quarter"}, {"type": "code", "text": "SELECT * FROM users\nWHERE active = 1;"}, {"type": "math", "text": "\\sigma = \\sqrt{\\frac{1}{N}\\sum_{i=1}^N (x_i - \\mu)^2}", "label": "(2)"}, {"type": "divider"}, {"type": "caption", "text": "Table 2: standalone label"}, {"type": "pagebreak"}, {"type": "spacer", "pt": 16} ] ``` ## Architecture ``` SKILL.md ← Claude entry point, routing only design/design.md ← Aesthetic system (read before any script) scripts/ make.sh ← Unified CLI: check / fix / run / demo palette.py ← content metadata → tokens.json cover.py ← tokens.json → cover.html render_cover.js ← cover.html → cover.pdf (Playwright) render_body.py ← tokens.json + content.json → body.pdf (ReportLab) merge.py ← cover.pdf + body.pdf → final.pdf + QA report ``` Design tokens (color, typography, spacing) are written once by `palette.py` and consumed by every downstream script. This guarantees visual consistency between cover and body without any manual coordination. ## Dependencies | Tool | Purpose | Install | |---|---|---| | Python 3.9+ | palette, cover, render_body, merge | system | | `reportlab` | Body page rendering | `pip install reportlab` | | `pypdf` | Merging PDFs | `pip install pypdf` | | Node.js 18+ | Cover rendering | system | | `playwright` | Headless Chromium for cover | `npm install -g playwright && npx playwright install chromium` | Run `bash scripts/make.sh check` to verify everything at once. Run `bash scripts/make.sh fix` to auto-install what is missing. ## License MIT