# 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

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

---

## Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `template` | String | Path to HTML template or Markdown file |
| `data` | Object | Template variables to substitute |
| `output` | String | Output 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

```basic
' 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

```basic
' 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

```basic
' 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

```basic
' 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

```html
<!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

```markdown
# {{title}}

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

## Summary

{{summary}}

## Details

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

---
Generated by General Bots
```

---

## Template Placeholders

| Syntax | Description |
|--------|-------------|
| `{{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

```basic
' 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

```basic
' 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

```basic
' 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

```basic
' 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:

| Property | Description |
|----------|-------------|
| `result.url` | Full URL to the generated PDF (S3/MinIO path) |
| `result.localName` | Local filename of the generated PDF |

---

## Error Handling

```basic
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

| Error | Cause | Solution |
|-------|-------|----------|
| `TEMPLATE_NOT_FOUND` | Template file doesn't exist | Verify template path |
| `INVALID_TEMPLATE` | Template has syntax errors | Check template format |
| `MISSING_VARIABLE` | Required placeholder not provided | Include all variables |
| `STORAGE_FULL` | Insufficient space | Clean up storage |
| `RENDER_ERROR` | HTML/CSS rendering issue | Simplify 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

```html
<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`:

```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

---

## Related Keywords

- [MERGE PDF](keyword-merge-pdf.md) — Combine multiple PDFs
- [FILL](keyword-fill.md) — Fill templates with data (alternative approach)
- [READ](keyword-read.md) — Read template content
- [DOWNLOAD](keyword-download.md) — Send PDF to user
- [SEND MAIL](keyword-send-mail.md) — Email PDF as attachment
- [WRITE](keyword-write.md) — 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`.