PDFGenerator

PDFGenerator Template & Usage Guide

Design polished PowerPoint and Word reports, populate them from JSON, and preview the result as a PDF. This handbook covers the full workflow: starting the services, structuring data, writing tags, building native charts, replacing pictures, using the web interface, calling the API, and diagnosing template problems.

Scope: This guide documents the features implemented in this repository. The tag language is inspired by Carbone, but PDFGenerator is not a complete reimplementation of every Carbone feature. Check Compatibility and current limits before designing an advanced template.

Start here Best for
Five-minute first report Creating a working report quickly
Tag language Looking up substitutions and formatters
Native charts Populating PowerPoint or Word charts
Dynamic images Replacing picture placeholders
Report design recipes Planning a professional layout
Troubleshooting Resolving warnings or failed output

Getting started

Service map

PDFGenerator uses three local services. Each has one job and one stable default port.

Service Port Start command Open in a browser
FastAPI backend 9010 python main.py http://localhost:9010/docs
Next.js application 9011 npm run start from frontend http://localhost:9011
This guide 9012 python guide.py http://localhost:9012

The backend populates the Office template and runs the selected PDF converter. The frontend sends the uploaded template and JSON to the backend, then displays the PDF inline. The guide service renders this Markdown file as the page you are reading.

Prerequisites

Install and start

From the repository root, install and start the backend:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python main.py

Open another terminal for the application:

cd frontend
npm install
npm run start

Open a third terminal for this documentation site:

python guide.py

Then open http://localhost:9011 to generate documents and http://localhost:9012 to read the guide.

First-run note: The backend becomes reachable while the managed LibreOffice runtime is still preparing. Watch the renderer status in the application or request GET http://localhost:9010/api/converter/health before the first generation.

Download the supplied example

The documentation service exposes both committed sample files:

These are the best references for split text tags, native chart workbooks, empty-value handling, and realistic one-page report design.

Five-minute first report

This example creates a minimal customer summary. It works in either PowerPoint or Word.

1. Prepare the JSON

{
  "ReportTitle": "Customer Summary",
  "AsAtDate": "2026-06-30",
  "Customer": {
    "Name": "Ada Lovelace",
    "AccountNumber": "INV-1042"
  },
  "PortfolioValue": 1250000.5
}

2. Design the Office template

Create the complete visual layout in PowerPoint or Word. Add these tags wherever the values should appear:

{d.ReportTitle}
Prepared for {d.Customer.Name}
Account {d.Customer.AccountNumber}
As at {d.AsAtDate:formatD('DD MMMM YYYY')}
Value R{d.PortfolioValue:formatN(2, '.', ' ')}

The generated text is:

Customer Summary
Prepared for Ada Lovelace
Account INV-1042
As at 30 June 2026
Value R1 250 000.50

3. Generate the PDF

  1. Open http://localhost:9011.
  2. Choose the .pptx or .docx template.
  3. Paste the JSON into the editor.
  4. Keep Auto preview enabled or click Generate PDF.
  5. Review the PDF in the right-hand preview panel.
  6. Read any template warnings shown below the editor.

The web app sends the template and JSON together. The backend creates a populated copy, converts that copy to PDF, and returns the PDF inline; it does not modify the template on disk.

How generation works

JSON object + PPTX/DOCX template
              │
              ▼
       OOXML template engine
       ├─ replaces text tags
       ├─ expands supported loops
       ├─ updates embedded chart workbooks
       └─ replaces tagged pictures
              │
              ▼
     LibreOffice or Gotenberg
              │
              ▼
        PDF shown in browser

PowerPoint and Word files are ZIP-based Office Open XML packages. PDFGenerator changes the relevant XML, embedded workbook, and media parts while retaining the template's native masters, styles, charts, and page geometry. LibreOffice is responsible for the final Office-to-PDF layout pass.

Supported input and output

Item Current behavior
Template input .pptx and .docx
JSON input One JSON object at the root
Browser output PDF displayed inline
Debug output Populated .pptx or .docx from /api/render-template
Default converter Managed local LibreOffice
Optional converter Authenticated Gotenberg selected in config.py
Template size Up to 50 MB by default
Image size Up to 15 MB decoded/downloaded by default

Design a reliable JSON payload

The template should be a view of the data, not the place where business logic lives. Prepare totals, labels, and domain-specific decisions before sending the JSON whenever possible.

Group related values, keep collection names plural, and use ISO dates plus native JSON numbers for calculations and charts.

{
  "Report": {
    "Title": "Quarterly Portfolio Report",
    "EffectiveDate": "2026-06-30",
    "Reference": "QPR-2026-02"
  },
  "Client": {
    "Name": "Example Holdings",
    "Adviser": "Jordan Smith"
  },
  "Summary": {
    "MarketValue": 1250000.5,
    "Return": 0.0875,
    "RiskBand": "Moderate"
  },
  "Holdings": [
    { "Name": "Domestic Equity", "Weight": 0.42, "Value": 525000.21 },
    { "Name": "Global Equity", "Weight": 0.31, "Value": 387500.16 },
    { "Name": "Fixed Income", "Weight": 0.27, "Value": 337500.13 }
  ]
}

Data-design rules

  1. Use an object at the root. An array, string, or number at the root is rejected by the API.
  2. Match names exactly. EffectiveDate and effectiveDate are different keys.
  3. Send chart values as numbers. Use 0.0875, not the string "8.75%", when a native chart needs the value.
  4. Use ISO dates. 2026-06-30 or a full ISO timestamp is predictable for formatD.
  5. Keep display-only text when it is intentional. A prepared value such as "R396 264 998" is useful when no further calculation is needed.
  6. Prefer one array of objects for a chart or table. Categories and series values should live on the same item so one structural loop controls the range.
  7. Represent absent values consistently. Use null or "", then apply ifEmpty(...) in the template where a fallback is required.

Numbers: raw versus preformatted

JSON Template Best use
1250000.5 {d.Value:formatN(2, '.', ' ')} Calculations and text
0.0875 {d.Return:mult(100):toFixed(2):append('%')} Percentages and charts
"R1 250 000.50" {d.DisplayValue} Fixed display text only

Do not put currency symbols or thousands separators into values used by a native chart. Office needs a numeric cell to plot the point correctly.

Tag language

Tags are written between braces. The d prefix means “read from the submitted data object.” Tags can be in a text box, paragraph, table cell, header, footer, notes part, or a supported embedded chart cell.

Basic substitutions

Data Tag Result
{"name":"Ada"} {d.name} Ada
{"client":{"name":"Ada"}} {d.client.name} Ada
{"active":true} {d.active} true
{"count":12} {d.count} 12

PowerPoint and Word sometimes split a visible tag into several internal text runs. The engine joins those runs for matching and places the replacement into the run containing the opening brace, so the replacement keeps that run's formatting.

Fixed array indexes

Use a numeric index when you want one known item rather than a repeating structure. Indexes are zero-based.

{
  "Contacts": [
    { "Name": "Ada" },
    { "Name": "Grace" }
  ]
}
Primary: {d.Contacts[0].Name}
Secondary: {d.Contacts[i=1].Name}

Both forms resolve without creating a loop. Structural [i] and [i+1] markers are covered in Repeating data.

Chaining formatters

Add a formatter after a colon. Each formatter receives the previous formatter's output.

{d.Client.Name:lowerCase:ucWords}
{d.Return:mult(100):round(2):append('%')}
{d.EffectiveDate:formatD('DD MMMM YYYY')}

Arguments can be constants or another d.path:

{d.Name:prepend('Client: ')}
{d.Amount:add(d.Adjustment):formatN(2, '.', ' ')}

Empty-value formatters

Formatter Example Meaning
ifEmpty {d.MiddleName:ifEmpty('-')} Use - when missing, null, empty text, an empty array, or an empty object
ifEM {d.Value:ifEM(0)} Short alias for ifEmpty

Text formatters

Formatter Example Example result
lowerCase {d.Name:lowerCase} ada lovelace
upperCase {d.Code:upperCase} BALTRF
ucFirst {d.Name:lowerCase:ucFirst} Ada lovelace
ucWords {d.Name:lowerCase:ucWords} Ada Lovelace
prepend {d.Code:prepend('Fund: ')} Fund: BALTRF
append {d.Value:append(' units')} 42 units
replace {d.Code:replace('-', ' ')} Replaces every matching substring
substr {d.Reference:substr(0, 8)} First eight characters
ellipsis {d.Description:ellipsis(80)} Shortens long text and adds ...
len {d.Description:len} Character count, or collection length
print {d.Name:print('Confidential')} Prints the supplied constant

Number formatters

Formatter Example Notes
add {d.Value:add(10)} Addition
sub {d.Value:sub(10)} Subtraction
mult {d.Return:mult(100)} Multiplication; this implementation uses mult
div {d.Value:div(4)} Division; division by zero produces a warning
round {d.Value:round(2)} Returns a rounded number
toFixed {d.Value:toFixed(2)} Returns text with exactly two decimals
formatN {d.Value:formatN(2, '.', ' ')} Precision, decimal separator, thousands separator

Examples for 1234567.8:

{d.Value:formatN(2, '.', ',')}  -> 1,234,567.80
{d.Value:formatN(2, ',', ' ')}  -> 1 234 567,80
{d.Value:round(0)}              -> 1234568

Date formatter

formatD accepts an ISO date or timestamp. Supported tokens are YYYY, YY, MMMM, MMM, MM, and DD. The common LL pattern is also available.

{d.Date:formatD('DD MMMM YYYY')} -> 30 June 2026
{d.Date:formatD('YYYY-MM-DD')}   -> 2026-06-30
{d.Date:formatD('LL')}           -> June 30, 2026

Month names currently use the Python process locale; there is no per-request language option in the public API.

Array formatter

arrayJoin(separator, start, count) joins an array of strings or numbers.

{ "Tags": ["income", "balanced", "global", "retirement"] }
{d.Tags:arrayJoin(' · ')}       -> income · balanced · global · retirement
{d.Tags:arrayJoin(', ', 1, 2)}  -> balanced, global

Missing paths and warnings

If {d.Report.Date} is in the template but the JSON contains ReportDate, the engine does not guess. The output value becomes empty and a warning identifies the missing path. This strict behavior prevents a spelling error from silently binding to the wrong field.

Warnings are visible in the web interface and returned as a JSON array in the X-PDFGenerator-Warnings response header.

PowerPoint template design

PowerPoint is ideal for fixed-page reports, factsheets, certificates, and presentation- style PDFs. Design the entire visual system in PowerPoint; PDFGenerator changes content, not the creative direction.

Layer Recommendation
Slide size Choose the final PDF proportion before designing, such as A4 portrait or 16:9
Master/layout Put logos, page furniture, recurring footer text, and backgrounds on the master or layout
Grid Use consistent outer margins and align text boxes, tables, pictures, and charts to common guides
Typography Use Gotham or Montserrat from assets/fonts; keep a small hierarchy of title, section, body, and note styles
Color Use one dark neutral, one accent, and a restrained chart palette with accessible contrast
Long text Give descriptions enough height; PowerPoint text boxes do not automatically move content below them
Charts Use native Office charts and style them in PowerPoint before adding data tags

Text tags in PowerPoint

Example title block:

{d.TitleName}
{d.Report.ReportName}
As at {d.FormattedEffectiveDate}

PowerPoint-specific limits

Word template design

Word is ideal for flowing reports, letters, statements, and documents with tables that may grow vertically.

  1. Use Word styles for headings, body text, captions, and tables.
  2. Put headers, footers, and page numbers in their native Word areas.
  3. Use a borderless table when precise alignment is more important than free-floating text boxes.
  4. Set paragraph spacing deliberately; avoid repeated blank paragraphs for layout.
  5. Keep a table header outside the two loop-marker rows.
  6. Allow table rows to break or stay together according to the expected content.
  7. Test with enough rows to force a page break.

Text substitutions work in the document body, headers, footers, footnotes, endnotes, and comments. Supported Word loops duplicate table rows vertically.

Repeating data

[i] means “the current item.” [i+1] marks the next template row or column and tells the engine which structure to expand. The first row or column is the visual template; its formatting is copied for every data item.

Vertical Word table loop

Given this data:

{
  "Items": [
    { "Description": "Consulting", "Quantity": 2, "Amount": 1500 },
    { "Description": "Implementation", "Quantity": 1, "Amount": 4200 },
    { "Description": "Support", "Quantity": 3, "Amount": 900 }
  ]
}

Create a Word table with a header and two template rows:

Description Quantity Amount
{d.Items[i].Description} {d.Items[i].Quantity} {d.Items[i].Amount:formatN(2)}
{d.Items[i+1].Description} {d.Items[i+1].Quantity} {d.Items[i+1].Amount:formatN(2)}

The two template rows become three output rows. Only vertical Word table loops are currently supported; horizontal and nested Word loops are not.

Loop rules

Native charts

PDFGenerator retains native PowerPoint and Word charts. Data tags live in the chart's embedded Excel workbook. At generation time the engine expands the workbook, changes the connected chart formula ranges, and rebuilds the chart's cached categories and values before LibreOffice renders the PDF.

Create a native chart

  1. In PowerPoint or Word, choose Insert → Chart and select the required chart type.
  2. Style the chart completely: type, colors, labels, axes, legend, gap width, fonts, and number formats.
  3. Open Chart Design → Edit Data to edit the embedded Excel workbook.
  4. Replace sample worksheet values with tags using one of the loop patterns below.
  5. Ensure the visible chart range covers both marker rows or columns.
  6. Save and close the embedded workbook, then save the Office template.
  7. Generate with a payload containing at least three items to prove expansion beyond the marker pair.

Vertical chart data

Use adjacent rows when categories run down the worksheet. This is the pattern used by the supplied Tracker template.

{
  "Performance": [
    { "Period": "1 Month", "Fund": 0.008, "Benchmark": 0.006 },
    { "Period": "3 Months", "Fund": -0.021, "Benchmark": -0.0012 },
    { "Period": "1 Year", "Fund": 0.207, "Benchmark": 0.203 },
    { "Period": "3 Years", "Fund": 0.165, "Benchmark": 0.168 }
  ]
}

Embedded worksheet design:

Cell/row A: Period B: Fund C: Benchmark
Row 1 Category Fund Benchmark
Row 2 {d.Performance[i].Period} {d.Performance[i].Fund} {d.Performance[i].Benchmark}
Row 3 {d.Performance[i+1].Period} {d.Performance[i+1].Fund} {d.Performance[i+1].Benchmark}

Rows 2–3 expand to rows 2–5. The category and both series formulas extend to row 5.

Horizontal chart data

Use adjacent columns when categories run across the worksheet.

Embedded worksheet design using the same JSON:

Cell/row A B C
Row 1 Period {d.Performance[i].Period} {d.Performance[i+1].Period}
Row 2 Fund {d.Performance[i].Fund} {d.Performance[i+1].Fund}
Row 3 Benchmark {d.Performance[i].Benchmark} {d.Performance[i+1].Benchmark}

Columns B–C expand to B–E. The engine copies the template column's width, styles, and number formats. Category and value formulas extend through column E, and chart caches are rebuilt from the rendered cells.

Chart design recommendations

Chart checklist

Dynamic images

A dynamic image starts as a normal placeholder picture in PowerPoint or Word. Put the data tag into the picture's alt text/description; the engine replaces only that picture's media while preserving the shape's placement.

Add a picture placeholder

  1. Insert a temporary raster picture into the Office template.
  2. Crop and size its shape to the desired output box.
  3. Open the picture's Alt Text pane.
  4. Put a tag such as {d.ProfilePhoto} in the description or title.
  5. Supply an HTTP(S) URL or Base64 data URI at that JSON path.

Schematic JSON value:

{
  "ProfilePhoto": "data:image/png;base64,<BASE64_DATA>"
}

Or use an accessible remote image:

{
  "ProfilePhoto": "https://images.example.com/reports/client-1042.png"
}

Image fit modes

Alt-text tag Geometry behavior Best for
{d.Photo} Exact fill; keeps the shape box and stretches raster to it Logos, backgrounds, controlled-aspect images
{d.Photo:imageFit(fill)} Explicit exact fill Same as the default
{d.Photo:imageFit(contain)} Preserves aspect ratio inside the same box; may letterbox Portraits, product photos, charts as raster images
{d.Photo:imageFit(fillWidth)} Preserves aspect ratio and width; changes shape height Flowing Word layouts where height may change

PDFGenerator deliberately defaults a plain image tag to fill, so the replacement uses the exact placeholder box. Carbone defaults a plain image to fillWidth; use an explicit formatter when moving templates between the two systems.

Existing crop rectangles are cleared after replacement so a stale template crop does not hide the new image. EXIF orientation is normalized while decoding; :autoOrient is also accepted for compatibility.

Image design recommendations

Report design recipes

These recipes show how to pair a clean Office layout with a maintainable JSON shape.

Investment factsheet — PowerPoint

Use one A4 portrait slide for a concise, high-density report.

┌────────────────────────────────────────────────────┐
│ LOGO    FUND NAME                         DATE     │
├────────────────────────────────────────────────────┤
│ AUM       TARGET       RISK       INCEPTION        │
├───────────────────────┬────────────────────────────┤
│ Investment objective  │ Asset allocation chart     │
│ and description       │ (native donut/bar)         │
├───────────────────────┴────────────────────────────┤
│ Historical return chart (native clustered bars)   │
├────────────────────────────────────────────────────┤
│ TER table                      Disclaimer / footer │
└────────────────────────────────────────────────────┘

Recommended data groups:

{
  "Fund": {
    "Name": "Balanced Tracker Fund",
    "Code": "BALTRF",
    "EffectiveDate": "2026-06-30",
    "AUM": 396264998,
    "Target": "4–6% p.a. above CPI over rolling seven-year periods"
  },
  "Allocation": [
    { "AssetClass": "SA Equity", "Actual": 0.43, "Strategic": 0.4 },
    { "AssetClass": "Global Equity", "Actual": 0.29, "Strategic": 0.3 },
    { "AssetClass": "Fixed Income", "Actual": 0.2, "Strategic": 0.2 },
    { "AssetClass": "Cash", "Actual": 0.08, "Strategic": 0.1 }
  ],
  "Fees": {
    "Date": "March 2026",
    "TER": 0.0028,
    "TIC": 0.0028
  }
}

Useful tags:

{d.Fund.Name}
{d.Fund.EffectiveDate:formatD('DD MMMM YYYY')}
R{d.Fund.AUM:formatN(0, '.', ' ')}
{d.Fees.TER:mult(100):toFixed(2):append('%')}

Design notes:

Client statement — Word

Use a flowing DOCX when transaction or holding counts vary.

{
  "Statement": {
    "Number": "ST-2026-0061",
    "Date": "2026-06-30"
  },
  "Client": {
    "Name": "Example Holdings",
    "AddressLines": ["18 Market Street", "Cape Town", "8001"]
  },
  "Transactions": [
    { "Date": "2026-06-03", "Description": "Contribution", "Amount": 25000 },
    { "Date": "2026-06-14", "Description": "Management fee", "Amount": -350.5 },
    { "Date": "2026-06-28", "Description": "Distribution", "Amount": 1320.75 }
  ],
  "ClosingBalance": 485970.25
}

Suggested document structure:

  1. Branded header with statement number and date.
  2. Client and account block in a two-column borderless table.
  3. Transaction table with a vertical [i] / [i+1] row pair.
  4. Closing balance in a separate totals table outside the loop.
  5. Notes and contact information in the footer.

Template row tags:

{d.Transactions[i].Date:formatD('DD MMM YYYY')}
{d.Transactions[i].Description}
{d.Transactions[i].Amount:formatN(2, '.', ' ')}

Profile card — PowerPoint or Word

Use a fixed image placeholder plus a compact metadata block.

{
  "Person": {
    "Name": "Ada Lovelace",
    "Role": "Quantitative Analyst",
    "Biography": "Ada leads portfolio analytics and reporting automation.",
    "Photo": "data:image/jpeg;base64,<BASE64_DATA>"
  }
}
{d.Person.Name}
{d.Person.Role:upperCase}
{d.Person.Biography:ellipsis(180)}

Put {d.Person.Photo:imageFit(contain)} in the temporary portrait's alt-text description. Use a neutral placeholder with the exact output aspect ratio.

Use the web application

Standard upload

Browse upload and drag-and-drop create a browser snapshot of the selected file. If you edit the template afterward, select it again to upload the new version.

Live template mode

On Chrome or Edge over HTTPS or localhost, Open live template can retain a secure read handle to the selected local file. The app polls the file, waits for a save to become stable, and regenerates the preview after a change.

Browser security intentionally limits this feature:

Auto preview

Auto preview is enabled by default. After valid JSON changes, generation is debounced by about 900 ms. An older in-flight request is aborted when a newer edit arrives, preventing stale PDFs from replacing the latest result.

Turn auto preview off when:

Read the preview status

Status Meaning
Waiting for a report No successful generation yet
Update queued Valid change is waiting for the debounce timer
Waiting for valid JSON JSON syntax must be corrected
Rendering update Backend population/conversion is running
Up to date Current template snapshot and JSON produced the visible PDF
Update failed Inspect the error message and backend log

Use the HTTP API

The interactive API schema is available at http://localhost:9010/docs.

Generate a PDF

POST /api/generate accepts multipart form data:

Field Type Description
template File .pptx or .docx template
data Text JSON object serialized as text

PowerShell using curl.exe:

curl.exe -X POST "http://localhost:9010/api/generate" `
  -F "template=@C:\Reports\fund-template.pptx" `
  -F "data=<C:\Reports\payload.json" `
  --output "C:\Reports\fund-report.pdf"

Python using httpx:

import json
from pathlib import Path

import httpx

template_path = Path("fund-template.pptx")
payload = {
    "Fund": {"Name": "Balanced Tracker Fund"},
    "EffectiveDate": "2026-06-30",
}

with template_path.open("rb") as template:
    response = httpx.post(
        "http://localhost:9010/api/generate",
        files={
            "template": (
                template_path.name,
                template,
                "application/vnd.openxmlformats-officedocument.presentationml.presentation",
            )
        },
        data={"data": json.dumps(payload)},
        timeout=240,
    )

response.raise_for_status()
Path("fund-report.pdf").write_bytes(response.content)
warnings = json.loads(response.headers.get("X-PDFGenerator-Warnings", "[]"))
print(warnings)

Browser JavaScript:

const form = new FormData();
form.append("template", templateFile);
form.append("data", JSON.stringify(payload));

const response = await fetch("http://localhost:9010/api/generate", {
  method: "POST",
  body: form,
});

if (!response.ok) {
  const error = await response.json();
  throw new Error(error.detail ?? `Generation failed: ${response.status}`);
}

const pdfUrl = URL.createObjectURL(await response.blob());
pdfFrame.src = pdfUrl;

The PDF response has Content-Disposition: inline. The converter name is returned in X-PDFGenerator-Renderer, and template warnings are returned in X-PDFGenerator-Warnings.

Inspect the populated Office file

Use POST /api/render-template with the same multipart fields. The response is the populated .pptx or .docx before PDF conversion. This endpoint is invaluable when checking:

Health and configuration endpoints

Endpoint Purpose
GET /health Backend process health and version
GET /api/config Public ports, template types, size limit, and selected converter
GET /api/converter/health Active LibreOffice or Gotenberg readiness
GET /api/gotenberg/health Explicit remote Gotenberg diagnostic
GET http://localhost:9012/health Guide service health

Fonts and output fidelity

Gotham and Montserrat are committed under assets/fonts and activated for the managed LibreOffice process. Use those exact family names in PowerPoint and Word for the most predictable PDF output.

Font checklist

Layout can still differ slightly between Microsoft Office and LibreOffice. Test the actual converter used in production, especially for line wrapping, chart labels, page breaks, and uncommon font weights.

Troubleshooting

The template does not stay selected

JSON is rejected

A tag becomes blank

A loop does not expand

A chart is blank or stale

  1. Call /api/render-template and open the populated Office file.
  2. Open Edit Data and verify the embedded worksheet values.
  3. Confirm the series/category formula includes both marker rows or columns.
  4. Ensure plotted cells are numbers rather than formatted strings.
  5. Apply ifEmpty(0) where blank input should become zero.
  6. Check that the JSON array contains data.

An image is not replaced

The PDF layout differs from PowerPoint or Word

Unknown internet requests appear in backend logs

Public IP addresses are routinely scanned for .env, .git/config, WordPress, GraphQL, and framework debug endpoints. A 404 Not Found response means those paths do not exist; it does not mean the application initiated the request.

For any internet-facing deployment:

Compatibility and current limits

Capability PPTX DOCX Notes
Basic and nested text tags Yes Yes Tags split across Office runs are joined
Static array indexes Yes Yes [0] and [i=0]
Vertical table-row loops No Yes Two adjacent template rows
Vertical native-chart loops Yes Yes Two adjacent embedded-workbook rows
Horizontal native-chart loops Yes Yes Two adjacent embedded-workbook columns
Native Office charts Yes Yes Workbook formulas and caches are refreshed
Base64/HTTP raster pictures Yes Yes Alt-text title/description tags
Exact-box image fill Yes Yes Default for an unformatted image tag
Headers, footers, notes text Yes Yes According to the native Office part
Entire slide duplication loops No N/A Planned compatibility work
Horizontal or nested Word loops N/A No Planned compatibility work
Images inside structural loops No No Use fixed image placeholders
Smart conditional blocks No No Only the documented formatter subset is supported
Aliases and translations No No Not implemented
ECharts or SVG generation No No Use native Office charts and raster pictures
Barcodes and file append No No Not implemented
Uploaded XLSX templates No No XLSX is supported only as embedded chart data

When migrating an existing Carbone template, begin with substitutions, Word table loops, native chart loops, and picture alt-text tags. Remove or redesign any unsupported smart conditions, nested structures, transformations, barcodes, and file operations.

Deployment and operations

Select the PDF converter

In config.py:

PDF_CONVERTER = "libreoffice"

uses the application-managed local renderer. To use the configured remote service:

PDF_CONVERTER = "gotenberg"

The selection is explicit. A failed local conversion is not silently sent to Gotenberg.

Production recommendations

Guide maintenance

guide/USER_GUIDE.md is the canonical source. The guide server converts it on every request, so a saved Markdown change is visible after refreshing the browser; no static site build is required. Keep examples synchronized with automated tests and information files/aicontext.md whenever the template language changes.

Template review checklist

Data

PowerPoint or Word design

Generation QA

Reference examples and documentation basis

This guide uses the same approachable data/template/result teaching pattern found in Carbone's documentation while describing PDFGenerator's smaller, tested compatibility surface. For broader template-design ideas, consult these official Carbone resources and then verify every feature against this guide's compatibility table:

The supplied Tracker template and JSON remain the authoritative end-to-end example for this repository: