| name | pdf |
| description | Unified PDF skill — generate, reformat, fill, and read PDFs. Covers: text-to-PDF (reports, resumes, proposals, 可视化报告), LaTeX thesis, Markdown→PDF conversion, PDF form filling, and PDF reading/extraction/OCR. Trigger on any task with PDF as primary input or output. Not for DOCX or PPT.
|
| descriptions | {"zh-Hans":"生成、重排、填写和读取 PDF,支持报告、简历、提案、Markdown/LaTeX 转 PDF、表单和 OCR。"} |
| metadata | {"version":"3.0","category":"document-pdf"} |
pdf
Unified PDF skill. The model chooses the route based on user intent; this SKILL.md is an index. Each
route has its own guide in docs/. Read the guide before authoring or running anything.
Operational rules — read before doing anything
1. Match user query against docs/pitfalls-index.md FIRST. It
contains 10 production-ready canonical query templates (P1–P10), each with a
Match signatures block (sample queries) and a complete executable prompt that already encodes
every known pitfall, verification gate, and fall-back path. Workflow:
- Scan the Quick lookup table — match user's query keywords to a row.
- Copy the matching canonical query verbatim, substitute the
Slots (e.g. {PDF_PATH},
{OUTPUT_PATH}) with the user's actual values, and execute step-by-step.
- Multiple partial matches → fuse: take the strictest verification from each, never relax a
constraint.
- No match → fall back to the Routes / route guides below.
Do NOT skip verification steps in the canonical queries — they exist because past evaluation runs
shipped wrong outputs without them.
2. Locate before bulk-extracting any non-trivial PDF. Three independent thresholds, all
enforced together:
| Threshold | Rule |
|---|
| >20 pages AND user wants a specific datum (not the whole document) | locate-first is mandatory — do not run pdfplumber over every page; build a heading index first (pypdf outline → printed TOC → keyword grep) |
| 2 blind grep passes without landing on the target | stop and build the heading index, regardless of page count — a 3rd / 4th / 5th keyword search is the most common time sink |
| >200 pages | always build a heading index up-front, even before the first grep — at this size 6-8 blind greps balloon into 30+ shell calls |
See docs/read-guide.md §3 for the actual outline / TOC / grep recipes.
3. Chart pages and complex financial tables — vision, ONE PAGE PER CALL, mandatory. Any page
with a chart / diagram / info-graphic whose values matter, or any page with a complex
financial / regulatory table (balance sheet, income statement, cash-flow, debt schedule —
multi-level headers, merged cells, footnoted sub-totals), MUST be read visually, one page per
call, never a range — pdfplumber returns scrambled fragments on these layouts even when the PDF
is text-native, and packing neighbour pages into one image makes the model mis-attribute values to
the wrong page. Pick the visual path by model capability:
- Vision-capable model (e.g. M3 — can natively accept image input): rasterise that single page
to PNG (
pdftoppm / pypdfium2, or scripts/render/page_rasterize.py) and read the PNG
directly with the Read tool. Faster, offline, no upstream LLM call — this is the default now.
- Text-only model (e.g. M2.7 — cannot see images): fall back to
read_pdf_vision.py invoked
with --pages N (a single page), which ships the page to the matrix vision MCP.
4. Verify HTML→PDF page size and chart presence after every render. Always pass --format A4
or --format Letter explicitly to make.sh render — Chromium overrides CSS @page { size } when
the CLI flag is missing. After render:
pdfinfo out.pdf | grep "Page size"
pdfimages -list out.pdf | tail -n +3 | wc -l
pdftotext cannot see images and will silently pass a chart-less deck. Both checks are mandatory.
5. Don't suppress stderr. 2>/dev/null is never the right choice in this skill
(make.sh render/reformat/fill, read_pdf_vision.py, pdfinfo, pdftotext, pdfimages,
qpdf). On failure you lose the only signal that explains why and have to rerun blind. If output
is too noisy, redirect to a log file and grep on demand:
python3 -m scripts.read_pdf_vision --input report.pdf --pages 5 \
2>/tmp/vision.log
6. Always serialise JSON with ensure_ascii=False. When this skill writes a JSON config /
manifest that a downstream step parses (chart data, content manifests, form values), use
json.dumps, never hand-concatenate strings. CJK / smart quotes / em-dashes in data are the most
common reason a "looks fine" JSON file fails to json.load():
Path("content.json").write_text(
json.dumps(payload, ensure_ascii=False, indent=2),
encoding="utf-8",
)
7. AcroForm fill — copy the one canonical pypdf snippet. In pypdf ≥ 4 the only working pattern
is PdfWriter(clone_from=src) + update_page_form_field_values(...)
set_need_appearances_writer(True). clone_reader_document_root, direct /Annots patching,
and append_pages_from_reader all silently produce a PDF with no values written — there is no
error to debug. See docs/forms-guide.md §B.
8. Header/footer discipline for generated PDFs. For any formal or multi-page PDF (contracts,
reports, proposals, forms, manuals, translated documents), decide the header/footer strategy
before rendering: preserve source headers/footers when present; otherwise add a conservative
running header/footer or explicitly justify why none is appropriate (e.g. cover-only one-pager).
Reserve print-space so running elements do not collide with body content, tables, signatures, or
charts. Verification must include a visual check of at least one body page and the
final/signature/table-heavy page, not only pdftotext. Implementation details live in
docs/html-pdf-spec.md §3.3.
9. DOCX→PDF is a DOCX-native render/export task, not an HTML task. When the user asks to
convert a Word/DOCX file to PDF while preserving the Word document, route to docx / the
DOCX renderer first (e.g. scripts/docx_to_pdf.py or LibreOffice/soffice export). DOCX already
has native page geometry, styles, sections, headers/footers, fields, numbering, and table layout;
converting DOCX → Markdown/HTML → PDF just to make a PDF is a fidelity bug. Verify the native PDF
with pdfinfo, pdftotext, and visual spot checks. Use HTML→PDF only for explicit
redesign/recomposition, when the native render is visibly unacceptable, or when the requested
deliverable is a newly authored web/print design. If HTML is used, say it is a recomposition
route, not the default DOCX→PDF conversion path.
10. Every PDF output needs clickable TOC/index navigation, regardless of route. This is a
global delivery contract for CREATE, REFORMAT, LATEX_THESIS, FILL/overlay, MUTATE/merge/split,
DOCX-native export handoff, and any read→write chain. Any multi-page PDF produced, transformed,
merged, or substantially reformatted by this skill must include a visible TOC / index that maps
major sections to their destination pages and is clickable in the final PDF. For HTML→PDF,
implement TOC rows as internal anchors (<a href="#section-id">) and give every target section a
stable, unique id. For LaTeX, hyperref is mandatory and \tableofcontents plus any manual
\addcontentsline targets must resolve to live links. For markdown/text reformatting, generate or
preserve a TOC before rendering; do not ship a flat prose PDF without navigable section links. For
filled forms, official one-page forms may omit TOC, but multi-page filled packets must preserve
existing bookmarks/links or add an index/outline without altering the form semantics. For
merged/split/watermarked/pypdf/reportlab-built outputs, preserve existing links where possible and
add/update PDF outline/bookmarks plus /Link annotations when the visible TOC cannot be generated
by the renderer alone. Exceptions are only single-page forms/posters/certificates or
source-faithful official forms where adding pages/visual TOC would invalidate the document; the
delivery note must explicitly say why and what navigation was preserved instead. Verification must
include checking link or outline annotations and spot-clicking several TOC/index entries to
confirm they land on the intended pages.
Routes — pick by user intent, then read the guide
WRITE a PDF
Mechanical contract for any HTML→PDF authoring (page geometry, page-break rules, Chart.js settle,
CJK cascade, color fidelity) lives in docs/html-pdf-spec.md. Read it
before writing any new HTML.
READ a PDF
| Intent | Guide | Entry |
|---|
| Default — text + tables from any text-native PDF | docs/read-guide.md §3.1 | pdfplumber (5–10 line inline recipe) |
| Vision escalation — scanned / image-only PDFs, chart values, broken reading order | docs/vision-guide.md | python3 -m scripts.read_pdf_vision --input report.pdf --pages 1-30 |
| Coordinate-aware extraction, page count, decryption, rasterise pages | docs/read-guide.md §3–§4 | inline cookbook recipes |
Default to pdfplumber. Only escalate to vision when the text path is insufficient ((cid:NNN)
glyphs, empty strings, charts that matter, magazine-style layout). When vision IS needed, check
the running model first: a vision-capable model (e.g. M3) should rasterise the page(s) to PNG and
read them with the Read tool directly — no daemon, no MCP, no upstream LLM call.
read_pdf_vision.py is the fallback for text-only models (e.g. M2.7) that cannot accept image
input. See docs/read-guide.md §3.3 and
docs/vision-guide.md.
Combined chains — read then write
When the source is an existing PDF, run the read route first, then feed the extracted text/markdown
into REFORMAT or CREATE. Common patterns:
| User intent | Read step | Write step |
|---|
| Restyle an existing PDF | pdfplumber recipe (docs/read-guide.md §3.1) for text-native, or read_pdf_vision.py for layout-heavy | REFORMAT — pass extracted markdown as --input |
| Fill an unfamiliar form | read_pdf_vision.py to describe field layout/labels | bash scripts/make.sh fill probe, then docs/forms-guide.md |
| Author a PDF inspired by a reference | read_pdf_vision.py on the reference (palette, cover, sections) | CREATE — encode cues in HTML (CSS variables + cover archetype) |
| Translate a PDF/EML preserving layout | EML: parse text/html + cid assets; PDF: pdfplumber per page (or vision if scanned / broken layout) | REFORMAT + templates/translate-preserve-layout/; for rich EML load docs/email-translation-goldman-two-sessions-case.md |
| Verify a generated PDF (sanity loop) | pdfplumber on the freshly written file | n/a — write → read |
Out of scope
- OCR a single image with no PDF involved → call any vision tool directly.
- Desktop-grade PDF editing (paragraph reflow, font substitution) → not an automation job.
.docx or .pptx → use docx / pptx.
- Authoring long-form thesis content from scratch → the skill provides the compilation toolchain
and format scaffold only; do not generate full thesis body text without user-provided source
material.
Reference index
| File | Purpose |
|---|
docs/pitfalls-index.md | Read first when the task matches a known case. 10 evaluation cases keyed by task signature → past failure → recommended trace |
docs/create-guide.md | CREATE route: 4-step flow, component primitive cookbook, cover archetype re-skinning, verification checklist |
docs/latex-academic-thesis-guide.md | LATEX_THESIS route: trigger signatures, input checklist, tectonic/xelatex compile chain, qpdf merge, verification gates, common pitfalls |
docs/latex-technical-book-guide.md | LATEX_TECHNICAL_BOOK route: Chinese technical book / engineering monograph scaffold, HTML style mapping, B5 book compile and verification |
docs/markdown-static-academic-data-viz-case.md | P3 progressive case: Markdown report → A4 print-academic PDF, preserve source hierarchy, generate Matplotlib static charts for source tables, verify images/text/no Markdown remnants |
docs/email-translation-goldman-two-sessions-case.md | P5 progressive case: rich EML research report → Chinese A4 PDF, optimized translation prompt, cid image preservation, link/image verification evidence |
docs/ai-voice-cloning-regulatory-report-case.md | P4 progressive case: AI voice cloning multi-jurisdiction regulatory survey → Chinese A4 PDF, bilingual research prompts, fact-check report, optimized print-academic HTML source |
docs/reformat-guide.md | REFORMAT route: input formats, title-lift, accent re-skinning, "when NOT to REFORMAT" |
docs/forms-guide.md | FILL route: probe → AcroForm or visual overlay, JSON schemas, geometry lint |
docs/read-guide.md | READ route: pdfplumber default, library + CLI cookbook, troubleshooting |
docs/vision-guide.md | read_pdf_vision.py reference — flags, JSON schema, internal chunking, time budget, error matrix |
docs/html-pdf-spec.md | HTML→PDF mechanical contract — page geometry, page-break, Chart.js settle, CJK, color fidelity, quality gate |
../xlsx/docs/superstore-multiformat-conversion-case.md | Cross-skill X8/P9 case: 10k-row Superstore Excel → CSV/JSON/HTML→PDF + XML-template CSV→XLSX |
docs/design-guide.md | Aesthetic layer — palette mood table, typography pairs, cover archetypes, anti-patterns |
docs/advanced-reference.md | Mutation / annotation / signature / text replacement cookbook (qpdf, pypdf, reportlab) |
docs/troubleshooting.md | Environment / CJK / merge / verification / stale-script / read-side issues |
templates/INDEX.md | Templates index — eight skeletons + LaTeX thesis scaffold |
templates/latex-academic-thesis/source.tex | Compact LaTeX thesis scaffold — cover, abstract, TOC, body, refs, appendix, headers/footers, page-number breaks |
templates/latex-technical-book/source.tex | Technical book scaffold — O'Reilly-like cover, red-number TOC, code highlighting, callouts, diagrams, B5 geometry |
templates/latex-academic-thesis/README.md | Template fill-in guide — structural invariants, compile commands, common patterns |
Environment
First run on a new machine? Always start with bash scripts/make.sh check. If anything prints
WARN, run bash scripts/make.sh fix immediately and re-run check until the output is all
green. Generation routes assume a green environment — proceeding with WARN will fail
mid-pipeline.
bash scripts/make.sh check
bash scripts/make.sh fix
check enforces minimum versions (Python ≥ 3.9, Node ≥ 18, pypdf ≥ 3.0, markdown-it-py ≥ 3.0) and
prints the actual installed version for each. fix propagates real pip failures (no longer
silently green) — read the last 20 lines of pip output it dumps on failure rather than re-running
blindly.
| Tool | Used by | Install |
|---|
| Python 3.9+ | all .py scripts | system |
markdown-it-py | REFORMAT (reformat_parse.py) | pip install markdown-it-py |
pypdf | FILL, MUTATE | pip install pypdf |
pdfplumber | READ (default) | pip install pdfplumber |
pdf2image, pillow, pypdfium2 | READ vision + page rasterise | pip install pdf2image pillow pypdfium2 |
| Node.js 18+ | render_html.cjs | system |
playwright + Chromium | render_html.cjs | npm install -g playwright && npx playwright install chromium |
pdfinfo, pdftotext, pdfimages (poppler) | READ + verification | brew install poppler |
qpdf | MUTATE / decryption | brew install qpdf (optional) |
read_pdf_vision.py additionally requires the local mavis daemon to be running with the matrix MCP
authenticated (set MAVIS_ACCESS_TOKEN and MATRIX_BASE_URL in ~/.zshrc, then restart daemon).
Details: docs/vision-guide.md.
Windows (win32) platform notes
All scripts in this skill are bash-based and run via Git Bash on Windows. The agent's shell is
PowerShell, so invoke scripts with the bash prefix:
bash scripts/make.sh check
bash scripts/make.sh render --in page.html --out out.pdf --format A4 --wait 15000
bash scripts/make.sh reformat --input doc.md --out report.pdf
bash scripts/make.sh fill probe form.pdf
For piped verification commands, wrap in bash -c:
bash -c "pdfinfo out.pdf | grep 'Page size'"
bash -c "pdfimages -list out.pdf | tail -n +3 | wc -l"
bash -c "pdftotext -layout out.pdf - | head -40"
Tool mapping:
| Unix reference | Windows equivalent |
|---|
python3 | python (inside bash: the script's PY var handles this) |
/tmp/pdf-* | Git Bash auto-maps /tmp/ — no change needed |
brew install poppler | winget install or scoop install poppler |
~/.zshrc | $PROFILE (PowerShell profile) or set env vars via System Settings |
If Git Bash or any tool is missing, read the mavis skill's
references/windows-tool-bootstrap.md for detection + auto-install commands.