Learn more about our native MCP
Directus Logo
  • Use Cases and Features
    • Headless CMS
      Manage and deliver content with ease
    • Backend-as-a-Service
      Build and ship applications faster
    • Headless Commerce
      A single source of truth for products
    • 100+ More Use Cases
      Build anything (or everything)
    • Instant APIs
      Connect a database, get REST + GraphQL APIs
    • Granular Policy-Based Auth
      Provide secure, autonomous data access
    • Visual Automation Builder
      Automate content and data workflows with ease
    • 50+ More Features
      Get everything you need out-of-the-box
    Project Showcase
    Built With Directus

    Built With Directus

    See what everyone's been building with Directus

  • Learn More
    • Blog
      Read our latest articles and guides
    • Case Studies
      Case studies and success stories
    • Community Forum
      Questions and conversations
    • Agency Directory
      Browse our list of agency partners
    • About Us
      Learn more about Directus and the team
    • Wall of Love
      See what others are saying about us
    • Contact
      Have a general inquiry or question for us?
    • Support
      Reach out to Directus support
    Watch Directus TV
    Directus TV
    Video

    Directus TV

    Go down the rabbit hole with hours of original video content from our team.

  • Developers
  • Enterprise
  • Pricing
Book a DemoGet StartedLog In
GitHub logo34,485
Directus Marketplace
Marketplace
  1. Extensions
  2. Integrations
  3. Templates
Back to Extensions
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

Generate PDF flow operation

Features

  • Generic pdfme template rendering from JSON
  • template.basePdf is the source of truth for page size and margins
  • Optional full-page backgrounds through a single background_image value
  • Output to Directus Files or raw base64 response
  • Dot notation support in schema names, for example user.first_name
  • Built-in local Roboto-Bold registration from fonts/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: pdfme template object
  • inputs: object or array of objects used by template fields
  • generate_options: optional object passed to pdfme.generate()
  • background_image: optional image source used as full-page background. Accepts data:image/..., a same-host /assets/<file_id> URL, or a Directus file ID
  • filename: output filename
  • title: Directus file title
  • storage: Directus storage adapter
  • folder: optional Directus folder
  • store_file: store the PDF in Directus Files, defaults to true
  • return_base64: include PDF base64 in the operation response, defaults to false

Response

  • file_id: Directus file ID when the PDF is stored
  • filename: generated filename
  • title: Directus file title used for the stored asset
  • storage: Directus storage adapter used for the output
  • stored: whether the PDF was uploaded to Directus Files
  • folder: Directus folder ID when provided
  • asset_url: asset URL when the file was stored and PUBLIC_URL is configured
  • mime_type: always application/pdf
  • bytes: generated PDF size in bytes
  • pages: number of generated pages (based on inputs)
  • warnings: non-fatal warnings collected during generation
  • base64: optional PDF base64 when return_base64 is true

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_options is for PDF metadata, color mode, and fonts. It does not control page size, margins, or schema positions.
  • background_image accepts data:image/..., a URL on the same host as Directus PUBLIC_URL under /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 basePdf is 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-Bold is auto-registered from fonts/Roboto-Bold.ttf when 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

Generate PDF demo

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

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)

@cesarechazu/directus-extension-generate-pdf

npm i @cesarechazu/directus-extension-generate-pdf

This extension is not sandboxed and can only be installed in self-hosted instances using npm. Learn more .

Cesar Mauricio
Cesar Mauricio
1.0.3
Works with v11.0.0+
Updated recently
MIT License
237.8 KiB (7 files)
npm
Repository
Report Issue
  • Directus LogoDirectus Logo

    A composable backend to build your Headless CMS, BaaS, and more. 

  • Solutions
    • Headless CMS
    • Backend-as-a-Service
    • Product Information
    • 100+ Things to Build
  • Resources
    • Documentation
    • Guides
    • Community
    • Release Notes
  • Support
    • Issue Tracker
    • Feature Requests
    • Community Chat
    • Cloud Dashboard
  • Organization
    • About
    • Careers
    • Brand Assets
    • Contact
©2026 Monospace Inc
  • Cloud Policies
  • License
  • Terms
  • Privacy