---
source: https://carbone.io/documentation/design/template-formats/html.html
title: "HTML Templates | PDF Generation from HTML & CSS | Carbone"
description: "Generate pixel-perfect PDFs from HTML and CSS templates with built-in dynamic data injection, loops, conditions, charts, barcodes, and custom headers/footers. Ideal for invoices, reports, certificates, and contracts."
generated_at: "2026-07-13"
---

# HTML templates

Generate pixel-perfect PDFs from HTML and CSS templates with built-in dynamic data injection, loops, conditions, charts, barcodes, and custom headers/footers. Ideal for invoices, reports, certificates, and contracts.  
COMMUNITY FEATURE Available for:  
✓ Carbone Cloud  
✓ Carbone On-premise  
✓ Embedded Carbone JS   v5.0+ 

Carbone supports HTML as both an input template and an output format, enabling you to generate PDFs that render exactly as they do in a browser directly from HTML and CSS using a Chromium-based rendering engine. Unlike tools that only convert HTML to PDF, Carbone is a full template engine: inject JSON data, loop over arrays, apply conditions, format dates and currencies, and generate professional documents — invoices, reports, certificates, contracts — all in a single API call, without a separate templating layer.

## Basic

Carbone needs 2 elements to generate a document:

-   **JSON data**: coming from your application / database / API (e.g. the following JSON)
-   **An HTML Template**: made from an existing website, email, or from your code editor. The following template includes Carbone tags: the `{d.user.firstName}`, `{d.user.lastName}`, `{d.user.email}`, and `{d.user.membershipLevel}`. These tags will be replaced with the corresponding values from the JSON data.

The **d** in **{d.user.firstName}** refers to the root of your JSON data. The path after **d** (e.g., user.firstName) must match the structure of your JSON. You can use any valid HTML structure; Carbone will only replace the tags with the corresponding data.

```cdata
{
  "user": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@carbone.io"
  },
  "membershipLevel": "Premium"
}
```

```ctemplate
 <!DOCTYPE html>
 <html>
    <head>
        <title>Welcome Email </title>
    </head>
    <body>
        <h1>Welcome, {d.user.firstName} {d.user.lastName}! </h1>
        <p>Thank you for registering with us. </p>
        <p>Your email address is: {d.user.email} </p>
        <p>Your membership level: {d.membershipLevel} </p>
    </body>
 </html>
```

```cresult
<!DOCTYPE html>
<html>
    <head>
        <title>Welcome Email</title>
    </head>
    <body>
        <h1>Welcome, <strong>John Doe</strong>!</h1>
        <p>Thank you for registering with us.</p>
        <p>Your email address is: <strong><a>john.doe@carbone.io</a></strong></p>
        <p>Your membership level: <strong>Premium</strong> </p>
    </body>
</html>
```

## Loops

Loops in Carbone allow you to repeat a section of your template for each item in an array from your JSON data. This is useful for generating lists, tables, images, or any repeated content dynamically.

A loop in Carbone requires two tags:

-   **Start Tag**: Marks the beginning of the repeated block (e.g., `{d.users[i].id}`). The **i** is the loop iterator (automatically managed by Carbone).
-   **End Tag**: Marks the end of the repeated block (e.g., `{d.users[i+1]}`). The end tag **i+1** does not refer to an actual array item but signals the end of the pattern.

All content (text, Carbone tags, HTML, and styles) between the start and end tags is repeated.

```cdata
{
  "users": [
    { "id": 1, "name": "Alice", "role": "Admin" },
    { "id": 2, "name": "Bob", "role": "Editor" },
    { "id": 3, "name": "Charlie", "role": "Viewer" }
  ]
}
```

```ctemplate
<!DOCTYPE html>
<html>
<body>
    <h1>User List</h1>
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Role</th>
            </tr>
        </thead>
        <tbody>
            <!-- Start of loop -->
            <tr>
                <td>{d.users[i].id}</td>
                <td>{d.users[i].name}</td>
                <td>{d.users[i].role}</td>
            </tr>
            <!-- End of loop -->
            <tr>
                <td>{d.users[i+1]}</td>
            </tr>
        </tbody>
    </table>
</body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
<body>
    <h1>User List</h1>
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Role</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>Alice</td>
                <td>Admin</td>
            </tr>
            <tr>
                <td>2</td>
                <td>Bob</td>
                <td>Editor</td>
            </tr>
            <tr>
                <td>3</td>
                <td>Charlie</td>
                <td>Viewer</td>
            </tr>
        </tbody>
    </table>
</body>
</html>
```

> ℹ️ **Note:** Unlike traditional programming, Carbone loops do not require keywords like "for" or "foreach". The loop is defined implicitly by the tags.

## Conditions

Three methods are available to conditionally show, hide, or style elements in your HTML templates. Each method is suited to different use cases, depending on the complexity of your conditions and the structure of your content:

-   **Inline CSS/HTML Conditions**: Simple, and granular control over individual HTML/CSS elements. Use when you need to show/hide a single HTML element or CSS properties/attributes based on a data field.
-   **Smart conditions**: Use `:drop()` or `:keep()` formatters for removing or retaining entire elements, such as: tables, columns, rows, paragraphs, divs, or spans.
-   **Conditional Blocks**: Use `:showBegin/showEnd` or `:hideBegin/hideEnd` for showing or hiding large sections of content, including multiple elements, paragraphs, tables, or pages.

### Inline HTML/CSS Conditions

Carbone allows you to dynamically inject CSS styles into your HTML templates using conditional logic and data fields. This is useful for showing/hiding elements or applying styles based on your dataset. Carbone supports the following conditional techniques:

-   **[Inject CSS Values](#conditions-inject-css-values-example)**: Use Carbone’s inline conditions to dynamically set CSS property values (such as `display`, `visibility`, or `opacity`) based on your data.
-   **[Inject CSS Properties](#conditions-inject-css-properties-example)**: You can inject entire CSS properties from your dataset.
-   **[Inject Style Attribute](#conditions-inject-style-attribute-example)**: Inject a full style attribute from a data field.
-   **[Inject HTML Blocks](#conditions-inject-html-block)**: Use the `:html` formatter to inject raw HTML strings from your dataset with conditions.

#### Inject CSS Values Example

In the following example, the `display`, `visibility`, and `opacity` properties are conditionally set using Carbone tags:

-   The <div> element is hidden if `d.showDiv` is false.
-   The <p> element is hidden if `d.showParagraph` is false.
-   The <span> element is invisible if `d.showSpan` is false.

```cdata
{
    "display": "none",
    "showDiv": false,
    "showParagraph": false,
    "showSpan": false
}
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <!-- Inject CSS values -->
        <div style="display:{d.display};">This element is hidden and takes no space, only if 'showDiv' is 'true'.</div>

        <!-- Inject CSS values based on conditions -->
        <div style="display:{d.showDiv:ifEQ(true):show('block'):elseShow('none')};">This element is hidden and takes no space, only if 'showDiv' is 'false'.</div>
        <p style="visibility:{d.showParagraph:ifEQ(true):show('visible'):elseShow('hidden')};"> This paragraph is hidden but occupies space, only if 'showParagraph' is 'false'.</p>
        <span style="opacity:{d.showSpan:ifEQ(true):show('1'):elseShow('0')};">This element is invisible but still clickable, only if the 'showSpan' is 'false'.</span>
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <!-- Inject CSS values -->
        <div style="display:none;">This element is hidden and takes no space, only if 'showDiv' is 'true'.</div>

        <!-- Inject CSS values based on conditions -->
        <div style="display:none;">This element is hidden and takes no space, only if 'showDiv' is 'true'.</div>
        <p style="visibility:hidden;"> This paragraph is hidden but occupies space, only if 'showParagraph' is 'false'. </p>
        <span style="opacity:0;">This element is invisible but still clickable, only if the 'showSpan' is 'false'.</span>
    </body>
</html>
```

#### Inject CSS Properties Example

This example demonstrates how to dynamically inject entire CSS properties into an HTML element using Carbone tags. Two methods are available:

-   **Direct Injection**: The first <img> tag injects the value of d.imageStyle directly into its style attribute.
-   **Conditional Injection**: The second <img> tag uses a condition to determine which CSS property to apply, either the value of `d.imageStyle` or a static string "display:block".

```cdata
{
    "showImage": true,
    "imageStyle": "display: none;"
}
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <!-- Inject CSS properties -->
        <img src="image.jpg" style="width: 150px;{d.imageStyle}border-radius: 8px;" alt="Hidden Image">

        <!-- Inject CSS properties based on a condition -->
        <img src="image.jpg" style="{d.showImage:ifEQ(true):show('display:block;'):elseShow(d.imageStyle)}" alt="Visible Image">
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <!-- Inject CSS properties -->
        <img src="image.jpg" style="width: 150px;display: none;border-radius: 8px;" alt="Hidden Image">

        <!-- Inject CSS properties based on a condition -->
        <img src="image.jpg" style="display:block;" alt="Visible Image">
    </body>
</html>
```

#### Inject Style Attribute Example

This example shows how to inject an entire style attribute from your dataset into an HTML element.

-   **Direct Injection**: The first button uses the style defined in `d.buttonStyles1`.
-   **Conditional Injection**: The second button applies the value of `d.buttonStyles1` if `d.buttonTheme` is "forest", otherwise it uses the value of `d.buttonStyles2`.

```cdata
{
    "buttonTheme": "ocean",
    "buttonStyles1": "style="background-color: green;color: white;border: none;"",
    "buttonStyles2": "style="background-color: black;color: red;border: 1px solid white;""
}
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <!-- 3. Inject CSS Style Attribute -->
        <button {d.buttonStyles1}>Click Me</button>
        <!-- Inject CSS Style Attribute based on a condition -->
        <button {d.buttonTheme:ifEQ('forest'):show(d.buttonStyles1):elseShow(d.buttonStyles2)}>Click Me</button>
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <!-- 3. Inject CSS Style attribute -->
        <button style="background-color: green;color: white;border: none; >Click Me</button>
        <!-- Inject CSS Style Attribute based on a condition -->
        <button style="background-color: black;color: red;border: 1px solid white;">Click Me</button>
    </body>
</html>
```

#### Inject HTML Block

This example demonstrates how to inject raw HTML blocks from your dataset into your template using conditions and the **:html** formatter.

-   **Direct Injection**: The first line directly renders the HTML stored in `d.noteSection`.
-   **Conditional Injection**: The second line conditionally renders either `d.noteSection` if `d.displayNote` is true, or `d.nothingSection` if false.

```cdata
{
    "displayNote": false,
    "noteSection": "<div style='border-left: 3px solid #ff9800; padding: 8px;'><strong>Note:</strong> Maintenance at 2 AM UTC.</div>",
    "nothingSection": "<strong style='text-align: center; padding: 10px; background: #f0f8ff;'>Nothing Significant To Report.</strong>"
}
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <!-- Inject HTML Block -->
        {d.noteSection:html}

        <!-- Inject HTML Block based on a condition -->
        {d.displayNote:ifEQ(true):show(d.noteSection):elseShow(d.nothingSection):html}
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <!-- Inject HTML Block -->
        <div style='border-left: 3px solid #ff9800; padding: 8px;'><strong>Note:</strong> Maintenance at 2 AM UTC.</div>

        <!-- Inject HTML Block based on a condition -->
        <strong style='text-align: center; padding: 10px; background: #f0f8ff;'>Nothing Significant To Report.</strong>
    </body>
</html>
```

### Drop / Keep

The `:drop()` and `:keep()` formatters allow you to conditionally remove or retain elements (such as tables, rows, or paragraphs) in your HTML templates. These formatters are especially useful for dynamic content generation, where you want to show or hide elements based on data conditions. Syntax:

-   **:drop(element)** : Removes the element if the condition is true.
-   **:keep(element)** : Retains the element if the condition is true.

| Element | Description | Tag Position | Example |
| --- | --- | --- | --- |
| p | Drop/keep a paragraph | Within a paragraph | {d.text:ifEM:drop(p)} |
| row | Drop/keep a table row | Inside a table cell | {d.condition:ifNE(value):keep(row)} |
| col | Drop/keep an entire table column | In any cell of the target column | {d.condition:ifEQ(value):drop(col)} |
| table | Drop/keep an entire table | Within a table row | {d.condition:ifEQ(value):drop(table)} |
| div | Drop/keep a div block | Within a div element | {d.condition:ifEQ(value):drop(div)} |
| span | Drop/keep a span element | Within a span element | {d.condition:ifEQ(value):drop(span)} |

**Notes**

-   For paragraphs and rows, you can specify a second parameter to drop/keep the next N elements (e.g., `:drop(p, 3)`). This is not supported for `col`.
-   No Chaining: Do not chain other formatters after :drop() or :keep().
-   HTML-Specific: Only `table`, `col`, `row`, `p`, `div`, and `span` elements are supported in HTML templates.
-   Clean Output: The Carbone tag is removed from the final report.

#### Drop a Table Example

```cdata
{
  "showTable": false,
  "list": [
    {
      "name": "Model 5"
    },
    {
      "name": "Model 3"
    },
    {
      "name": "Falcon Heavy"
    }
  ]
}
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <table>{d.showTable:ifEQ(false):drop(table)}
            <tr><td>{d.list[i].name}</td></tr>
            <tr><td>{d.list[i+1].name}</td></tr>
        </table>
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
    </body>
</html>
```

#### Drop a Table Row Example

```cdata
[
  { "name": "Falcon 9" },
  { "name": "Model 5" },
  { "name": "Model 3" },
  { "name": "Falcon Heavy" }
]
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <table>
            <tr><td>{d[i].name}{d[i].name:ifIN('Falcon'):drop(row)}</td></tr>
            <tr><td>{d[i+1].name}</td></tr>
        </table>
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <table>
            <tr><td>Model 5</td></tr>
            <tr><td>Model 3</td></tr>
        </table>
    </body>
</html>
```

#### Drop a Paragraph Example

```cdata
{ "text": "" }
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <p>{d.text}{d.text:ifEM:drop(p, 2)}This paragraph and the next one will be dropped if 'text' is empty.</p>
        <p>This paragraph will also be dropped.</p>
        <p>This paragraph will remain.</p>
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <p>This paragraph will remain.</p>
    </body>
</html>
```

### Conditional Sections

The pair of `:showBegin/:showEnd` and `:hideBegin/;hideEnd` formatters allow you to conditionally display a section of content in your HTML templates. These formatters are useful when you want to show/hide whole pages or groups of text, tables, or other elements based on data conditions. Syntax:

-   `:showBegin` or `:hideBegin`: Marks the start of a block to show/hide if the condition is true.
-   `:showEnd` or `hideEnd`: Marks the end of the block.

#### showBegin / showEnd Example

The **details** section containing a list is hidden using the `showBegin/showEnd` formatters, only if the `showDetails` field is `true`.

```cdata
{
  "showDetails": false,
  "product": "Carbone Pro"
}
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <p>Product: {d.product}</p>
        {d.showDetails:ifEQ(true):showBegin}
        <p>Details:</p>
        <ul>
            <li>Feature 1</li>
            <li>Feature 2</li>
        </ul>
        {d.showDetails:ifEQ(true):showEnd}
        <p>Thank you for using Carbone!</p>
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <p>Product: Carbone Pro</p>
        <p>Thank you for using Carbone!</p>
    </body>
</html>
```

#### hideBegin / hideEnd Example

The **greetings** section is deleted when the field `name` is `undefined/null/empty`.

```cdata
{
  "name": null
}
```

```ctemplate
<!DOCTYPE html>
<html>
    <body>
        <h2> Dashboard </h2>
        {d.name:ifEM:hideBegin}
        <strong style='margin: 0; font-size: 14px;'>
            Welcome {d.name}!
        </strong>
        {d.name:ifEM:hideBegin}
    </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
    <body>
        <h2> Dashboard </h2>
    </body>
</html>
```

## Pictures

Dynamic pictures allow you to inject images into your documents dynamically using data from your JSON input. For HTML templates, use the `<img>` tag with the **src** attribute pointing to one of the following:

-   **Absolute URLs** (e.g., `https&#58;//carbone.io/img/carbone-logo.svg`). Carbone does not support relative paths (e.g., `/images/logo.png` or `../assets/photo.jpg`).
-   **Data-URIs** (Base64 encoded, e.g., `data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...`).

```cdata
{
  "userProfile": {
    "name": "John Doe",
    "profilePictureUrl": "https://carbone.io/images/john-doe.jpg",
    "profilePictureBase64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD..."
  }
}
```

```ctemplate
<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>Welcome, {d.userProfile.name}!</h1>
    <div>
        <img src="{d.userProfile.profilePictureUrl}" alt="Profile Picture" style="width:200px; height:auto;"/>
    </div>
</body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>Welcome, John Doe!</h1>
    <div>
        <img src="data:image/jpg...IMAGE BASE 64" alt="Profile Picture" style="width:200px; height:auto;"/>
    </div>
</body>
</html>
```

To change the dimensions or scaling of an image, you can use HTML attributes or CSS styling for more control and responsiveness:

-   Set fixed dimensions directly in the `<img>` tag, e.g., `<img src="{d.imageUrl}" width="300" height="200" alt="Dynamic Image">`.
-   Use CSS for sizing and changing aspect ratios. Inline styles can be used directly within your HTML tags: `<img src="{d.imageUrl}" style="width:300px; height:auto;" alt="Dynamic Image">`.

## Charts

Use the `:chart` formatter to dynamically inject charts into HTML templates: embed a Carbone tag in the **src** attribute of an `<img>` tag.

```cdata
{
    "chartOptions": {
        "type": "echarts@v5a",
        "width": 600,
        "height": 400,
        "theme": null,
        "option": {
            "xAxis": {
                "type": "category",
                "data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
            },
            "yAxis": {
                "type": "value"
            },
            "series": [{
                "data": [150, 230, 224, 218, 135, 147, 260],
                "type": "line"
            }]
        }
    }
}
```

```ctemplate
<html>
    <body>
        <div class="chart-container">
            <h2 class="chart-title">Sales Overview</h2>
            <img src="{d.chartOptions:chart}" alt="Chart Image" width="600" height="400">
        </div>
    </body>
</html>
```

```cresult
<html>
    <body>
        <div class="chart-container">
            <h2 class="chart-title">Sales Overview</h2>
            <img src="data:image/svg+xml CHART BASE 64" alt="Chart Image" width="600" height="400">
        </div>
    </body>
</html>
```

In this example, the `{d.chartOptions:chart}` tag is replaced with an SVG image of the chart, which is dynamically generated based on the data provided in your dataset.

Key points:

-   **Supported ECharts Version:** Carbone supports echarts@v5 (default) and echarts@v5a (optimized for SVG compatibility).
-   **Styling:** Chart styling must be defined within the JSON dataset.
-   **No External Dependencies:** Chart configuration does not support external dependencies (e.g., JS scripts).
-   **Dimensions:** You can adjust the dimensions of the chart in two ways:
    -   Using the `<img>` Tag: Set the **width** and **height** attributes directly on the `<img>` tag to control the size of the chart as it appears in the HTML document.
    -   Using the EChart Configuration: Define the **width** and **height** within the EChart configuration object in your JSON dataset. This allows you to specify the dimensions programmatically.

## Colors

You can dynamically set or change CSS color properties in your HTML templates using Carbone tags. This is useful for theming, conditional styling, or personalization based on your dataset. Two Methods are available:

-   **Direct Injection**: Inject color values directly from your dataset into style attributes.
-   **Conditional Injection**: Use Carbone’s `:ifEQ`, `:show`, and `:elseShow` to conditionally apply colors.

```cdata
{
  "theme": "dark",
  "textColor": "#ffffff",
  "bgColor": "#1a1a1a",
  "alertColor": "#ff4444",
  "isUrgent": true
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <!-- Directly inject color values -->
    <div style="color: {d.textColor}; background-color: {d.bgColor}; padding: 10px;">
      This div uses colors from the dataset.
    </div>

    <!-- Conditionally apply colors -->
    <p style="color: {d.isUrgent:ifEQ(true):show(d.alertColor):elseShow('#333333')};">
      {d.isUrgent:ifEQ(true):show('Urgent message!'):elseShow('Normal message.')}
    </p>

    <!-- Apply colors based on a theme -->
    <button style="
      color: {d.theme:ifEQ('dark'):show('#ffffff'):elseShow('#000000')};
      background-color: {d.theme:ifEQ('dark'):show('#333333'):elseShow('#ffffff')};
      border: none;
      padding: 8px 16px;
    ">
      {d.theme:ifEQ('dark'):show('Dark Mode'):elseShow('Light Mode')}
    </button>
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <div style="color: #ffffff; background-color: #1a1a1a; padding: 10px;">
      This div uses colors from the dataset.
    </div>
    <p style="color: #ff4444;">
      Urgent message!
    </p>
    <button style="color: #ffffff; background-color: #333333; border: none; padding: 8px 16px;">
      Dark Mode
    </button>
  </body>
</html>
```

## Formatters

Carbone provides built-in formatters to transform data values directly inside your HTML templates. Formatters are chained to a Carbone tag using the `:` operator (e.g. `{d.value:formatterName(args)}`). The sections below cover the most commonly used formatters for dates, numbers, and currencies. For the full list of available formatters, see the [Formatters documentation](/documentation/design/formatters/overview.md).

### Date Formatting

Use the `:formatD()` formatter to display dates in any format. The pattern follows [Day.js](https://day.js.org/docs/en/display/format) tokens (e.g. `YYYY`, `MM`, `DD`, `dddd`).

```cdata
{
  "orderDate": "2025-03-15T14:30:00Z",
  "deliveryDate": "2025-04-01T00:00:00Z"
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <p>Order date: {d.orderDate:formatD('DD/MM/YYYY')}</p>
    <p>Delivery date: {d.deliveryDate:formatD('dddd DD MMMM YYYY')}</p>
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <p>Order date: 15/03/2025</p>
    <p>Delivery date: Tuesday 01 April 2025</p>
  </body>
</html>
```

For the full list of supported date patterns and options, see the [Date formatter documentation](/documentation/design/formatters/date.md).

### Number Formatting

Use the `:formatN()` formatter to control decimal precision and thousands separators. The output format automatically adapts to the `lang` option passed at render time.

```cdata
{
  "quantity": 1234567.891,
  "rating": 4.5
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <p>Quantity: {d.quantity:formatN(2)}</p>
    <p>Rating: {d.rating:formatN(1)}</p>
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <p>Quantity: 1,234,567.89</p>
    <p>Rating: 4.5</p>
  </body>
</html>
```

For the full list of options, see the [Number formatter documentation](/documentation/design/formatters/number.md).

### Currency Formatting

Use the `:formatC()` formatter to display monetary values with the correct currency symbol, decimal precision, and locale-aware formatting.

```cdata
{
  "price": 4999.99,
  "discount": 250
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <p>Price: {d.price:formatC(2, 'EUR')}</p>
    <p>Discount: {d.discount:formatC(2, 'USD')}</p>
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <p>Price: €4,999.99</p>
    <p>Discount: $250.00</p>
  </body>
</html>
```

For the full list of supported currencies and options, see the [Currency formatter documentation](/documentation/design/formatters/currency.md).

## Barcodes

Carbone supports barcode generation in HTML templates using the `:barcode` formatter. This allows you to inject barcodes as SVG images directly into your documents. How It Works:

1.  Use the `:barcode` formatter in an  tag's src attribute.
2.  Specify the barcode type (e.g., `qrcode`, `code128`, `ean13`). [See the full list of supported barcode types here.](/documentation/design/advanced-features/barcode.md#barcodes-as-image-supported-barcodes)
3.  Optional - Adjust dimensions either via the `<img>` tag attributes or within the formatter.

```cdata
{
  "productCode": "123456789",
  "qrData": "https://example.com/product/123456789"
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <!-- QR Code (dimensions set via img tag) -->
    <img src="{d.qrData:barcode(qrcode)}" alt="QR Code" width="200" height="200">

    <!-- EAN-13 Barcode (dimensions set via formatter) -->
    <img src="{d.productCode:barcode(ean13, 'width:200;height:100')}" alt="EAN-13 Barcode">
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <!-- QR Code -->
    <img src="data:image/svg+xml;base64,...[SVG_DATA]..." alt="QR Code" width="200" height="200">

    <!-- EAN-13 Barcode -->
    <img src="data:image/svg+xml;base64,...[SVG_DATA]..." alt="EAN-13 Barcode">
  </body>
</html>
```

## Hyperlinks

You can dynamically set the **href** attribute of <a> tags using Carbone tags. This is useful for generating links based on your dataset, such as personalized URLs.

```cdata
{
    "documentationUrl": "https://carbone.io/documentation/",
    "isLoggedIn": true,
    "profileUrl": "https://account.carbone.io/",
    "defaultUrl": "https://account.carbone.io/login",
    "product": {
        "name": "Carbone Cloud",
        "url": "https://carbone.io/pricing.html"
    }
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <!-- Directly inject a URL -->
    <a href="{d.documentationUrl}">Read the Docs</a>

    <!-- Conditionally set a URL -->
    <a href="{d.isLoggedIn:ifEQ(true):show(d.profileUrl):elseShow(d.defaultUrl)}">
      {d.isLoggedIn:ifEQ(true):show('Your Profile'):elseShow('Login')}
    </a>

    <!-- Inject a URL from a nested object -->
    <a href="{d.product.url}">Learn about {d.product.name}</a>

    <!-- Combine static and dynamic paths -->
    <a href="/docs/{d.product.name:lowerCase():replace(' ', '-')}">Documentation</a>
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <a href="https://carbone.io/documentation/">Read the Docs</a>
    <a href="https://account.carbone.io/">Your Profile</a>
    <a href="https://carbone.io/pricing.html">Learn about Carbone Cloud</a>
    <a href="/docs/carbone-cloud">Documentation</a>
  </body>
</html>
```

## Inject HTML in HTML

Carbone allows you to inject raw HTML strings from your dataset into your templates using the **:html** formatter. Without it, `<` and `>` characters are automatically encoded as `&lt;` and `&gt;`, which prevents the HTML from being rendered as markup.

```cdata
{
  "welcomeMessage": "<strong>Welcome!</strong> Here’s your dashboard.",
  "alertMessage": "<div class='alert'>⚠️ Maintenance at 2 AM UTC.</div>",
  "isAdmin": true,
  "adminPanel": "<a href='/admin'>Go to Admin Panel</a>",
  "userPanel": "<a href='/profile'>Go to Profile</a>"
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <!-- Without :html (HTML is escaped) -->
    <p>{d.welcomeMessage}</p>

    <!-- With :html (HTML is rendered) -->
    <p>{d.welcomeMessage:html}</p>

    <!-- Conditional HTML injection -->
    {d.isAdmin:ifEQ(true):show(d.adminPanel):elseShow(d.userPanel):html}
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <!-- Escaped HTML (displayed as text) -->
    <p>&lt;strong&gt;Welcome!&lt;/strong&gt; Here’s your dashboard.</p>

    <!-- Rendered HTML -->
    <p><strong>Welcome!</strong> Here’s your dashboard.</p>

    <!-- Conditionally injected HTML -->
    <a href='/admin'>Go to Admin Panel</a>
  </body>
</html>
```

## Page Breaks

To insert a page break in PDFs generated from HTML templates, first define the following CSS class in your template’s `<style>` section:

```css
.page-break {
  page-break-before: always;
  break-before: page;
}
```

Then, insert a `<div>` or `<p>` with the **page-break** class where you want the new page to start:

```html
<div>Page 1 content</div>
<p class="page-break"></p>
<div>Page 2 content</div>
```

**Note:** It works only for PDF output (ignored in HTML).

## Table of Content

A Table of Contents (TOC) helps readers navigate long documents by providing links to sections and subsections. You can use Carbone tags to populate the TOC and headings using standard HTML elements and internal links. To create a TOC, follow these steps:

1.  **Define the TOC Structure:** Use an unordered list (`<ul>`) to structure the TOC. Each list item (`<li>`) should contain a link (`<a>`) to a section in your document. The `href` attribute of each link should point to the `id` of the corresponding heading.
2.  **Add Headings with IDs:** For each section or subsection in your document, add an `id` attribute to the heading (e.g., `<h1>`, `<h2>`). The TOC links will use these IDs to navigate to the correct section.
3.  **Style the TOC (Optional)**: Use CSS to style the TOC container and links for better readability and visual appeal.

```cdata
{
  "sections": [
    {
      "id": 1,
      "title": "First Point Header",
      "subSections": [
        {
          "id": 11,
          "title": "First Sub Point 1",
          "content": "This is the content for the first sub-point. You can add as much detail as needed here."
        },
        {
          "id": 12,
          "title": "First Sub Point 2",
          "content": "This is the content for the second sub-point. More details or paragraphs can go here."
        }
      ]
    },
    {
      "id": 2,
      "title": "Second Point Header",
      "subSections": []
    },
    {
      "id": 3,
      "title": "Third Point Header",
      "subSections": []
    }
  ]
}
```

```ctemplate
<!DOCTYPE html>
<html>
<head>
    <title>TOC Example</title>
    <style>
        .page-break {
            page-break-before: always;
            break-before: page;
        }
    </style>
</head>
<body>
    <div id="toc_container">
        <p class="toc_title">Contents</p>
        <ul class="toc_list">
            {d.sections[i]}
            <li>
                <a href="#section_{d.sections[i].id}">{d.sections[i].title}</a>
                <ul>
                    <li><a href="#subSection_{d.sections[i].subSections[i].id}">{d.sections[i].subSections[i].title}</a>
                    </li>
                </ul>
                {d.sections[i].subSections[i+1]}
            </li>
            {d.sections[i+1]}
        </ul>
    </div>
    <div>
        {d.sections[i]}
        <p class="page-break"></p>
        <h1 id="section_{d.sections[i].id}">{d.sections[i].title}</h1>
        <h2 id="subSection_{d.sections[i].subSections[i].id}">{d.sections[i].subSections[i].title}</h2>
        <p>{d.sections[i].subSections[i].content}</p>
        {d.sections[i+1].subSections[i+1]}
    </div>
</body>
```

```cresult
<!DOCTYPE html>
<html>
<head>
    <title>TOC Example</title>
    <style>
        .page-break {
            page-break-before: always;
            break-before: page;
        }
    </style>
</head>
<body>
    <div id="toc_container">
        <p class="toc_title">Contents</p>
        <ul class="toc_list">
            <li>
                <a href="#section_1">First Point Header</a>
                <ul>
                    <li><a href="#subSection_11">First Sub Point 1</a>
                    </li>
                </ul>
                <ul>
                    <li><a href="#subSection_12">First Sub Point 2</a>
                    </li>
                </ul>
            </li>
            <li>
                <a href="#section_2">Second Point Header</a>
            </li>
            <li>
                <a href="#section_3">Third Point Header</a>
            </li>
        </ul>
    </div>
    <div>
        <p class="page-break"></p>
        <h1 id="section_1">First Point Header</h1>
        <h2 id="subSection_11">First Sub Point 1</h2>
        <p>This is the content for the first sub-point. You can add as much detail as needed here.</p>
        <h2 id="subSection_12">First Sub Point 2</h2>
        <p>This is the content for the second sub-point. More details or paragraphs can go here.</p>
        <p class="page-break"></p>
        <h1 id="section_2">Second Point Header</h1>
        <p class="page-break"></p>
        <h1 id="section_3">Third Point Header</h1> 
    </div>
</body>
```

## Page Formats

By default, Carbone generates a PDF document in the `A4` format. Insert the `<carbone-pdf-options>` custom element in the HTML template, and use the **paper-size** option to specify standard page sizes:

| Value | Description |
| --- | --- |
| `A0`, `A1`, `A2`, `A3`, `A4`, `A5`, `A6` | ISO A-series paper sizes |
| `Letter` | US Letter (8.5 × 11 in) |
| `Legal` | US Legal (8.5 × 14 in) |
| `Tabloid` | Tabloid (11 × 17 in) |
| `Ledger` | Ledger (17 × 11 in) |

**Note:** Additional [PDF Conversion options](#pdf-conversion-options) (margins, scaling, headers/footers, etc.) are available.

### Example

This sets the PDF to A3 size in landscape orientation.

```html
<carbone-pdf-options paper-size="A3" landscape="true" />
```

## Headers Footers

Carbone allows you to define custom headers and footers for PDF documents generated from HTML templates.

**Custom Elements**

-   `<carbone-pdf-header>`: Defines the header for every page (e.g. [header example](#headers-footers-custom-header-example)).
-   `<carbone-pdf-footer>`: Defines the footer for every page (e.g. [footer example](#headers-footers-custom-footer-example)).

**Key Features**

-   Include static text, dynamic content (using Carbone tags), and page numbers.
-   For page numbering, use:
    -   `<span class="pageNumber"></span>` for the current page.
    -   `<span class="totalPages"></span>` for the total pages.
-   Insert only Inline CSS because external stylesheets are not supported.
-   Headers and footers are PDF-only and are ignored in HTML output.
-   Elements render in PDF even if hidden or commented out in the HTML template.

**Limitation**

The same header/footer applies to every page: A `<carbone-pdf-header>` or `<carbone-pdf-footer>` cannot be turned on or off, or changed, on a specific page. For example, hiding the page number on the cover page with a CSS rule like `.pageNumber:first-child { display: none; }` will not work — the rule is silently ignored. This is a known limitation we are working on. In the meantime, two workarounds are available:

1.  **Split the document into two HTML templates.** Put the cover page in one template (without `<carbone-pdf-footer>`) and the body of the document in a second template (with `<carbone-pdf-footer>`). Use the [`:appendTemplate`](/documentation/design/advanced-features/file-operations.md#appendtemplate-templateorversionid-position) formatter to concatenate them into one. The drawback is that page numbering restarts at 1 on the second template, as the two PDFs are merged after rendering.
2.  **Use a DOCX, PPTX, or XLSX template instead.** Office formats natively support different headers and footers on the first page, on odd/even pages, or per section. A single template can have a cover page without a footer and the rest of the document with one — no template-splitting or PDF concatenation required.

### Custom Header Example

This example demonstrates a custom PDF header with dynamic content:

-   Displays a centered title (Company Report | Q3 Financials).
-   Shows the current date (Generated: 2025-10-17) using Carbone's built-in `{c.now}` chained with `:formatD('YYYY-MM-DD')` to format dates.
-   Includes the user's name (User: John Doe) from the dataset.

```cdata
{
  "reportTitle": "Q3 Financials",
  "user": { "name": "John Doe" }
}
```

```ctemplate
<carbone-pdf-header>
  <div style="font-size: 12px; text-align: center; width: 100%;">
    <span>Company Report | {d.reportTitle}</span>
    <span>Generated: {c.now:formatD('YYYY-MM-DD')}</span>
    <span>User: {d.user.name}</span>
  </div>
</carbone-pdf-header>
```

```cresult
<carbone-pdf-header>
  <div style="font-size: 12px; text-align: center; width: 100%;">
    <span>Company Report | Q3 Financials</span>
    <span>Generated: 2025-10-17</span>
    <span>User: John Doe</span>
  </div>
</carbone-pdf-header>
```

### Custom Footer Example

This example demonstrates a custom PDF footer with:

-   Page numbering (e.g., "Page 5 of 10") using `<span class="pageNumber"></span>` and `<span class="totalPages"></span>`.
-   Dynamic copyright text injected from the dataset (`d.year` and `d.company`).

```cdata
{
  "year": "2025",
  "company": "Acme Inc"
}
```

```ctemplate
<carbone-pdf-footer>
  <div style="font-size: 10px; text-align: center; width: 100%;">
    Page <span class="pageNumber"></span> of <span class="totalPages"></span>
  </div>
  <div style="font-size: 9px; text-align: right;">
    © {d.year} {d.company}. All rights reserved.
  </div>
</carbone-pdf-footer>
```

```cresult
<carbone-pdf-footer>
  <div style="font-size: 10px; text-align: center; width: 100%;">
    Page 5 of 10
  </div>
  <div style="font-size: 9px; text-align: right;">
    © 2025 Acme Inc. All rights reserved.
  </div>
</carbone-pdf-footer>
```

## Convert HTML to PDF

> ℹ️ **Note:** **Using Carbone with AI-generated content:** LLMs like ChatGPT, Claude, and Gemini can generate HTML templates. A typical workflow is: prompt your LLM to produce a structured HTML document → inject dynamic fields with Carbone tags (e.g. `{d.clientName}`, `{d.date}`) → call the Carbone API with your JSON data to render the final PDF. Ideal for automated invoices, reports, contracts, and certificates.

To generate a PDF from an HTML template using the Carbone API, follow this workflow:

1.  **Prepare Two Files**: Create an **HTML Template** (with Carbone tags e.g. `{d.name}`) and a **JSON dataset** to populate the template.
2.  **Generate a PDF**, you have three options:
    -   Use the official Carbone integrations for platforms like N8N, Make, Zapier, Airtable, or Bubble.
    -   **Or**, use one of the Carbone SDKs (Node.js, Go, Python, Java, JavaScript, or PHP).
    -   **Or**, send a `POST /render/template` HTTP request to the [Carbone API](/documentation/developer/http-api/introduction.md). Here’s an example using curl:

```bash
curl --location --request POST 'https://api.carbone.io/render/template?download=true' \
  --header 'carbone-version: 5' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer API_TOKEN' \
  --data-raw "{
    \"data\": {},
    \"template\": \"$(base64 -i webpage.html)\",
    \"convertTo\": \"pdf\",
    \"converter\": \"C\"
  }" \
  --output result.pdf
```

> 💡 **Linux users**: replace `base64 -i webpage.html` with `base64 -w 0 webpage.html`

**Key Parameters**

| Parameter | Description |
| --- | --- |
| `template` | Your HTML template, encoded as a Base64 string. |
| `data` | JSON dataset used to populate the template. |
| `convertTo` | Must be set to "pdf". |
| `converter` | Must be set to `"C"` to use the Chromium rendering engine; output matches what you see in Chrome. |
| `?download=true` | Query parameter to download the document as a stream. |
| `Authorization` | Header required for the Carbone Cloud API. [Get your API key here.](https://account.carbone.io/) |

For more details, refer to the [Carbone API Documentation](/documentation/developer/http-api/introduction.md).

## PDF Conversion Options

Customize the PDF document by inserting the `<carbone-pdf-options/>` element in the HTML template, and provide [options](#pdf-conversion-options) as attributes.

### Example

This configures the PDF to use A3 size, landscape orientation, a 2.5-inch bottom margin, and disables background printing.

```html
<carbone-pdf-options
  paper-size="A3"
  landscape="true"
  margin-bottom="2.5"
  print-background="false"
/>
```

### Available Options

| Option | Type | Description |
| --- | --- | --- |
| `landscape` | boolean | Paper orientation. Defaults to `false`. |
| `display-header-footer` | boolean | Display header and footer. Defaults to `false`, or `true` if header/footer is provided |
| `print-background` | boolean | Print background graphics. Defaults to `true`. |
| `scale` | number | Scale of the webpage rendering. Defaults to `1`. |
| `paper-size` | string | Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6 (Default: A4) |
| `paper-width` | number | Overwrite Paper width in inches. Defaults to `8.27` inches (Default: A4) |
| `paper-height` | number | Overwrite Paper height in inches. Defaults to `11.7` inches (Default: A4) |
| `margin-top` | number | Overwrite Top margin in inches. Defaults to `0.5` inches. |
| `margin-bottom` | number | Overwrite Bottom margin in inches. Defaults to `0.5` inches. |
| `margin-left` | number | Overwrite Left margin in inches. Defaults to `0.5` inches. |
| `margin-right` | number | Overwrite Right margin in inches. Defaults to `0.5` inches. |
| `page-ranges` | string | Paper ranges to print, e.g., `1-5, 8, 11-13`. Pages are printed in document order. No page appears more than once. Empty string prints entire document. Invalid ranges are ignored. An error is thrown if the start page is greater than the end page. |
| `prefer-css-page-size` | boolean | Prefer CSS-defined page size. Defaults to `false`, in which case the content will be scaled to fit the paper size |
| `generate-tagged-pdf` | boolean | Generate a tagged PDF/UA for accessibility. Enables semantic structure for screen readers. Defaults to `false`. |

## Translations and i18n

Carbone can generate documents in any language from a single HTML template. Instead of maintaining one template per language, you define a **localization dictionary** and pass a `lang` option at render time — Carbone automatically substitutes all translated strings.

Two mechanisms are available:

-   **`{t( )}`** for static text in the template (labels, headings, fixed strings).
-   **`:t` formatter** for dynamic text values coming from your JSON dataset (translated via a dictionary key).

Locale-aware formatters such as `:formatD()`, `:formatN()`, and `:formatC()` automatically adapt their output to the target language as well.

```cdata
{
  "data"         : { "tool": "key1", "protection": "key2" },
  "convertTo"    : "pdf",
  "lang"         : "en-us",
  "translations" : {
    "fr-fr" : {
      "Invoice"    : "Facture",
      "Date"       : "Date",
      "key1"       : "Tournevis",
      "key2"       : "Gants"
    },
    "en-us" : {
      "Invoice"    : "Invoice",
      "Date"       : "Date",
      "key1"       : "Screwdrivers",
      "key2"       : "Gloves"
    }
  }
}
```

```ctemplate
<!DOCTYPE html>
<html>
  <body>
    <h1>{t(Invoice)}</h1>
    <p>{t(Date)}: {d.date:formatD('DD/MM/YYYY')}</p>
    <table>
      <thead>
        <tr><th>Item</th><th>Quantity</th></tr>
      </thead>
      <tbody>
        <tr><td>{d.tool:t}</td><td>1</td></tr>
        <tr><td>{d.protection:t}</td><td>2</td></tr>
      </tbody>
    </table>
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
  <body>
    <h1>Invoice</h1>
    <p>Date: 15/03/2025</p>
    <table>
      <thead>
        <tr><th>Item</th><th>Quantity</th></tr>
      </thead>
      <tbody>
        <tr><td>Screwdrivers</td><td>1</td></tr>
        <tr><td>Gloves</td><td>2</td></tr>
      </tbody>
    </table>
  </body>
</html>
```

> ℹ️ **Note:** The localization dictionary can be passed directly in the API request body (`translations` field). For the complete reference including CLI tools and dictionary generation, see the [Translations i18n documentation](/documentation/design/advanced-features/translations-i18n.md).

## Comments

Carbone processes all text in your template, including HTML comments (`<!-- -->`). If you include Carbone tags (e.g., `{d.user.name}`) inside comments, they will be evaluated and replaced with data.

**Best Practices:**

-   **Documentation and Notes**: If you must include Carbone tags in comments for taking notes, you can "escape" them by breaking the syntax so Carbone won’t interpret them: `<!-- Note: Use d.user.name (escaped) for the username -->`.
-   **Dynamic Comments**: If a whole HTML comment is injected, you must use the `:html` at the end of the Carbone tag (as described in the [documentation above](#inject-html-in-html)).

```cdata
{
  "showComment": true,
  "user": {
    "name": "John"
  },
  "comment": "<!-- Injected -->"
}
```

```ctemplate
<!DOCTYPE html>
<html>
<head>
  <title>User Profile</title>
</head>
  <body>
      <!-- Printing Carbone Tags within Comments: -->
      <!-- {d.user.name} -->

      <!-- Inject a whole comment section coming from the JSON data-set, using the <code>:html</code> formatter: -->
      {d.showComment:ifEQ(true):show(.comment):elseShow(''):html}
  </body>
</html>
```

```cresult
<!DOCTYPE html>
<html>
<head>
  <title>User Profile</title>
</head>
  <body>
      <!-- Printing Carbone Tags within Comments: -->
      <!-- John -->

      <!-- Inject a whole comment section coming from the JSON data-set, using the <code>:html</code> formatter: -->
      <!-- Injected -->
  </body>
</html>
```

## Related topics

- [PDF templates](/documentation/design/template-formats/pdf.md)
- [Markdown templates](/documentation/design/template-formats/markdown.md)
