Generates branded PDF documents. Triggered when the user asks to "create a PDF", "generate a PDF report", "export as PDF", "make a PDF", "build a PDF document", "PDF summary", "PDF memo", or references PDF output for reports, memos, proposals, or summaries.
Generate branded PDF documents using Python libraries. Supports creation from scratch, form filling, merge/split, watermarks, and conversion to images.
Before generating output, check that {WORKING_DIR}/.reporting-resolved/brand-config.json exists. If it does not, tell the user: "The branding plugin is required but has not run. Please install the branding plugin and run /reporting-plugins:brand first." Do not produce unbranded output.
If a JSON data file was generated earlier in this session (in output/text/), read it as the canonical data source to ensure cross-format parity. If no prior JSON exists, use data from the conversation context directly.
Read .reporting-resolved/brand-config.json for all brand values. Read the logo from .reporting-resolved/logo.png (if it exists — it may have been suppressed).
If not already installed, run:
pip install reportlab pypdf pdfplumber pypdfium2
Or use the plugin's requirements.txt:
pip install -r {PLUGIN_ROOT}/requirements.txt
output/pdf/{slug}-{YYYY-MM-DD}-{HHmm}-{xxx}.pdf| Task | Library | Notes |
|---|---|---|
| Create new PDFs | reportlab | Programmatic generation with full layout control |
| Merge, split, rotate pages | pypdf | Page-level operations on existing PDFs |
| Extract text and tables | pdfplumber | Layout-preserving extraction |
| Render PDF to images | pypdfium2 | Fast page-to-PNG conversion for QA |
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate
doc = SimpleDocTemplate(
output_path,
pagesize=letter,
topMargin=brand["layout"]["margin_top"],
bottomMargin=brand["layout"]["margin_bottom"],
leftMargin=brand["layout"]["margin_left"],
rightMargin=brand["layout"]["margin_right"],
)
Every page must include:
Header:
components.logo_max_heightfont_size_caption, semantic.muted_colorcomponents.header_heightFooter:
content.confidentiality_notice left-aligned, font_size_caption, semantic.muted_colorcomponents.footer_heightfont_size_h1, semantic.heading_color, font_family_heading, boldfont_size_h2, semantic.heading_color, font_family_headingfont_size_body, colors.text, font_familycomponents.table_header_bg background, components.table_header_text text; alternating rows with components.table_alt_rownumber_formats.*<sub> and <super> in reportlab Paragraph markup. NEVER use Unicode subscript/superscript characters — they render incorrectly in reportlab.font_family from brand config. If the font is not registered with reportlab, fall back to Helvetica and log a warning.logo_max_height for the logo.{name}.validation.json sidecar with per-check results.For detailed form-filling workflows (fillable fields vs. annotation-based), see forms.md.
For merge/split/rotate (pypdf), text extraction (pdfplumber), and image conversion (pypdfium2), see reference.md.
If firm.name was overridden in the brand config but firm.website was NOT explicitly set by the user, do not render firm.website anywhere in the PDF. The default Acme URL must never appear alongside a non-Acme firm name.