Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GENERATE PDF

The GENERATE PDF keyword creates PDF documents from HTML templates or Markdown content, enabling bots to produce professional reports, invoices, certificates, and other documents.

Note: This keyword uses spaces, not underscores. Write GENERATE PDF not GENERATE_PDF.


Syntax

result = GENERATE PDF template, data, "output.pdf"

Parameters

ParameterTypeDescription
templateStringPath to HTML template or Markdown file
dataObjectTemplate variables to substitute
outputStringOutput path for the generated PDF

Description

GENERATE PDF renders an HTML or Markdown template into a PDF document, substituting placeholders with provided values. The generated PDF is stored in the bot’s drive storage and can be downloaded, emailed, or processed further.

Use cases include:

  • Generating invoices and receipts
  • Creating reports and summaries
  • Producing certificates and credentials
  • Building contracts and agreements
  • Creating personalized documents

Examples

Basic PDF Generation

' Generate PDF from template with data
data = #{
    "title": "Invoice",
    "date": FORMAT(NOW(), "MMMM DD, YYYY")
}
result = GENERATE PDF "templates/invoice.html", data, "invoices/inv-001.pdf"

TALK "Invoice generated!"

With Template Variables

' Generate PDF with data substitution
data = #{
    "customer_name": customer.name,
    "customer_email": customer.email,
    "invoice_number": invoice_id,
    "date": FORMAT(NOW(), "MMMM DD, YYYY"),
    "items": order_items,
    "subtotal": order_subtotal,
    "tax": order_tax,
    "total": order_total
}

result = GENERATE PDF "templates/invoice.html", data, "invoices/inv-" + invoice_id + ".pdf"

TALK "Invoice #" + invoice_id + " generated!"

Generate and Download

' Create PDF and send to user
data = #{
    "title": "Monthly Report",
    "period": FORMAT(NOW(), "MMMM YYYY"),
    "data": report_data
}

result = GENERATE PDF "templates/report.html", data, "temp/report.pdf"

DOWNLOAD result.url AS "Monthly Report.pdf"
TALK "Here's your report!"

Generate and Email

' Create PDF and email it
data = #{
    "party_a": company_name,
    "party_b": customer_name,
    "effective_date": FORMAT(NOW(), "MMMM DD, YYYY"),
    "terms": contract_terms
}

result = GENERATE PDF "templates/contract.html", data, "contracts/" + contract_id + ".pdf"

SEND MAIL customer_email, "Your Contract", 
    "Please find attached your contract for review.",
    [result.localName]

TALK "Contract sent to " + customer_email

Template Format

HTML Template

<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: Arial, sans-serif; }
        .header { text-align: center; margin-bottom: 20px; }
        .invoice-number { color: #666; }
        table { width: 100%; border-collapse: collapse; }
        th, td { border: 1px solid #ddd; padding: 8px; }
        .total { font-weight: bold; font-size: 1.2em; }
    </style>
</head>
<body>
    <div class="header">
        <h1>INVOICE</h1>
        <p class="invoice-number">{{invoice_number}}</p>
    </div>
    
    <p><strong>Date:</strong> {{date}}</p>
    <p><strong>Customer:</strong> {{customer_name}}</p>
    
    <table>
        <tr>
            <th>Item</th>
            <th>Quantity</th>
            <th>Price</th>
        </tr>
        {{#each items}}
        <tr>
            <td>{{this.name}}</td>
            <td>{{this.quantity}}</td>
            <td>${{this.price}}</td>
        </tr>
        {{/each}}
    </table>
    
    <p class="total">Total: ${{total}}</p>
</body>
</html>

Markdown Template

# {{title}}

**Date:** {{date}}
**Prepared for:** {{customer_name}}

## Summary

{{summary}}

## Details

{{#each items}}
- **{{this.name}}:** {{this.description}}
{{/each}}

---
Generated by General Bots

Template Placeholders

SyntaxDescription
{{variable}}Simple variable substitution
{{#each items}}...{{/each}}Loop over array
{{#if condition}}...{{/if}}Conditional rendering
{{#unless condition}}...{{/unless}}Negative conditional
{{this.property}}Access property in loop

Common Use Cases

Invoice Generation

' Generate a complete invoice
items = FIND "order_items" WHERE order_id = order.id

data = #{
    "invoice_number": "INV-" + FORMAT(order.id, "00000"),
    "date": FORMAT(NOW(), "MMMM DD, YYYY"),
    "due_date": FORMAT(DATEADD(NOW(), 30, "day"), "MMMM DD, YYYY"),
    "customer_name": customer.name,
    "customer_address": customer.address,
    "items": items,
    "subtotal": FORMAT(order.subtotal, "#,##0.00"),
    "tax": FORMAT(order.tax, "#,##0.00"),
    "total": FORMAT(order.total, "#,##0.00")
}

result = GENERATE PDF "templates/invoice.html", data, "invoices/" + order.id + ".pdf"

TALK "Invoice generated: " + result.localName

Certificate Generation

' Generate completion certificate
data = #{
    "recipient_name": user.name,
    "course_name": course.title,
    "completion_date": FORMAT(NOW(), "MMMM DD, YYYY"),
    "certificate_id": GUID(),
    "instructor_name": course.instructor
}

result = GENERATE PDF "templates/certificate.html", data, "certificates/" + user.id + "-" + course.id + ".pdf"

DOWNLOAD result.url AS "Certificate - " + course.title + ".pdf"
TALK "Congratulations! Here's your certificate!"

Report Generation

' Generate monthly sales report
sales_data = FIND "sales" WHERE 
    date >= DATEADD(NOW(), -30, "day")

summary = AGGREGATE sales_data SUM amount
count = AGGREGATE sales_data COUNT

data = #{
    "title": "Monthly Sales Report",
    "period": FORMAT(NOW(), "MMMM YYYY"),
    "total_sales": FORMAT(summary, "$#,##0.00"),
    "transaction_count": count,
    "sales_data": sales_data,
    "generated_at": FORMAT(NOW(), "YYYY-MM-DD HH:mm")
}

result = GENERATE PDF "templates/sales-report.html", data, "reports/sales-" + FORMAT(NOW(), "YYYYMM") + ".pdf"

TALK "Sales report generated!"

Contract Generation

' Generate service agreement
data = #{
    "contract_number": contract_id,
    "client_name": client.name,
    "client_company": client.company,
    "service_description": selected_service.description,
    "monthly_fee": FORMAT(selected_service.price, "$#,##0.00"),
    "start_date": FORMAT(start_date, "MMMM DD, YYYY"),
    "term_months": contract_term,
    "end_date": FORMAT(DATEADD(start_date, contract_term, "month"), "MMMM DD, YYYY")
}

result = GENERATE PDF "templates/service-agreement.html", data, "contracts/sa-" + contract_id + ".pdf"

TALK "Service agreement ready for signature!"

Return Value

Returns an object with generation details:

PropertyDescription
result.urlFull URL to the generated PDF (S3/MinIO path)
result.localNameLocal filename of the generated PDF

Error Handling

ON ERROR RESUME NEXT

data = #{
    "customer_name": customer_name,
    "total": order_total
}

result = GENERATE PDF "templates/invoice.html", data, "invoices/test.pdf"

IF ERROR THEN
    TALK "PDF generation failed: " + ERROR MESSAGE
    
    IF INSTR(ERROR MESSAGE, "template") > 0 THEN
        TALK "Template file not found."
    ELSE IF INSTR(ERROR MESSAGE, "storage") > 0 THEN
        TALK "Not enough storage space."
    ELSE
        TALK "Sorry, I couldn't generate the document. Please try again."
    END IF
ELSE
    TALK "PDF generated successfully!"
END IF

ON ERROR GOTO 0

Common Errors

ErrorCauseSolution
TEMPLATE_NOT_FOUNDTemplate file doesn’t existVerify template path
INVALID_TEMPLATETemplate has syntax errorsCheck template format
MISSING_VARIABLERequired placeholder not providedInclude all variables
STORAGE_FULLInsufficient spaceClean up storage
RENDER_ERRORHTML/CSS rendering issueSimplify template

Styling Tips

Supported CSS

  • Basic typography (fonts, sizes, colors)
  • Box model (margins, padding, borders)
  • Tables and layouts
  • Page breaks (page-break-before, page-break-after)
  • Print media queries (@media print)

Page Setup

<style>
    @page {
        size: A4;
        margin: 2cm;
    }
    
    .page-break {
        page-break-after: always;
    }
    
    @media print {
        .no-print { display: none; }
    }
</style>

Configuration

No specific configuration required. Uses bot’s standard drive settings from config.csv:

name,value
drive-provider,seaweedfs
drive-url,http://localhost:8333
drive-bucket,my-bot

Implementation Notes

  • Implemented in Rust under src/file/pdf.rs
  • Uses headless browser rendering for HTML
  • Supports embedded images (base64 or relative paths)
  • Handles Unicode and special characters
  • Maximum PDF size: 50 MB
  • Template caching for performance

  • MERGE PDF — Combine multiple PDFs
  • FILL — Fill templates with data (alternative approach)
  • READ — Read template content
  • DOWNLOAD — Send PDF to user
  • SEND MAIL — Email PDF as attachment
  • WRITE — Create template dynamically

Summary

GENERATE PDF creates professional PDF documents from HTML or Markdown templates with variable substitution. Use it for invoices, reports, certificates, contracts, and any document that needs a polished format. Templates support loops, conditionals, and styling for flexible document generation. Combine with DOWNLOAD to deliver PDFs to users or SEND MAIL to email them as attachments.

Syntax reminder: Always use GENERATE PDF (with space), not GENERATE_PDF.