pdf-builder
Generate branded PSD PDF documents with letterhead, clean fonts, and Documenso-ready field coordinates. Use when: creating forms, permission slips, agreements, contracts, waivers, board resolutions, or any document needing PSD branding and/or digital signing. Triggers on: create pdf, build pdf, branded pdf, generate form, pdf builder, letterhead document, signable document.
What this skill does
# PDF Builder — PSD Branded Document Generator
Generate professional PDFs with Peninsula School District letterhead, Inter/Josefin Sans fonts, and Documenso-ready field manifests for digital signing workflows.
## Quick Start
### Using a Built-in Template
```bash
uv run generate_pdf.py --template <template-name> --data '<json>' --output ~/Downloads/doc.pdf
```
### Using a Custom JSON Spec
```bash
uv run generate_pdf.py --json '<spec>' --output ~/Downloads/doc.pdf
# or
uv run generate_pdf.py --spec spec.json --output ~/Downloads/doc.pdf
```
> **Note**: Output paths must be within the skill's `paths:` scope (`scripts/`, `references/`, `~/Downloads/`, `~/Desktop/`).
### Output
Every invocation produces:
1. **PDF file** at the `--output` path
2. **Field manifest** at `{output}.fields.json` — Documenso-ready percentage coordinates
3. **Stdout JSON**: `{"pdf": "...", "manifest": "...", "pages": N, "fields": N}`
## Scripts
All scripts live in `plugins/psd-productivity/skills/pdf-builder/scripts/`.
| Script | Purpose |
|--------|---------|
| `generate_pdf.py` | Core PDF generator — takes JSON spec or template name |
| `letterhead.py` | Letterhead module — logo, colors, footer, continuation pages |
| `install_fonts.py` | One-time font setup — downloads Inter + Josefin Sans |
**Runtime**: All scripts use PEP 723 inline dependencies. Run with `uv run`.
### First-Time Setup
Before first use, install fonts:
```bash
cd plugins/psd-productivity/skills/pdf-builder/scripts
uv run install_fonts.py
```
Fonts are stored in `scripts/fonts/` (checked into repo). This only needs to run once.
## Built-in Templates
| Template | Description | Signers |
|----------|-------------|---------|
| `permission-slip` | Student activity permission | 1 (parent) |
| `employment-agreement` | HR hiring document | 2 (employee + HR) |
| `contractor-agreement` | Vendor/contractor MOU | 2 (contractor + district) |
| `policy-acknowledgment` | Staff policy receipt | 1 (employee) |
| `field-trip-waiver` | Liability waiver + emergency info | 1 (parent) |
| `board-resolution` | Board action item | 2 (chair + secretary) |
| `leave-request` | Staff leave form | 3 (employee + supervisor + HR) |
| `generic-form` | Blank letterhead + title | Configurable |
### Template Data Variables
Pass data to templates via `--data` JSON. Each template accepts different variables.
**permission-slip**: `student_name`, `event`, `date`, `school`, `grade`, `title`, `body`
**employment-agreement**: `employee_name`, `position`, `department`, `location`, `terms`
**contractor-agreement**: `terms`
**policy-acknowledgment**: `title`, `body`
**board-resolution**: `resolution_number`, `subject`, `body`, `resolution_text`
**field-trip-waiver**: `student_name`, `destination`, `title`, `body`
**leave-request**: *(no data variables — all fields are fillable)*
**generic-form**: `title`, `body`
## Custom Document Spec
For documents that don't fit a template, build a JSON spec with sections:
```json
{
"title": "Document Title",
"department": "Technology Department",
"data": {
"name": "Jane Doe"
},
"sections": [
{"type": "heading", "text": "Section Title", "level": 1},
{"type": "paragraph", "text": "Hello {{name}}, this is body text."},
{"type": "field_row", "fields": [
{"label": "Full Name", "type": "TEXT", "width": 0.5},
{"label": "Date", "type": "DATE", "width": 0.5}
]},
{"type": "checkbox_group", "label": "Options", "items": [
{"label": "Option A"},
{"label": "Option B", "checked": true}
]},
{"type": "table", "headers": ["Col1", "Col2"], "rows": [["A", "B"]]},
{"type": "divider"},
{"type": "spacer", "height": 20},
{"type": "signature_block", "signers": [
{"role": "Employee", "fields": ["SIGNATURE", "DATE"]},
{"role": "Supervisor", "fields": ["SIGNATURE", "DATE", "NAME"]}
]}
]
}
```
### Section Types
| Type | Description | Key Properties |
|------|-------------|----------------|
| `heading` | Josefin Sans Bold title | `text`, `level` (1=18pt, 2=14pt, 3=11pt) |
| `paragraph` | Inter Regular body text (auto-wrapped) | `text`, `fontSize`, `lineHeight` |
| `field_row` | Horizontal row of labeled input boxes | `fields[]` with `label`, `type`, `width` (0-1 fraction), `value`, `height` (points, default 22). Section-level options: `showLabels` (bool, default `true`), `gap` (int, default ~9pt — horizontal gap between cells), `rowGap` (int, default ~9pt — vertical gap after the row) |
| `checkbox_group` | Vertical checkbox list | `label`, `items[]` with `label`, `checked`, `required` |
| `table` | Data table with alternating rows | `headers[]`, `rows[][]` |
| `signature_block` | Signing zone with role labels | `signers[]` with `role`, `fields[]`, `anchor` ("flow" or "bottom") |
| `spacer` | Vertical whitespace | `height` (points) |
| `divider` | Horizontal line | `color`, `weight` |
**CRITICAL — Duplicate field labels**: Field labels are slugified to create manifest field names. Two fields with the same label (e.g., both "Position") produce the same slug, causing the manifest `positions` object to only keep the last one. Use unique labels (e.g., "Team Member Position", "Evaluator Position") or deduplicate manually in downstream code.
**Spacer for page breaks**: A spacer of ~80pt after a signature block reliably pushes the next section to a new page. Use this to separate reference appendices from the evaluation content.
### Building a tight table of form fields
Use `showLabels: false`, `gap: 0`, `rowGap: 0` on stacked `field_row` sections to render a proper table of fillable cells (one header row with labels, many flush data rows below). Each cell still needs a unique label for slug uniqueness — compact names like `S2 First` / `S2 Last` work well.
```json
{ "type": "field_row", "gap": 0, "rowGap": 0, "fields": [
{ "label": "First Name", "type": "TEXT", "width": 0.18, "height": 14 },
{ "label": "Last Name", "type": "TEXT", "width": 0.18, "height": 14 },
{ "label": "Student ID", "type": "TEXT", "width": 0.13, "height": 14 },
{ "label": "DOB", "type": "TEXT", "width": 0.15, "height": 14 },
{ "label": "Grade", "type": "TEXT", "width": 0.10, "height": 14 },
{ "label": "School", "type": "TEXT", "width": 0.26, "height": 14 }
]},
{ "type": "field_row", "showLabels": false, "gap": 0, "rowGap": 0, "fields": [
{ "label": "S2 First", "type": "TEXT", "width": 0.18, "height": 14 },
{ "label": "S2 Last", "type": "TEXT", "width": 0.18, "height": 14 },
{ "label": "S2 ID", "type": "TEXT", "width": 0.13, "height": 14 },
{ "label": "S2 DOB", "type": "TEXT", "width": 0.15, "height": 14 },
{ "label": "S2 Grade", "type": "TEXT", "width": 0.10, "height": 14 },
{ "label": "S2 School","type": "TEXT", "width": 0.26, "height": 14 }
]},
...
```
Reference implementation: `workflows/ssd-mv-intake/template-spec.json` in the `psd-workflow-automation` repo (6-student McKinney-Vento intake table).
### Page 1 title rendering
The letterhead module only renders the spec's top-level `title` in the **continuation header** on pages 2+. Page 1 has the logo block and letterhead but no title bar. To show the document title on page 1, add an explicit `heading` section at the top of `sections`:
```json
{ "type": "spacer", "height": 4 },
{ "type": "heading", "text": "Annual McKinney-Vento Intake Form", "level": 1 },
{ "type": "spacer", "height": 6 },
```
### Field Types for `field_row` and `signature_block`
| Type | Documenso Mapping | Use |
|------|------------------|-----|
| `TEXT` | TEXT/text | General text input |
| `DATE` | DATE/date | Date fields |
| `NAME` | NAME/name | Full name |
| `EMAIL` | EMAIL/email | Email address |
| `NUMBER` | TEXT/number | Numeric input |
| `SIGNATURE` | SIGNATURE/signature | Signature line |
| `INITIALS` | INITIALS/initials | Initials box |
| `CHECKBOX` | CHECKBOX/checkbox | Checkboxes |
| `DROPDOWN` | DROPDOWN/dropdown | Dropdown (provide `options`) |
Related in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.