Skip to main content

PDF Documents (profy-pdf)

PDF work splits into two unrelated paths. Establish which one you are on first — everything else follows from that:

Activation

profy-pdf is user_selectable: falseautomatically available, nothing to tick.
contracts declares only skills: ["skills/"] and no tools. This plugin ships two skills: builtin/pdf for general processing and builtin/kami for typesetting, covered in its own section below.

Creation path: WeasyPrint is the only option

Never use reportlab, fpdf2, or any coordinate-based drawing library for PDF creation. Those libraries require manually positioning every element, which produces poor output and is effectively unmaintainable — change one sentence and every subsequent position has to be recomputed by hand.
WeasyPrint’s approach is to write HTML + CSS and render it, so pagination and layout are handled by a CSS layout engine. You get declarative typesetting rather than manual placement.

Headers, footers, and pagination

@page rules are a print-specific CSS capability — unusable in a browser, essential here:
counter(page) and counter(pages) are filled in by the renderer after pagination — precisely the thing a coordinate-based approach cannot give you, since it does not know the page count until it has already placed everything.

Design guidance

  • Use CSS Grid / Flexbox for complex layouts such as cards and multi-column blocks
  • Drive page size, margins, headers, and footers entirely from @page
  • Control pagination with page-break-before / page-break-after
  • For CJK text use font-family: 'Noto Sans CJK SC', sans-serif (preinstalled in the sandbox)
  • For branded reports, set accent colours and use CSS background shapes

When not to use WeasyPrint

Manipulation path: command-line tools

pdftotext (poppler-utils)

-layout matters more than it looks: without it, multi-column layouts come out as interleaved nonsense, because the extractor follows the text stream rather than the visual columns.

qpdf

pdftk (if available)

Form handling

The skill ships a set of form scripts: extract_form_field_info.py, extract_form_structure.py, check_fillable_fields.py, fill_fillable_fields.py, fill_pdf_form_with_annotations.py, plus verification helpers check_bounding_boxes.py, create_validation_image.py, and convert_pdf_to_images.py. The workflow is probe before filling. Form field naming varies arbitrarily between PDFs, so guessing field names is guaranteed to fail — and to fail silently, since writing to a non-existent field is not an error. Extract the real structure first.

OCR for scanned documents

Scanned PDFs have no text layer, so pdftotext returns nothing. These require pytesseract plus pdf2image to OCR into searchable text.

kami · 紙: the typesetting engine

builtin/kami is the second skill inside the pdf plugin, dedicated to deliverables that need to look designed: warm parchment canvas, ink-blue accent, serif-led hierarchy, tight editorial rhythm.

Nine document types

Selection runs through a decision tree rather than a question. Ask only when two rows genuinely both fit:
Landing Page produces no PDF. It is a screen-first interactive template with a gallery carousel, hero entrance animation, responsive breakpoints at 880px and 480px, and prefers-reduced-motion support. The deliverable is a ready-to-host .html file.
Slides default to slides-weasy.html (WeasyPrint HTML → PDF); the PPTX-producing variant is used only when the user explicitly needs an editable PowerPoint file.

Fonts

The Chinese fonts are commercial. Before building Chinese documents, run the font recovery script once:
It tries several CDN sources with retry and size validation, and suggests Source Han Serif SC as a fallback if all of them fail.
Japanese currently rides the CJK template path with no dedicated -ja templates. Before shipping, visually verify line breaks, punctuation rhythm, and emphasis weight — those three are exactly where Chinese and Japanese typesetting conventions diverge.

Handling vague feedback

When a user says “looks off”, “too cramped”, or “not elegant”, the skill forbids guessing and requires asking back with the current values. The value of this rule is that it converts a subjective argument into a parameter adjustment: “line-height is currently 1.6, should it go to 1.8?” resolves far faster than “let me try again”.

Boundaries and failure modes

  • Creation has exactly one supported route, WeasyPrint; coordinate-drawing libraries are explicitly prohibited.
  • Scanned documents require OCR, or text extraction returns empty.
  • Form fields must be probed first; guessing names fails silently.
  • kami’s Chinese fonts are commercial and are not bundled with distributions — they are fetched by ensure-fonts.sh.
  • Landing Page has no PDF output. Do not expect to export one.

Troubleshooting

Verify your output

  1. Convert to images and review every page: convert_pdf_to_images.py. Layout accidents are invisible in the source.
  2. For Chinese documents, search for box glyphs — the classic signature of a font that never loaded.
  3. On multi-page documents, jump to the last page and confirm that of N shows a real page count rather than literal text.

Office documents overview

How the four document capabilities differ and when to use each

Presentations

When the deliverable is a deck rather than a document
Verified 2026-08-11. Sources: services/agent-runtime/src/plugins/builtin/pdf/plugin.json, skills/pdf/SKILL.md, skills/kami/SKILL.md, skills/pdf/scripts/, skills/kami/scripts/ensure-fonts.sh.