operation
Generate PDF
Directus Flow operation to generate generic PDFs from JSON using pdfme






Generate PDF
Directus Flow operation that generates PDF files from JSON templates using pdfme.
Flow Operation
Features
- Generic
pdfmetemplate rendering from JSON template.basePdfis the source of truth for page size and margins- Optional full-page backgrounds through a single
background_imagevalue - Output to Directus Files or raw base64 response
- Dot notation support in schema names, for example
user.first_name - Built-in local
Roboto-Boldregistration fromfonts/Roboto-Bold.ttf - Built-in plugins for text, images, shapes, dates, form controls, tables, and barcodes
Installation
Local extension:
cd extensions/directus-extension-generate-pdf
npm install
Then restart Directus so it reloads the extension.
To rebuild the published extension files after changing src/:
npm run build
Operation
- Extension package:
@cesarechazu/directus-extension-generate-pdf - Operation ID:
generate-pdf-operation - Display name:
Generate PDF
Inputs
template:pdfmetemplate objectinputs: object or array of objects used by template fieldsgenerate_options: optional object passed topdfme.generate()background_image: optional image source used as full-page background. Acceptsdata:image/..., a same-host/assets/<file_id>URL, or a Directus file IDfilename: output filenametitle: Directus file titlestorage: Directus storage adapterfolder: optional Directus folderstore_file: store the PDF in Directus Files, defaults totruereturn_base64: include PDF base64 in the operation response, defaults tofalse
Response
file_id: Directus file ID when the PDF is storedfilename: generated filenametitle: Directus file title used for the stored assetstorage: Directus storage adapter used for the outputstored: whether the PDF was uploaded to Directus Filesfolder: Directus folder ID when providedasset_url: asset URL when the file was stored andPUBLIC_URLis configuredmime_type: alwaysapplication/pdfbytes: generated PDF size in bytespages: number of generated pages (based oninputs)warnings: non-fatal warnings collected during generationbase64: optional PDF base64 whenreturn_base64istrue
Supported Schema Types
- Text:
text,multiVariableText - Media:
image,svg - Layout and shapes:
table,line,rectangle,ellipse - Date and time:
dateTime,date,time - Form controls:
select,radioGroup,checkbox - Barcodes:
qrcode,japanpost,ean13,ean8,code39,code128,nw7,itf14,upca,upce,gs1datamatrix,pdf417
Notes
- Page size and margins must be defined in
template.basePdf. - This operation does not override page size or margins with separate fields.
generate_optionsis for PDF metadata, color mode, and fonts. It does not control page size, margins, or schema positions.background_imageacceptsdata:image/..., a URL on the same host as DirectusPUBLIC_URLunder/assets/, or a Directus file ID (recommended).- URLs on other hosts are rejected. In this version, external background images must be uploaded to Directus first and then referenced by asset URL or file ID.
- Background images are validated as supported image files (
png,jpeg,webp,gif) before being embedded. - If a background is provided and
basePdfis blank-object mode, it is inserted as a full-page static background. - If a schema name is
user.first_name, the operation can resolve it from nested input data such as{ "user": { "first_name": "John" } }.
Fonts
- Default fallback font is
Roboto. Roboto-Boldis auto-registered fromfonts/Roboto-Bold.ttfwhen the file is present.- You can still define custom fonts in
generate_options.font.
Hello PDF Example
Template:
{
"basePdf": {
"width": 210,
"height": 297,
"padding": [10, 10, 10, 10]
},
"schemas": [
[
{
"name": "highlight_box",
"type": "rectangle",
"position": { "x": 20, "y": 42 },
"width": 170,
"height": 42,
"borderWidth": 1,
"borderColor": "#0F172A",
"color": "#F8FAFC",
"radius": 2
},
{
"name": "title",
"type": "text",
"position": { "x": 20, "y": 24 },
"width": 120,
"height": 12,
"fontSize": 24,
"fontName": "Roboto-Bold"
},
{
"name": "body",
"type": "text",
"position": { "x": 26, "y": 52 },
"width": 170,
"height": 24,
"fontSize": 12
},
{
"name": "separator",
"type": "line",
"position": { "x": 20, "y": 96 },
"width": 170,
"height": 1,
"color": "#CBD5E1"
},
{
"name": "qr_label",
"type": "text",
"position": { "x": 20, "y": 104 },
"width": 30,
"height": 6,
"fontSize": 8,
"content": "QR"
},
{
"name": "qr_code",
"type": "qrcode",
"position": { "x": 20, "y": 112 },
"width": 30,
"height": 30,
"content": "https://directus.io/docs",
"backgroundColor": "#FFFFFF",
"barColor": "#111827"
},
{
"name": "ean_label",
"type": "text",
"position": { "x": 60, "y": 104 },
"width": 90,
"height": 6,
"fontSize": 8,
"content": "EAN-13"
},
{
"name": "ean_code",
"type": "ean13",
"position": { "x": 60, "y": 112 },
"width": 90,
"height": 18,
"content": "2112345678900",
"backgroundColor": "#FFFFFF",
"barColor": "#111827",
"textColor": "#111827",
"includetext": true
},
{
"name": "footer",
"type": "text",
"position": { "x": 20, "y": 150 },
"width": 170,
"height": 10,
"fontSize": 10
}
]
]
}
Inputs:
{
"title": "Hello PDF",
"body": "This document shows text, a shape, a divider line, a QR code, and an EAN-13 barcode generated from JSON.",
"qr_code": "https://directus.io/docs",
"ean_code": "2112345678900",
"footer": "Useful for receipts, labels, certificates, or simple printable layouts."
}
Example Result
Multi-page inputs:
[
{ "title": "Page 1", "body": "First page" },
{ "title": "Page 2", "body": "Second page" }
]
Invoice Example
Template:
Show Template JSON{
"basePdf": {
"width": 210,
"height": 297,
"padding": [10, 10, 10, 10]
},
"schemas": [
[
{
"name": "header_box",
"type": "rectangle",
"position": { "x": 20, "y": 18 },
"width": 170,
"height": 28,
"borderWidth": 0,
"borderColor": "#0F172A",
"color": "#F8FAFC",
"radius": 2
},
{
"name": "invoice_title",
"type": "text",
"position": { "x": 24, "y": 24 },
"width": 80,
"height": 12,
"fontSize": 22,
"fontName": "Roboto-Bold",
"content": "INVOICE"
},
{
"name": "company_name",
"type": "text",
"position": { "x": 24, "y": 34 },
"width": 70,
"height": 6,
"fontSize": 9,
"content": "Example Labs LLC"
},
{
"name": "invoice_number_label",
"type": "text",
"position": { "x": 132, "y": 24 },
"width": 25,
"height": 6,
"fontSize": 9,
"content": "Number"
},
{
"name": "invoice_number",
"type": "text",
"position": { "x": 160, "y": 24 },
"width": 30,
"height": 6,
"fontSize": 9,
"fontName": "Roboto-Bold"
},
{
"name": "issue_date_label",
"type": "text",
"position": { "x": 132, "y": 32 },
"width": 25,
"height": 6,
"fontSize": 9,
"content": "Date"
},
{
"name": "issue_date",
"type": "text",
"position": { "x": 160, "y": 32 },
"width": 25,
"height": 6,
"fontSize": 9,
"fontName": "Roboto-Bold"
},
{
"name": "due_date_label",
"type": "text",
"position": { "x": 132, "y": 40 },
"width": 25,
"height": 6,
"fontSize": 9,
"content": "Due"
},
{
"name": "due_date",
"type": "text",
"position": { "x": 160, "y": 40 },
"width": 25,
"height": 6,
"fontSize": 9
},
{
"name": "customer_box",
"type": "rectangle",
"position": { "x": 20, "y": 56 },
"width": 82,
"height": 42,
"borderWidth": 1,
"borderColor": "#CBD5E1",
"color": "#F8FAFC",
"radius": 2
},
{
"name": "seller_box",
"type": "rectangle",
"position": { "x": 108, "y": 56 },
"width": 82,
"height": 42,
"borderWidth": 1,
"borderColor": "#CBD5E1",
"color": "#FFFFFF",
"radius": 2
},
{
"name": "bill_to_label",
"type": "text",
"position": { "x": 24, "y": 60 },
"width": 30,
"height": 6,
"fontSize": 8,
"content": "Bill To"
},
{
"name": "customer_name",
"type": "text",
"position": { "x": 24, "y": 68 },
"width": 70,
"height": 8,
"fontSize": 12,
"fontName": "Roboto-Bold"
},
{
"name": "customer_tax_id",
"type": "text",
"position": { "x": 24, "y": 78 },
"width": 70,
"height": 6,
"fontSize": 9
},
{
"name": "customer_address",
"type": "text",
"position": { "x": 24, "y": 86 },
"width": 70,
"height": 8,
"fontSize": 8
},
{
"name": "from_label",
"type": "text",
"position": { "x": 112, "y": 60 },
"width": 20,
"height": 6,
"fontSize": 8,
"content": "From"
},
{
"name": "seller_name",
"type": "text",
"position": { "x": 112, "y": 68 },
"width": 70,
"height": 8,
"fontSize": 11,
"fontName": "Roboto-Bold"
},
{
"name": "seller_tax_id",
"type": "text",
"position": { "x": 112, "y": 78 },
"width": 70,
"height": 6,
"fontSize": 9
},
{
"name": "seller_address",
"type": "text",
"position": { "x": 112, "y": 86 },
"width": 70,
"height": 8,
"fontSize": 8
},
{
"name": "items_header_box",
"type": "rectangle",
"position": { "x": 20, "y": 108 },
"width": 170,
"height": 10,
"borderWidth": 0,
"borderColor": "#E2E8F0",
"color": "#E2E8F0",
"radius": 1
},
{
"name": "items_header_line",
"type": "line",
"position": { "x": 20, "y": 118 },
"width": 170,
"height": 1,
"color": "#CBD5E1"
},
{
"name": "description_header",
"type": "text",
"position": { "x": 24, "y": 111 },
"width": 78,
"height": 6,
"fontSize": 8,
"fontName": "Roboto-Bold",
"content": "Description"
},
{
"name": "qty_header",
"type": "text",
"position": { "x": 110, "y": 111 },
"width": 15,
"height": 6,
"fontSize": 8,
"fontName": "Roboto-Bold",
"content": "Qty"
},
{
"name": "price_header",
"type": "text",
"position": { "x": 130, "y": 111 },
"width": 20,
"height": 6,
"fontSize": 8,
"fontName": "Roboto-Bold",
"content": "Price"
},
{
"name": "discount_header",
"type": "text",
"position": { "x": 150, "y": 111 },
"width": 16,
"height": 6,
"fontSize": 8,
"fontName": "Roboto-Bold",
"content": "Disc."
},
{
"name": "total_header",
"type": "text",
"position": { "x": 170, "y": 111 },
"width": 18,
"height": 6,
"fontSize": 8,
"fontName": "Roboto-Bold",
"content": "Total"
},
{
"name": "invoice_items",
"type": "table",
"position": { "x": 20, "y": 120 },
"width": 170,
"height": 64,
"content": "[]",
"showHead": false,
"repeatHead": false,
"head": ["Description", "Qty", "Price", "Disc.", "Total"],
"headWidthPercentages": [50, 12, 14, 10, 14],
"tableStyles": {
"borderColor": "#CBD5E1",
"borderWidth": 0.2
},
"headStyles": {
"fontName": "Roboto-Bold",
"alignment": "left",
"verticalAlignment": "middle",
"fontSize": 8,
"lineHeight": 1,
"characterSpacing": 0,
"fontColor": "#0F172A",
"backgroundColor": "#E2E8F0",
"borderColor": "#CBD5E1",
"borderWidth": { "top": 0, "right": 0, "bottom": 0, "left": 0 },
"padding": { "top": 2, "right": 2, "bottom": 2, "left": 2 }
},
"bodyStyles": {
"fontName": "Roboto",
"alignment": "left",
"verticalAlignment": "middle",
"fontSize": 8,
"lineHeight": 1,
"characterSpacing": 0,
"fontColor": "#111827",
"backgroundColor": "#FFFFFF",
"alternateBackgroundColor": "#F8FAFC",
"borderColor": "#E2E8F0",
"borderWidth": { "top": 0, "right": 0, "bottom": 0.1, "left": 0 },
"padding": { "top": 2, "right": 2, "bottom": 2, "left": 2 }
},
"columnStyles": {
"alignment": {
"1": "center",
"2": "right",
"3": "right",
"4": "right"
}
}
},
{
"name": "totals_line",
"type": "line",
"position": { "x": 120, "y": 194 },
"width": 70,
"height": 1,
"color": "#CBD5E1"
},
{
"name": "subtotal_label",
"type": "text",
"position": { "x": 130, "y": 200 },
"width": 30,
"height": 6,
"fontSize": 9,
"content": "Subtotal"
},
{
"name": "subtotal",
"type": "text",
"position": { "x": 165, "y": 200 },
"width": 25,
"height": 6,
"fontSize": 9
},
{
"name": "discount_total_label",
"type": "text",
"position": { "x": 130, "y": 208 },
"width": 30,
"height": 6,
"fontSize": 9,
"content": "Discount"
},
{
"name": "discount_total",
"type": "text",
"position": { "x": 165, "y": 208 },
"width": 25,
"height": 6,
"fontSize": 9
},
{
"name": "tax_label",
"type": "text",
"position": { "x": 130, "y": 216 },
"width": 30,
"height": 6,
"fontSize": 9,
"content": "Tax"
},
{
"name": "tax",
"type": "text",
"position": { "x": 165, "y": 216 },
"width": 25,
"height": 6,
"fontSize": 9
},
{
"name": "total_label",
"type": "text",
"position": { "x": 130, "y": 226 },
"width": 30,
"height": 7,
"fontSize": 10,
"fontName": "Roboto-Bold"
},
{
"name": "total",
"type": "text",
"position": { "x": 165, "y": 226 },
"width": 25,
"height": 7,
"fontSize": 10,
"fontName": "Roboto-Bold"
},
{
"name": "payment_box",
"type": "rectangle",
"position": { "x": 20, "y": 244 },
"width": 170,
"height": 34,
"borderWidth": 1,
"borderColor": "#CBD5E1",
"color": "#F8FAFC",
"radius": 2
},
{
"name": "payment_note",
"type": "text",
"position": { "x": 24, "y": 250 },
"width": 116,
"height": 12,
"fontSize": 9
},
{
"name": "bank_reference",
"type": "text",
"position": { "x": 24, "y": 264 },
"width": 116,
"height": 8,
"fontSize": 8
},
{
"name": "payment_qr",
"type": "qrcode",
"position": { "x": 150, "y": 250 },
"width": 24,
"height": 24,
"backgroundColor": "#FFFFFF",
"barColor": "#111827"
}
]
]
}
Inputs:
Show Inputs JSON{
"invoice_number": "INV-0001",
"issue_date": "2026-02-28",
"due_date": "2026-03-15",
"customer_name": "Ada Lovelace",
"customer_tax_id": "Tax ID: 20-12345678-9",
"customer_address": "12 Analytical Engine St, London",
"seller_name": "Example Labs LLC",
"seller_tax_id": "VAT ID: GB-99887766",
"seller_address": "42 Computing Ave, Manchester",
"invoice_items": [
["Discovery workshop and architecture review", "1", "$600.00", "$0.00", "$600.00"],
["Custom flow development", "8", "$120.00", "$80.00", "$880.00"],
["PDF template implementation", "3", "$150.00", "$0.00", "$450.00"],
["QA and browser validation", "2", "$90.00", "$0.00", "$180.00"],
["Deployment and handoff session", "1", "$220.00", "$20.00", "$200.00"]
],
"subtotal": "$2,310.00",
"discount_total": "$100.00",
"tax": "$464.10",
"total_label": "Total",
"total": "$2,674.10",
"payment_note": "Please transfer the balance before the due date. Scan the QR to open the payment portal or use the bank reference below.",
"bank_reference": "Reference: INV-0001 | IBAN: GB82 WEST 1234 5698 7654 32",
"payment_qr": "https://pay.example.com/invoices/INV-0001"
}
Invoice example result
Generate Options Example
{
"title": "Invoice INV-0001",
"subject": "Professional services invoice",
"author": "Example Labs LLC",
"creator": "Directus Flow: Generate PDF",
"producer": "pdfme",
"keywords": ["invoice", "directus", "billing", "pdf"],
"colorType": "rgb",
"lang": "en"
}
License
MIT (MIT)