Quickstart

Which Carbone service should I use to generate reports?

  • Carbone JS: Manage Carbone on your infrastructure; it provides only Community Features.
  • Carbone Cloud API: Secure, fast, and high available REST API to request Carbone from anywhere; It provides all Enterprise Features and priority support.
  • Carbone Cloud Studio: Web interface to design, preview and manage templates; It provides all Enterprise Features and priority support.
  • Carbone On-premise: Manage Carbone on your infrastructure; it provides all Enterprise features and priority support.

Quickstart Carbone JS

CarboneJS is an open source document generator, it provides:

  • The NodeJS engine generates any documents from a template and a JSON data-set.
  • Community features: Text replacement, loops, PDF generations, data formatting, and more! It does not contain Enterprise features*.
  • All export formats: DOCX, PDF, XLSX, ODS, ODG, XML, HTML, and more!
  • Apache License: The source code is open source on Github.
  • Support: Create an issue on Github
  • Tutorial: Access the CarboneJS quickstart.

* Enterprise Features (dynamic images, charts, colors, et cetera...): are available through the Carbone Cloud API or Carbone On-premise.

Minimum Requirements

  • NodeJS 8.x+
  • Runs on OSX, Linux, and Docker Containers
  • Libre Office is required to generate PDFs. Without LibreOffice, Carbone can still generate other formats, with the constraint that the export file type must be the template type (e.g., Docx to Docx, Xlsx to Xlsx, Pptx to Pptx, Odt to Odt, etc).

Carbone JS Setup

1 - Install

$ yarn add carbone
// OR
$ npm install carbone

2 - Copy-paste the following code into a JavaScript file, and execute it with NodeJS.

const fs = require('fs');
const carbone = require('carbone');

// Data to inject
var data = {
  firstname : 'John',
  lastname : 'Doe'
};

// Generate a report using the sample template provided by carbone module
// This LibreOffice template contains "Hello {d.firstname} {d.lastname} !"
// Of course, you can create your own templates!
carbone.render('./node_modules/carbone/examples/simple.odt', data, function(err, result){
  if (err) {
    return console.log(err);
  }
  // write the result
  fs.writeFileSync('result.odt', result);
});

Et voilà, you know how to use Carbone JS in your application, the next step is to learn how to build a template.

You can read the Carbone JS API documentation if you want to know more about the API.

Here is more Carbone rendering examples:

If you want a ready-to-use High Availability REST API to render reports, we recommend you to use the Carbone Cloud API service.

Quickstart Carbone Cloud API

Carbone Cloud API is a secure, fast, high-availability REST API serving Carbone anywhere in the world.
Generate documents through HTTP requests from any applications, services, or languages.
The Cloud API provides community and enterprise features.

Requirements

  • Create a Carbone Cloud account or sign-in: https://account.carbone.io/
  • For testing purposes, use a test API key: Generate unlimited watermarked PDFs for free (Only PDF file format).
  • For production purposes, use a production API key: Generate any document formats without a watermark. The free Sandbox Plan plan is available, giving you 100 credits per month. If you need more credits, you can upgrade your subscription.

1 - Design a test template

Create an ODT or DOCX document with your favorite text editor. Then write tags, which are placeholders used by Carbone to inject data, such as

Name: {d.firstname} {d.lastname}

2 - Upload a template

Upload a template into your personal storage space with one of the two solutions:

  • Programmatically using Carbone Cloud API: add the template with a POST render.carbone.io/template. Join the API token and version into the header { "Authorization": "YOU_API_TOKEN", "carbone-version" : "4" }. The request will return a unique template ID. The template is stored for an unlimited time.
  • Manully using Carbone Cloud Studio: add a new template by clicking on the menu Report > Select New > Add a new report > upload the file. You will find the unique template ID on the top right "info" icon. Your template is accessible on Carbone Cloud services (API and Studio). The template is stored for an unlimited time.

3 - Generate a report from the template ID

Use the template ID, a unique file identifier, to generate a report with the request POST https://render.carbone.io/render/:templateID.

The body must contain the report type to generate "convertTo": "pdf", and the data object that includes all your data, for instance:

{
  "convertTo": "pdf",
  "data": {
    "firstname": "John",
    "lastname": "Wick"
  }
}

The request will return a render ID used to download the report.

4 - Download the document

Download the PDF document with GET https://render.carbone.io/render/:renderID

The document is available only for one hour and is deleted automatically when:

  • The document is downloaded. To download another document, you must generate a new document.
  • After one hour.

Voilà, your first document is generated! 🥳

Next steps:

We do not log nor store the data sent to Carbone Cloud API or Carbone Cloud Studio.

Quickstart Carbone Cloud Studio

Carbone studio is a Web interface used to:

  • Design and preview reports
  • Manage your templates: usable directly into Cloud API or Cloud Studio
  • Version and tag your templates

Carbone Studio is available at: https://studio.carbone.io.

Tutorial to design a template and preview on Carbone Studio

Read our tutorial on the Carbone help center.

Live reloading

The live reloading allows designing and editing templates from a text editor, and Carbone Studio will preview the report automatically. Upload a template once, and the document previews on Carbone studio as soon as the JSON/template is updated.

This feature only works on navigators based on Chromium:

  • Google Chrome
  • Brave
  • Edge
  • Opera
  • Vivaldi

The live reload is not available on Firefox because the browser does not support the Native File System API for now.

Quickstart Carbone On-premise

Carbone On-premise includes all Enterprise Features, an API, and a Stateless Studio. The API is the same as the Carbone Cloud API. Reach the team via chat/email, and we will send you the documentation for integrating Carbone On-premise on your infrastructure.

Building a template

This section aims to teach you how we built the templating engine and how to use it. If you just want to learn how to design a template, you can jump to the next section.

Philosophy

Carbone is a powerfull low-code templating engine which works in all XML-based document (docx, odt, ods, xml, ...)

It can also convert your report to reliable PDFs and any other document formats.

It uses a simple declarative programming language, which is not intrusive. You don't need to insert control flow code in your document such as if or for-loop tags.

In computer science, declarative programming is a programming paradigm, a style of building the structure and elements of computer programs, that expresses the logic of a computation without describing its control flow.[1] Many languages applying this style attempt to minimize or eliminate side effects by describing what the program should accomplish in terms of the problem domain, rather than describing how to go about accomplishing it as a sequence of the programming language primitives[2] (the how being left up to the language's implementation). This is in contrast with imperative programming, in which algorithms are implemented in terms of explicit steps. source

This way of programming provides more flexibility for travelling data. You can transform your original JSON array directly in the template. See examples:

Moreover, the system is XML-agnostic. It means the template engine works on any valid XML-based documents, not only XML-documents created by Microsoft Office™, LibreOffice™ or OpenOffice™. Carbone finds automatically repetition patterns in your template.

How Carbone works?

Carbone finds all tags {} in your document (xlsx, odt, docx, ...) and replaces those placeholders by data. According to the syntax of your tags, you can make a lot of operations including

  • Replacing a field, such as {d.firstname} {d.lastname}
  • Formatting data, such as {d.number:formatN} to format a number, many formatters are available!
  • Repeating a document portion (a table row or anything else), such as {d.list[i].total} {d.list[i+1]}
  • Looping on unlimited nested arrays
  • Conditionally displaying a data based on a test expression, such as {d.value:ifEQ(true):show('The value is true.'):elseShow('The value is false.')}

The data path is defined based on the JSON dataset, and it is like using a JSON Array or Object in Javascript. For instance, if a JSON dataset is { "name": "John"} the following tag can be inserted into a template: {d.name}, details:

  • Starting tag {
  • The root of the data is d
  • The data attribute name is .name: it can be infinite object depth, such as .object.list[i].value
  • Ending tag }

Combined with features of LibreOffice™ or MS Office™, you can easily create documents with:

  • Graphics
  • Headers, footers
  • Automatically repeated table header across pages
  • Insert computed field
  • Page count
  • ...

Schema to summarize how Carbone works:

Carbone workflow

Design your first template

Designing a template is mostly 95% knowing your text editor, and 5% knowing the Carbone language (the following list).

A 2mn tutorial is available to create your first template. As soon as you understand all the basics, you can learn how to insert specific elements dynamically:

Everything about the Carbone language is available on:

Finally, If you are stuck, feel free to reach the support team on the chat!

Notes: features name followed by the icon "" means it is an Enterprise Feature available only for Carbone Cloud services (Studio/API) and Carbone On-premise.

Supported files and features list

Carbone supports a lot of document types as templates. The team and the community are working on supporting new document formats and improving features, take a look at our roadmap!

Input Template Formats XML HTML ODT ODS ODP DOCX XLSX PPTX ODG IDML
Output File Formats xml html pdf odt doc
docx pdf
txt jpg
png epub
ods xlsx
xls csv
pdf txt
odp ppt
pptx pdf
jpg png
odt doc
docx pdf
txt jpg
png epub
ods xlsx
xls csv
pdf txt
odp ppt
pptx pdf
jpg png
pdf
idml
Community Features
Substitutions
Repetitions
Formatters
Translations
Enterprise Features
Conditions
Aggregators
Colors
Pictures
Charts
Barcodes
Hyperlinks
HTML
Forms

Caption

  • Working
  • Issues (We are fixing it)
  • Coming soon
  • Not available

If you're facing any issues, search on Github for a similar case to ensure it doesn't already exist. Otherwise, create an issue to help us.

Quotations marks

Carbone language uses single quotes ' (also known as Straight quotes) to delimit text into formatters's arguments, such as: {d.value:ifEQ(true):show('This is true!')}. All other quotes won't be considered: double quotes, magic quotes, or chevrons.

Text editors may replace automatically "single quotes" with "smart quotes" that are not supported by Carbone. Solution:

  • MS word: On the File tab > click on Options > Click on Proofing > click on AutoCorrect Options. In the AutoCorrect dialog box, do the following: Click the AutoFormat As You Type tab and select or clear the "Straight quotes" with “smart quotes” check box.
  • LibreOffice: click on Tools > AutoCorrect > Localized Options. Then make sure the "replace" checkboxes for single and double quote are disabled.

Fonts

To use a specific Font on a report, based on each services:

  • Carbone JS: Install the font locally (laptop/container), restart LibreOffice, and apply the font to the template.
  • Carbone Cloud services (API & Studio): Google Fonts are pre-installed on Carbone Cloud servers. If the font is not rendering into documents, verify if the font is available on Google Fonts. New fonts can be installed only with a free Commercial License.
  • Carbone On-premise: Install the font locally (server/container), restart LibreOffice, and apply the font into the template.

Substitutions

COMMUNITY FEATURE

Each part of this documentation shows exactly what happens if you send Data (JSON Object) and a Template (handmade docx, odt, ...) to Carbone.

Basic

Carbone tags are placeholders {d.} within the template, then they are substituted for data from your JSON dataset. The value can be a string, a number, or a date.

Data

{
  "firstname": "John",
  "lastname": "Doe"
}

Template

Hello {d.firstname} {d.lastname} !

Result

Hello John Doe !

Accessing sub-objects

If a dataset contains sub-objects, Carbone can access them using the Dot Notation to go deeper in the object tree.

Data

{
  "firstname": "John",
  "type": {
    "id": 1,
    "name": "human"
  }
}

Template

{d.firstname} is a {d.type.name}

Result

John is a human

Accessing arrays

When data is an array of objects, you can access directly each object using the reserved word i, which represents the ith item of the array. Carbone uses zero-based arrays:

  • The first item of an array is [i=0] or [0].
  • The second item of an array is [i=1] or [1]
  • Et cetera...

An array is reachable using the Square Bracket Notation [].

Data

[
  { "movie": "Inception" },
  { "movie": "Matrix" },
  { "movie": "Back to the future" }
]

Template

My preferred movie is {d[i=1].movie}

Result

My preferred movie is Matrix

Array filters

Instead of using the reserved word i, Carbone accepts filters using attributes of objects.

If the filter returns many rows, Carbone keeps only the first occurrence.

Data

[
  { "name": "Inception", "year": 2010 },
  { "name": "Matrix", "year": 1999 },
  { "name": "BTTF", "year": 1985 }
]

Template

The movie of 1999 was {d[year=1999].name}

Result

The movie of 1999 was Matrix

Filter to the last element

If you want access to the last element of a list, assign a negative index [i=-1] can be used as a filter. If you want to get the second to last, you have to use [i=-2]. Here is an example:

[
  { "name": "Inception", "year": 2010 },
  { "name": "Matrix", "year": 1999 },
  { "name": "BTTF", "year": 1985 }
]

Template

The oldest movie was {d[i=-1].name}.

Result

The oldest movie was BTTF.

Filter and print parent

Access properties of the parent object with two points .. (or more) when you need to print a parent property using filters in nested arrays:

Data

{
  "country": "USA",
  "movies": [
    { "name": "Inception", "year": 2010 },
    { "name": "Matrix", "year": 1999 },
    { "name": "BTTF", "year": 1985 }
  ]
}

Template

{d.movies[year=1999]..country}

Result

USA

Multiple array filters

Multiple filters are accepted, even using a sub-object (depth-limited, a filter cannot go deeper in the object tree for the moment).

If the filter returns many rows, Carbone keeps only the first occurrence.

Data

[
  { "name": "Matrix"        , "year": 1999, "meta": { "type": "SF"    } },
  { "name": "The Green Mile", "year": 1999, "meta": { "type": "Drama" } }
]

Template

The movie of 1999 was {d[year=1999, meta.type='SF'].name}

Result

The movie of 1999 was Matrix

Alias

Aliases can be used to simplify the maintenance of your report avoiding code repetition or to insert tags somewhere in the document where special characters like square brackets are not allowed (worksheet names for example).

  • Write {#... = ...} to define an alias
  • Write {$...} to use this alias

Aliases can be defined anywhere in the document, at the end, or at the beginning. They are parsed and removed by Carbone before rendering.

Data

{
  "name": "Cars",
  "wheels": 4
}

Template

{#myAlias = d.wheels}

{d.name} need {$myAlias} wheels!

Result

Cars need 4 wheels!

Parametrized Alias

Aliases accept an unlimited number of parameters between parentheses, like a function in many languages. Each parameter must start by $.

Data

[
  { "name": "chicken", "weekday": 1 },
  { "name": "fish"   , "weekday": 2 }
]

Template

{#mealOf($weekday) = d[weekday = $weekday].name}

Tuesday, we eat {$mealOf(2)}.

Result

Tuesday, we eat fish.

Repetitions

COMMUNITY FEATURE

Carbone can repeat a section (rows, title, pages...) of the document.

We don't need to describe where the repetition starts and ends, we just need to design a "repetition example" in the template using the reserved key word i and i+1. Carbone will find automatically the pattern to repeat using the first row (i) as an example. The second row (i+1) is removed before rendering the result.

Simple array

In this example, we want to travel an array of cars.

Data

{
  "cars" : [
    {"brand" : "Lumeneo"},
    {"brand" : "Tesla"  },
    {"brand" : "Toyota" }
  ]
}

Template

Cars
{d.cars[i].brand}
{d.cars[i+1].brand}

Result

Cars
Lumeneo
Tesla
Toyota

Nested arrays

Carbone manages nested arrays (unlimited depth). Here is an example where a whole portion of a document is repeated.

Data

[
  {
    brand: "Toyota",
    models: [{ size: "Prius 2" }, { size: "Prius 3" }]
  },
  {
    brand: "Tesla",
    models: [{ size: "S" }, { size: "X" }]
  }
];

Template

{d[i].brand}

Models
{d[i].models[i].size}
{d[i].models[i+1].size }

{d[i+1].brand}

Result

Toyota

Models
Prius 2
Prius 3

Tesla

Models
S
X

As you may notice, it is useless to repeat completely the first paragraph twice in the template, only the title of the second paragraph is necessary to help Carbone detect where the repetition pattern of the main array ends {d.cars[i+1].brand}.

Bi-directional loop

COMMUNITY FEATURE v4.8.0+

The bidirectional loop performs iterations in 2 directions, creating additional columns and rows.

There are a few restrictions:

  • The JSON parent array must be used for rows, and the nested array must be used for columns. This limitation will be removed in Carbone v5.
  • Officialy supported in DOCX template. In addition, Word can automatically resize the table to fit the page width. Bidirectional loops with other template formats are still experimental. Please get in touch with us in the chat if necessary.

Here is a real life example of a bidirectional loop.

Data

{
  titles : [{ name: "Toyota" }, { name: "Tesla" }, { name: "Hopium" }],
  cars   : [
    { models : [ "S", "Prius 1", "Prototype" ] },
    { models : [ "3", "Prius 2", "" ]          },
    { models : [ "X", "Prius 3", "" ]          }
  ]
}

Template

{d.titles[i].name} {d.titles[i+1].name}
{d.cars[i].models[i]} {d.cars[i].models[i+1]}
{d.cars[i+1].models[i]}

Result

Toyota Tesla Lumeneo
Prius 1 S Prototype
Prius 2 3
Prius 3 X

Sorting

Carbone allows to use attributes of objects, instead of the reserved iterator i to iterate through arrays. It can be used to sort data directly in the template.

In this example, all cars are sorted by "power" in ascendant order (descendant order is not possible for the moment).

Data

{
  "cars" : [
    { "brand" : "Lumeneo" , "power" : 3 },
    { "brand" : "Tesla"   , "power" : 1 },
    { "brand" : "Toyota"  , "power" : 2 }
  ]
}

Template

Cars
{d.cars[power].brand}
{d.cars[power+1].brand}

Result

Cars
Tesla
Toyota
Lumeneo

Distinct items

A custom iterator can be used to select distinct rows according to the attribute's value.

Data

[
  { type: "car", brand: "Tesla" },
  { type: "plane", brand: "Airbus" },
  { type: "plane", brand: "Boeing" },
  { type: "car", brand: "Toyota" }
];

Template

Vehicles
{d[type].brand}
{d[type+1].brand}

Result

Vehicles
Tesla
Airbus

Iterate on multiple items

In the previous example, only distinct rows were kept. If we want to keep all rows, and sort them by type in ascendant order, we can add the reserved iterator i. Carbone accepts multiple iterators separated by a comma.

Data

[
  { type: "car", brand: "Tesla" },
  { type: "plane", brand: "Airbus" },
  { type: "plane", brand: "Boeing" },
  { type: "car", brand: "Toyota" }
];

Template

Cars
{d[type , i ].brand}
{d[type+1, i+1].brand}

Result

Vehicles
Tesla
Toyota
Airbus
Boeing

Filters

You can use conditional operators to filter arrays: > , < , >= , <= , =, !=.

filters example with numbers

Filters should be the same on the ith+1 tag and all other tags if you want to keep only matching rows. Use Alias to simplify report maintenance.

Data

[
  { name: "John", age: 20 },
  { name: "Eva", age: 18 },
  { name: "Bob", age: 25 },
  { name: "Charly", age: 30 }
];

Template

People
{d[i , age > 19, age < 30].name}
{d[i+1, age > 19, age < 30].name}

Result

People
John
Bob

filter example with strings

Filtering with string is also possible as the following example:

[
  { name: "Falcon 9", type: "rocket" },
  { name: "Model S", type: "car" },
  { name: "Model 3", type: "car" },
  { name: "Falcon Heavy", type: "rocket" }
];

Template

People
{d[i , type='rocket'].name}
{d[i+1, type='rocket'].name}

Result

People
Falcon 9
Falcon Heavy

filter with the iterator index

Filtering the loop index is also possible as the following example:

[
  { name: "Falcon 9" },
  { name: "Model S" },
  { name: "Model 3" },
  { name: "Falcon Heavy" }
];

Template

People
{d[i, i < 2].name}
{d[i+1, i < 2].name}

Result

People
Falcon 9
Model S

Iterate over objects

Iterate over objects if you need keys or values:

  • use .att to print the attribute
  • use .val to print the value

Iterating over object example

  {
    myObject : {
      paul : '10',
      jack : '20',
      bob  : '30'
    }
  }

Template

People name People age
{d.myObject[i].att} {d.myObject[i].val}
{d.myObject[i+1].att} {d.myObject[i+1].val}

Result

People name People age
paul 10
jack 20
bob 30

Access the loop iterator value

ENTERPRISE FEATURE NEWv4.0.0+

Access the iterator value when a list is printed into a document.
Example: In {d[i].cars[i].other.wheels[i].tire.subObject:add(.i):add(..i):add(...i)} The number of dots equals the number of previous i:

  • .i matches the index value of wheels[i]
  • ..i matches the index value of cars[i]
  • ...i matches the index value of d[i]

Formatters

COMMUNITY FEATURE

Formatters can be used to translate raw data into human readable text.

A formatter is applied to data using the separator :. Multiple formatters can be used one after another, each formatter input is the output of the previous one. Some formatters accept static or dynamic parameters.

Here is an example showing how to write "John" instead of "JOHN" using two chained formatters, and how to translate a raw ISO date into a human readable date.

Next chapters list all existing formatters by input data type.

Data

{
  "name"     : "JOHN",
  "birthday" : "2000-01-31"
}

Template

My name is {d.name:lowerCase:ucFirst}.

I was born on {d.birthday:convDate(YYYY-MM-DD, LL)}.

Result

My name is John.

I was born on Monday 31th 2000.

Dynamic parameters

Formatters accept dynamic variables if parameters start with a . and is not surounded by quotes.

Here is the dataset used for the following examples:

{
  "id" : 10,
  "qtyA" : 20,
  "subObject" : {
    "qtyB" : 5,
    "qtyC" : 3
  },
  "subArray" : [{
    "id" : 1000,
    "qtyE" : 3
  }]
}

To do mathematical operations:

{d.subObject.qtyB:add(.qtyC)} // 8 (5+3)

To insert parent attributes, two dots have to be used. To access to grandparents attributes, three dots are necessary, etc...

{d.subObject.qtyB:add(.qtyC):add(..qtyA)} // 28  (5+3+20)

To read parent objects and their children attributes (there is no limit in depth):

{d.subArray[i].qtyE:add(..subObject.qtyC)} // 6 (3+3)

It returns an error if the attribute does not exist.

{d.subArray[i].qtyE:add(..badAttr)} // [[C_ERROR]] badAttr not defined

It returns an error

{d.subObject.qtyB:add(..subArray[0].qtyE)} // [[C_ERROR]]

It returns an error

{d.subObject.qtyB:add(..subArray[i].qtyE)} // [[C_ERROR]] subArray[i] has no index

String manipulation

lowerCase( )

v0.12.5+

Lower case all letters

Examples

'My Car':lowerCase() // "my car"
'my car':lowerCase() // "my car"
null:lowerCase() // null
1203:lowerCase() // 1203

upperCase( )

v0.12.5+

Upper case all letters

Examples

'My Car':upperCase() // "MY CAR"
'my car':upperCase() // "MY CAR"
null:upperCase() // null
1203:upperCase() // 1203

ucFirst( )

v0.12.5+

Upper case first letter

Examples

'My Car':ucFirst() // "My Car"
'my car':ucFirst() // "My car"
null:ucFirst() // null
undefined:ucFirst() // undefined
1203:ucFirst() // 1203

ucWords( )

v0.12.5+

Upper case the first letter of all words

Examples

'my car':ucWords() // "My Car"
'My cAR':ucWords() // "My CAR"
null:ucWords() // null
undefined:ucWords() // undefined
1203:ucWords() // 1203

v0.13.0+

Always return the same message if called (sort of "catch all" formatter)

params desciption type
message text to print String

Examples

'My Car':print('hello!') // "hello!"
'my car':print('hello!') // "hello!"
null:print('hello!') // "hello!"
1203:print('hello!') // "hello!"

convEnum( type )

v0.13.0+

Convert user-defined enums to human readable values

User-defined enums must be passed in options of carbone.render.

params desciption type
type enum name passed in options of carbone.render(data, options) String

Examples

// with options = {
//   "enum": {
//     "ORDER_STATUS": [
//       "pending",
//       "sent",
//       "delivered"
//     ]
//   }
// }
0:convEnum('ORDER_STATUS') // "pending"
1:convEnum('ORDER_STATUS') // "sent"
5:convEnum('ORDER_STATUS') // 5
// with options = {
//   "enum": {
//     "YES_NO": {
//       "true": "Yes",
//       "false": "No"
//     }
//   }
// }
false:convEnum('YES_NO') // "No"
true:convEnum('YES_NO') // "Yes"
null:convEnum('YES_NO') // null
3:convEnum('UNKNOWN_ENUM') // 3

unaccent( )

v1.1.0+

Removes accents from text

Examples

'crème brulée':unaccent() // "creme brulee"
'CRÈME BRULÉE':unaccent() // "CREME BRULEE"
'être':unaccent() // "etre"
'éùïêèà':unaccent() // "euieea"

convCRLF( )

v4.1.0+

It renders carriage return \r\n and line feed \n into documents instead of printing them as a string.
Importante notes:

  • Feature supported for DOCX, PDF, ODT, and ODS files.
  • ODS supports is experimental for now, contact the support if you find issues.
  • Since v3.5.3, using the :convCRLF formatter before :html converts \n to <br> tags. Usage example: {d.content:convCRLF:html}

Examples

// with options = {
//   "extension": "odt"
// }
'my blue \n car':convCRLF() // "my blue <text:line-break/> car"
'my blue \r\n car':convCRLF() // "my blue <text:line-break/> car"
// with options = {
//   "extension": "docx"
// }
'my blue \n car':convCRLF() // "my blue </w:t><w:br/><w:t> car"
'my blue \r\n car':convCRLF() // "my blue </w:t><w:br/><w:t> car"

substr( begin, end, wordMode )

NEWv4.18.0+

Slice a string with a begin and an end.

params desciption type
begin Zero-based Index at which to begin extraction. Integer
end [optional] Zero-based index before which to end extraction Integer
wordMode [optional] If true, it never cuts words. In such a case: - end must be greater than begin and negative values cannot be used.
end - begin = maximum number of characters per line of text
- A word can only be truncated only if it does not fit in the line.
In this case, the word always starts at the beginning of a new line, just like in Word or LibreOffice
- The same line width (end - begin) must be used between successive calls of substr to print all the words of the text (no gaps). Ex:
{d.text(0 , 50 , true)} -> line 1 of 50 characters
{d.text(50 , 100, true)} -> line 2 of 50 characters
{d.text(100, 150, true)} -> line 3 of 50 characters
{d.text(150, 200, last)} -> line 4 of infinite characters
- last can be used instead of true to print the rest of the text, even if it is longer than the defined line width
Mixed

Examples

'foobar':substr(0, 3) // "foo"
'foobar':substr(1) // "oobar"
'foobar':substr(-2) // "ar"
'foobar':substr(2, -1) // "oba"
'abcd efg hijklm':substr(0, 11, true) // "abcd efg "
'abcd efg hijklm':substr(1, 11, true) // "abcd efg "

split( delimiter )

NEWv4.12.0+

Split a string using a delimiter

It can be used with arrayJoin('', 1, 2) to select one specific item of the generated array

params desciption type
delimiter The delimiter String

Examples

'abcdefc12':split('c') // ["ab","def","12"]
1222.1:split('.') // ["1222","1"]
'ab/cd/ef':split('/') // ["ab","cd","ef"]

padl( targetLength, padString )

NEWv3.0.0+

Pad the string from the start with another string

params desciption type
targetLength The length of the resulting string once the string has been padded. If the value is less than string length, then string is returned as-is. number
padString The string to pad the current str with. If padString is too long to stay within the targetLength, it will be truncated from the end. The default value is " " String

Examples

'abc':padl(10) // "       abc"
'abc':padl(10, 'foo') // "foofoofabc"
'abc':padl(6, '123465') // "123abc"
'abc':padl(8, '0') // "00000abc"
'abc':padl(1) // "abc"

padr( targetLength, padString )

NEWv3.0.0+

Pad the string from the end with another string

params desciption type
targetLength The length of the resulting string once the string has been padded. If the value is less than string length, then string is returned as-is. number
padString The string to pad the current str with. If padString is too long to stay within the targetLength, it will be truncated from the end. The default value is " " String

Examples

'abc':padr(10) // "abc       "
'abc':padr(10, 'foo') // "abcfoofoof"
'abc':padr(6, '123465') // "abc123"
'abc':padr(8, '0') // "abc00000"
'abc':padr(1) // "abc"

ellipsis( maximum )

NEWv4.12.0+

Add "..." if the text is too long

params desciption type
maximum number of characters to print. Integer

Examples

'abcdef':ellipsis(3) // "abc..."
'abcdef':ellipsis(6) // "abcdef"
'abcdef':ellipsis(10) // "abcdef"

prepend( textToPrepend )

NEWv4.12.0+

add a prefix to a text

params desciption type
textToPrepend text to prepend string

Examples

'abcdef':prepend('123') // "123abcdef"

append( textToAppend )

NEWv4.12.0+

Add a suffix to a text

params desciption type
textToAppend text to append string

Examples

'abcdef':append('123') // "abcdef123"

replace( oldText, newText )

NEWv4.12.0+

Replace a text based on a pattern

All matches of the pattern (first argument: oldText) is replaced by the replacement string (second argument: newText).
The pattern can only be a string.

params desciption type
oldText old text to replace string
newText new text string

Examples

'abcdef abcde':replace('cd', 'OK') // "abOKef abOKe"
'abcdef abcde':replace('cd') // "abef abe"
'abcdef abcde':replace('cd', null) // "abef abe"
'abcdef abcde':replace('cd', 1000) // "ab1000ef ab1000e"

len( )

v2.0.0+

Returns the length of a string or array.

Examples

'Hello World':len() // 11
'':len() // 0
[1,2,3,4,5]:len() // 5
[1,'Hello']:len() // 2

Number manipulation

Formatters can be chained with dynamic parameters to create complex operations, check out the example.

convCurr( target, source )

v1.2.0+

Convert from one currency to another

Exchange rates are included by default in Carbone but you can provide a new echange rate
for one report in options.currencyRates of Carbone.render or globally with Carbone.set

convCurr() without parameters converts automatically from options.currencySource to options.currencyTarget.

If `options.currencySource' is undefined, no conversion will be performed.

params desciption type
target [optional] convert to this currency ('EUR'). By default it equals options.currencyTarget String
source [optional] currency of source data ('USD'). By default it equals options.currencySource String

Examples

// with options = {
//   "currency": {
//     "source": "EUR",
//     "target": "USD",
//     "rates": {
//       "EUR": 1,
//       "USD": 2
//     }
//   }
// }
10:convCurr() // 20
1000:convCurr() // 2000
1000:convCurr('EUR') // 1000
1000:convCurr('USD') // 2000
1000:convCurr('USD', 'USD') // 1000

round( precision )

v1.2.0+

Round a number

Same as toFixed(2) but it rounds number correclty round(1.05, 1) = 1.1

params desciption type
precision number of decimal Number

Examples

10.05123:round(2) // 10.05
1.05:round(1) // 1.1

formatN( precision )

v1.2.0+

Format number according to the locale.
Applying a number of decimals depends on the report type:

  • For ODS/XLSX, the number of decimals has to be formatted based on the text editor.
  • For the other type of files, the number of decimals depends on the precision parameter passed to the formatter.
params desciption type
precision [optional] Number of decimals Number

Examples

// with options = {
//   "lang": "en-us"
// }
'10':formatN() // "10.000"
'1000.456':formatN() // "1,000.456"

formatC( precisionOrFormat, targetCurrencyCode )

v1.2.0+

params desciption type
precisionOrFormat [optional] Number of decimal, or specific format
- Integer : change default precision of the currency
- M : print Major currency name without the number
- L : prints number with currency symbol (by default)
- LL : prints number with Major currency name
Number
targetCurrencyCode [optional] target currency code (upper case). Ex: USD, EUR, ... It overwrites the global option options.currencyTarget String

Examples

// with options = {
//   "lang": "en-us",
//   "currency": {
//     "source": "EUR",
//     "target": "USD",
//     "rates": {
//       "EUR": 1,
//       "USD": 2
//     }
//   }
// }
'1000.456':formatC() // "$2,000.91"
'1000.456':formatC('M') // "dollars"
'1':formatC('M') // "dollar"
'1000':formatC('L') // "$2,000.00"
'1000':formatC('LL') // "2,000.00 dollars"
// with options = {
//   "lang": "fr-fr",
//   "currency": {
//     "source": "EUR",
//     "target": "USD",
//     "rates": {
//       "EUR": 1,
//       "USD": 2
//     }
//   }
// }
'1000.456':formatC() // "2 000,91 --TMPL-4-MARK--quot;
// with options = {
//   "lang": "fr-fr",
//   "currency": {
//     "source": "EUR",
//     "target": "EUR",
//     "rates": {
//       "EUR": 1,
//       "USD": 2
//     }
//   }
// }
'1000.456':formatC() // "1 000,46 €"

add( )

v1.2.0+

Add two numbers

Examples

1000.4:add(2) // 1002.4
'1000.4':add('2') // 1002.4

sub( )

v1.2.0+

Substract two numbers

Examples

1000.4:sub(2) // 998.4
'1000.4':sub('2') // 998.4

mul( )

v1.2.0+

Multiply two numbers

Examples

1000.4:mul(2) // 2000.8
'1000.4':mul('2') // 2000.8

div( )

v1.2.0+

Divide two numbers

Examples

1000.4:div(2) // 500.2
'1000.4':div('2') // 500.2

mod( value )

NEWv4.5.2+

Compute modulo

params desciption type
value Y Number

Examples

4:mod(2) // 0
3:mod(2) // 1

abs( )

NEWv4.12.0+

Get absolute value

Examples

-10:abs() // 10
-10.54:abs() // 10.54
10.54:abs() // 10.54
'-200':abs() // 200

int( )

UNRECOMMENDED FOR USEv1.0.0+

Converts a number to an INT

toEN( )

UNRECOMMENDED FOR USEv1.0.0+

Converts a number with English specifications (decimal separator is '.')

toFixed( )

UNRECOMMENDED FOR USEv1.0.0+

Converts a number into string, keeping only decimals

toFR( )

UNRECOMMENDED FOR USEv1.0.0+

Converts a number with French specifications (decimal separator is ',')

Array manipulation

:aggStr( separator )

ENTERPRISE FEATURE NEWv4.17.0+

The :aggStr combines values from a list, linking them with a separator to create a single string.

Arguments Description Type
separator (Optional) Separator between each value. The defautl separator is ,. string

Data

{
  "cars": [
    {"brand":"Tesla","qty":1,"sort":1},
    {"brand":"Ford","qty":4,"sort":4},
    {"brand":"Jeep","qty":3,"sort":3},
    {"brand":"GMC","qty":2,"sort":2},
    {"brand":"Rivian","qty":1,"sort":1},
    {"brand":"Chevrolet","qty":10,"sort":5}
  ]
}

Template

All car brands:
{d.cars[].brand:aggStr}
All car brands with a different separator:
{d.cars[].brand:aggStr(' - ')}
All car brands with a filter, the "qty" value must be above 3:
{d.cars[.qty > 3].brand:aggStr()}

Result

All car brands:
Tesla, Ford, Jeep, GMC, Rivian, Chevrolet
All car brands with a different separator:
Tesla - Ford - Jeep - GMC - Rivian - Chevrolet
All car brands with a filter, the "qty" value must be above 3:
Ford, Chevrolet

arrayJoin( separator, index, count )

NEWv4.12.0+

Flatten an array of String or Number

params desciption type
separator [optional] item separator (, by default) String
index [optional] select items from this array index. Number
count [optional] number of items to select from index. count can be a negative number to select N items from the end of the array. Number

Examples

['homer','bart','lisa']:arrayJoin() // "homer, bart, lisa"
['homer','bart','lisa']:arrayJoin(' | ') // "homer | bart | lisa"
['homer','bart','lisa']:arrayJoin('') // "homerbartlisa"
[10,50]:arrayJoin() // "10, 50"
[]:arrayJoin() // ""
null:arrayJoin() // null
{}:arrayJoin() // {}
20:arrayJoin() // 20
undefined:arrayJoin() // undefined
['homer','bart','lisa']:arrayJoin('', 1) // "bartlisa"
['homer','bart','lisa']:arrayJoin('', 1, 1) // "bart"
['homer','bart','lisa']:arrayJoin('', 1, 2) // "bartlisa"
['homer','bart','lisa']:arrayJoin('', 0, -1) // "homerbart"

arrayMap( objSeparator, attributeSeparator, attributes )

v0.12.5+

params desciption type
objSeparator [optional] object separator (, by default) String
attributeSeparator [optional] attribute separator (: by default) String
attributes [optional] list of object's attributes to print String

Examples

[{'id':2,'name':'homer'},{'id':3,'name':'bart'}]:arrayMap() // "2:homer, 3:bart"
[{'id':2,'name':'homer'},{'id':3,'name':'bart'}]:arrayMap(' - ') // "2:homer - 3:bart"
[{'id':2,'name':'homer'},{'id':3,'name':'bart'}]:arrayMap(' ; ', '|') // "2|homer ; 3|bart"
[{'id':2,'name':'homer'},{'id':3,'name':'bart'}]:arrayMap(' ; ', '|', 'id') // "2 ; 3"
[{'id':2,'name':'homer','obj':{'id':20},'arr':[12,23]}]:arrayMap() // "2:homer"
['homer','bart','lisa']:arrayMap() // "homer, bart, lisa"
[10,50]:arrayMap() // "10, 50"
[]:arrayMap() // ""
null:arrayMap() // null
{}:arrayMap() // {}
20:arrayMap() // 20
undefined:arrayMap() // undefined

count( start )

v1.1.0+

Count and print row number of any array

Usage example: d[i].id:count() will print a counter of the current row no matter the value of id

This formatter is replaced internally by :cumCount since version v4.0.0

params desciption type
start Number to start with (default: 1) String

Conditioned output

It is a system to hide and show a part of the document, the list of formatters:

  • drop (element) : NEW Delete specific elements conditionally from the document if condition is true. Simpler alternative to hideBegin/hideEnd.
  • keep (element) : NEW Display specific elements conditionally from the document if condition is true. Simpler alternative to showBegin/showEnd.
  • hideBegin / hideEnd : Hides the specified section of the document between "hideBegin" and "hideEnd" if the condition is met. It can be multiple pages, paragraphs, images, and more.
  • showBegin / showEnd : Display the specified section of the document between "showBegin" and "showEnd" if the condition is met. It can be multiple pages, paragraphs, images, and more.
  • ifEQ (value) : Matches values that are equal to a specified value, it replaces ifEqual
  • ifNE (value) : Matches all values that are not equal to a specified value
  • ifGT (value) : Matches values that are greater than a specified value.
  • ifGTE (value) : Matches values that are greater than or equal to a specified value.
  • ifLT (value) : Matches values that are less than a specified value.
  • ifLTE (value) : Matches values that are less than or equal to a specified value.
  • ifIN (value) : Matches any of the values specified in an array or string, it replaces ifContain
  • ifNIN (value) : Matches none of the values specified in an array or string
  • ifEM () : Matches empty values, string, arrays or objects, it replaces ifEmpty
  • ifNEM () : Matches not empty values, string, arrays or objects
  • ifTE (value) : Matches values where the type equals a specified value
  • and (value) : AND operator between two consecutive conditional formatters
  • or (value) : (default) OR operator between two consecutive conditional formatters
  • show (message) : print a message if a condition is true
  • elseShow (message) : print a message if a condition is false

No formatters can be chained after drop, hideBegin, hideEnd, showBegin, showEnd.

These new formatters replace the old ones ifEqual, ifEmpty and ifContain. It should be avoided to use them in new templates.

By default, condition formatters have a special behavior, the formatter's result is not propagated to the next formatter if the result of the condition is true. This enables test chaining with dynamic parameters.

Conditioned output example

Data

{
  "status1" : 1,
  "status2" : 2,
  "status3" : 3
}

Template

one = { d.status1:ifEQ(2):show(two):or(.status1):ifEQ(1):show(one):elseShow(unknown) }

two = { d.status2:ifEQ(2):show(two):or(.status2):ifEQ(1):show(one):elseShow(unknown) }

three = { d.status3:ifEQ(2):show(two):or(.status3):ifEQ(1):show(one):elseShow(unknown) }

Result

one = "one"

two = "two"

three = "unknown"

Drop(element)

ENTERPRISE FEATURE UPDATEDv4.17.0+

Use the drop formatter to delete elements from a document. Currently, this feature is exclusively accessible for DOCX/ODT/ODS/ODP/PDF/HTML documents.

The first argument passed to :drop(argument1) is the element to drop. It can be:

  • p to drop paragraphs, usage: {d.text:ifEM:drop(p)}. Include the tag within a paragraph, and all the elements it comprises are also removed if the condition is validated. Tutorial to drop paragraphs,
    • Option nbrParagraphToHide: A second argument is accepted to delete multiple paragraphe {d.data:ifEM:drop(row, nbrParagraphToDrop)}, such as {d.text:ifEM:drop(p, 3)} meaning the current paragraph and the two following paragraphs will be deleted.
  • row to drop table rows, usage: {d.data:ifEM:drop(row)}. Include the tag in an table row, and all the elements it comprises are also deleted if the condition is validated. Tutorial to drop table rows.
    • Option nbrRowsToDrop: Set the number of rows to drop as a second argument {d.data:ifEM:drop(row, nbrRowsToDrop)}, such as: {d.data:ifEM:drop(row, 3)}, meaning the current and next two rows will be removed if the condition is validated.
  • img to drop pictures, usage: {d.img:ifEM:drop(img)}. Include the tag within the image' title, description or alternative text. Tutorial to drop images with MS Word or LibreOffice.
  • table to drop table conditionally. Usage: {d.value:ifEM:drop(table)}. The tag must be located within a table cell.
  • chart to drop charts, usage: {d.dataset:ifEM:drop(chart)}. Include the tag within the alternative text of the graphic. Tutorial to drop charts.
  • shape to drop shape (square, circle, arrows, etc...), usage: {d.dataset:ifEM:drop(chart)}. Included the tag within the title, description or alternative text of the shape. Tutorial to drop shapes.
  • slide to drop slides, usage: {d.dataset:ifEM:drop(slide)}. This option is exclusively accessible for ODP templates, and the tag must be positioned within a text box.

Important Notes:

  • When the drop formatter is used, the Carbone tag value is not printed, and chained formatters are not executed.
  • The ODS template supports only the :drop(row) and :drop(img).
  • The HTML template supports only the :drop(table), :drop(row), and :drop(p).
  • The :keep is the opposite of the "drop" operation.
  • To delete conditionally a GROUP of elements, use hideBegin/hideEnd.

What are the advantages of choosing drop over hideBegin/hideEnd?

  • It offers a more concise syntax, requiring only a single Carbone tag instead of two (Begin + End).
  • It is easier to use, particularly in situations where placing hideBegin/hideEnd formatters in the document to hide a table row, for example, can be challenging.

Keep(element)

ENTERPRISE FEATURE NEWv4.17.0+

Utilize the :keep formatter to selectively print elements based on conditions within a document. Currently, this feature is exclusively accessible for DOCX/ODT/ODS/ODP/PDF/HTML documents.

The first argument passed to :keep(argument1) is the element to display. It can be:

  • p for paragraphs, usage: {d.text:ifNEM:keep(p)}. Embed the tag in a paragraph, and if the condition is met, all the elements it contains will be displayed.
    • Option nbrParagraphToKeep: A second argument is accepted to display multiple paragraphe {d.data:ifNEM:keep(row, nbrParagraphToKeep)}, such as {d.text:ifNEM:keep(p, 3)} meaning the current paragraph and the two following paragraphs will be printed.
  • row for table rows, usage: {d.data:ifEM:keep(row)}. Embed the tag in a table row, and if the condition is met, all the elements it contains will be displayed.
    • Option nbrRowsToKeep: Set the number of rows to drop as a second argument {d.data:ifNEM:keep(row, nbrRowsToKeep)}, such as: {d.data:ifNEM:keep(row, 3)}, meaning the current and next two rows will be printed if the condition is validated.
  • img for pictures, usage: {d.img:ifNEM:keep(img)}. Include the tag within the image' title, description or alternative text.
  • table for tables. Usage: {d.value:ifNEM:keep(table)}. Embed the tag in a table cell, and if the condition is met, all the elements it contains will be displayed.
  • chart for charts, usage: {d.dataset:ifNEM:keep(chart)}. Embed the tag in the alternative text of the chart, and if the condition is met, all the elements it contains will be displayed.
  • shape for shapes (square, circle, arrows, etc...), usage: {d.dataset:ifNEM:keep(chart)}. Embed the tag within the title, description or alternative text of the shape.
  • slide for slides, usage: {d.dataset:ifNEM:keep(slide)}. This option is exclusively accessible for ODP templates, and the tag must be positioned within a text box.

Important Notes:

  • When the keep formatter is used, the Carbone tag value is not printed, and chained formatters are not executed.
  • The ODS template supports only the :keep(row) and :keep(img).
  • The HTML template supports only the :keep(table), :keep(row), and :keep(p).
  • The :drop is the opposite of the "keep" operation.
  • To display conditionally a GROUP of elements, use showBegin/showEnd.

What are the advantages of choosing keep over showBegin/showEnd?

  • It offers a more concise syntax, requiring only a single Carbone tag instead of two (Begin + End).
  • It is easier to use, particularly in situations where placing hideBegin/hideEnd formatters in the document to hide a table row, for example, can be challenging.

showBegin()/showEnd()

COMMUNITY FEATURE v2.0.0+

Display the specified section of the document between "showBegin" and "showEnd" if the condition is met. It can be multiple pages, paragraphs, images, and more.

Best practices:

  • To display conditionally multiple elements, such as pages, tables or images: Use showBegin/showEnd. It is recommended to only use break-lines (shift + enter) between showBegin and showEnd (learn more).
  • To display one element conditionally, use the keep formatter, it provides a short and easy syntax.

Examples

Data

{
  "toBuy" : true
}

Template

Banana{d.toBuy:ifEQ(true):showBegin}
Apple
Pineapple
{d.toBuy:showEnd}grapes

Result

Banana
Apple
Pineapple
grapes

Note

hideBegin()/hideEnd()

COMMUNITY FEATURE v2.0.0+

Hides the specified section of the document between "hideBegin" and "hideEnd" if the condition is met. It can be multiple pages, paragraphs, images, and more. It is recommended to only use break-lines (shift + enter) between hideBegin and hideEnd (learn more).

Best practices:

  • To hide conditionally multiple elements, such as pages, titles, tables or images: Use hideBegin/hideEnd. It is recommended to only use break-lines (shift + enter) between hideBegin and hideEnd (learn more).
  • To hide one element conditionally, use the drop formatter, it provides a short and easy syntax to delete a paragraph, table row, image, chart or shape.

Examples

Data

{
  "toBuy" : true
}

Template

Banana{d.toBuy:ifEQ(true):hideBegin}
Apple
Pineapple
{d.toBuy:hideEnd}grapes

Result

Banana
grapes

and( value )

v2.0.0+

Change the default operator between conditional formatters.

For example: {d.car:ifEQ('delorean'):and(.speed):ifGT(80):show('TravelInTime'):elseShow('StayHere')}
means "if d.car equals 'delorean' AND d.speed is greater than 80, then it prints 'TravelInTime', otherwise
it prints 'StayHere'

params desciption type
value [optional] new value to test Mixed

or( value )

v2.0.0+

Change the default operator between conditional formatters.

For example: {d.car:ifEQ('delorean'):or(.speed):ifGT(80):show('TravelInTime'):elseShow('StayHere')}
means "if d.car equals 'delorean' OR d.speed is greater than 80, then it prints 'TravelInTime', otherwise
it prints 'StayHere'

params desciption type
value [optional] new value to test Mixed

ifEM( )

v2.0.0+

Matches empty values, string, arrays or objects (null, undefined, [], {}, ...), it replaces ifEmpty.

Examples

null:ifEM():show('Result true'):elseShow('Result false') // "Result true"
[]:ifEM():show('Result true'):elseShow('Result false') // "Result true"
{}:ifEM():show('Result true'):elseShow('Result false') // "Result true"
'':ifEM():show('Result true'):elseShow('Result false') // "Result true"
0:ifEM():show('Result true'):elseShow('Result false') // "Result false"
'homer':ifEM():show('Result true'):elseShow('Result false') // "Result false"
[23]:ifEM():show('Result true'):elseShow('Result false') // "Result false"
{'id':3}:ifEM():show('Result true'):elseShow('Result false') // "Result false"

ifNEM( )

v2.0.0+

Matches not empty values, string, arrays or objects.

Examples

0:ifNEM():show('Result true'):elseShow('Result false') // "Result true"
'homer':ifNEM():show('Result true'):elseShow('Result false') // "Result true"
[23]:ifNEM():show('Result true'):elseShow('Result false') // "Result true"
{'id':3}:ifNEM():show('Result true'):elseShow('Result false') // "Result true"
null:ifNEM():show('Result true'):elseShow('Result false') // "Result false"
[]:ifNEM():show('Result true'):elseShow('Result false') // "Result false"
{}:ifNEM():show('Result true'):elseShow('Result false') // "Result false"
'':ifNEM():show('Result true'):elseShow('Result false') // "Result false"

ifEQ( value )

v2.0.0+

Matches all values that are equal to a specified value. It can be combined with other formatters to create conditional content. It returns the initial marker. The state of the condition is not returned.

params desciption type
value value to test String, Integer

Examples

100:ifEQ(100):show('Result true'):elseShow('Result false') // "Result true"
100:ifEQ(101):show('Result true'):elseShow('Result false') // "Result false"
'homer':ifEQ('homer'):show('Result true'):elseShow('Result false') // "Result true"
'homer':ifEQ('bart'):show('Result true'):elseShow('Result false') // "Result false"
'':ifEQ(''):show('Result true'):elseShow('Result false') // "Result true"
null:ifEQ(100):show('Result true'):elseShow('Result false') // "Result false"
null:ifEQ(null):show('Result true'):elseShow('Result false') // "Result true"
0:ifEQ(100):show('Result true'):elseShow('Result false') // "Result false"

ifNE( value )

v2.0.0+

Matches all values that are not equal to a specified value. It can be combined with other formatters to create conditional content. It returns the initial marker. The state of the condition is not returned.

params desciption type
value value to test String, Integer

Examples

100:ifNE(100):show('Result true'):elseShow('Result false') // "Result false"
100:ifNE(101):show('Result true'):elseShow('Result false') // "Result true"
'homer':ifNE('homer'):show('Result true'):elseShow('Result false') // "Result false"
'homer':ifNE('bart'):show('Result true'):elseShow('Result false') // "Result true"
'':ifNE(''):show('Result true'):elseShow('Result false') // "Result false"
null:ifNE(100):show('Result true'):elseShow('Result false') // "Result true"
null:ifNE(null):show('Result true'):elseShow('Result false') // "Result false"
0:ifNE(100):show('Result true'):elseShow('Result false') // "Result true"

ifGT( value )

v2.0.0+

Matches values that are greater than a specified value.

params desciption type
value value to test Integer

Examples

1234:ifGT(1):show('Result true'):elseShow('Result false') // "Result true"
'50':ifGT('-29'):show('Result true'):elseShow('Result false') // "Result true"
'32q':ifGT('4q2'):show('Result true'):elseShow('Result false') // "Result true"
'1234Hello':ifGT('1'):show('Result true'):elseShow('Result false') // "Result true"
'10':ifGT('8Hello1234'):show('Result true'):elseShow('Result false') // "Result true"
-23:ifGT(19):show('Result true'):elseShow('Result false') // "Result false"
1:ifGT(768):show('Result true'):elseShow('Result false') // "Result false"
0:ifGT(0):show('Result true'):elseShow('Result false') // "Result false"
-2891:ifGT('33Hello'):show('Result true'):elseShow('Result false') // "Result false"

ifGTE( value )

v2.0.0+

Matches values that are greater than or equal to a specified value.

params desciption type
value value to test Integer

Examples

50:ifGTE(-29):show('Result true'):elseShow('Result false') // "Result true"
1:ifGTE(1):show('Result true'):elseShow('Result false') // "Result true"
1290:ifGTE(768):show('Result true'):elseShow('Result false') // "Result true"
'1234':ifGTE('1'):show('Result true'):elseShow('Result false') // "Result true"
-23:ifGTE(19):show('Result true'):elseShow('Result false') // "Result false"
1:ifGTE(768):show('Result true'):elseShow('Result false') // "Result false"
'1':ifGTE('1234'):show('Result true'):elseShow('Result false') // "Result false"

ifLT( value )

v2.0.0+

Matches values that are less than a specified value.

params desciption type
value value to test Integer

Examples

-23:ifLT(19):show('Result true'):elseShow('Result false') // "Result true"
1:ifLT(768):show('Result true'):elseShow('Result false') // "Result true"
'1':ifLT('1234'):show('Result true'):elseShow('Result false') // "Result true"
'123dsf':ifLT('103123'):show('Result true'):elseShow('Result false') // "Result true"
-1299283:ifLT('-2891feihuwf'):show('Result true'):elseShow('Result false') // "Result true"
50:ifLT(-29):show('Result true'):elseShow('Result false') // "Result false"
0:ifLT(0):show('Result true'):elseShow('Result false') // "Result false"
1290:ifLT(768):show('Result true'):elseShow('Result false') // "Result false"
'1234':ifLT('1'):show('Result true'):elseShow('Result false') // "Result false"

ifLTE( value )

v2.0.0+

Matches values that are less than or equal to a specified value.

params desciption type
value value to test Integer

Examples

-23:ifLTE(19):show('Result true'):elseShow('Result false') // "Result true"
1:ifLTE(768):show('Result true'):elseShow('Result false') // "Result true"
5:ifLTE(5):show('Result true'):elseShow('Result false') // "Result true"
'1':ifLTE('1234'):show('Result true'):elseShow('Result false') // "Result true"
1290:ifLTE(768):show('Result true'):elseShow('Result false') // "Result false"
'1234':ifLTE('1'):show('Result true'):elseShow('Result false') // "Result false"

ifIN( value )

v2.0.0+

Matches any of the values specified in an array or string, it replaces ifContain.

params desciption type
value value to test Integer

Examples

'car is broken':ifIN('is'):show('Result true'):elseShow('Result false') // "Result true"
[1,2,'toto']:ifIN(2):show('Result true'):elseShow('Result false') // "Result true"
'car is broken':ifIN('are'):show('Result true'):elseShow('Result false') // "Result false"
[1,2,'toto']:ifIN('titi'):show('Result true'):elseShow('Result false') // "Result false"

ifNIN( value )

v2.0.0+

Matches none of the values specified in an array or string.

params desciption type
value value to test Integer

Examples

'car is broken':ifNIN('are'):show('Result true'):elseShow('Result false') // "Result true"
[1,2,'toto']:ifNIN('titi'):show('Result true'):elseShow('Result false') // "Result true"
'car is broken':ifNIN('is'):show('Result true'):elseShow('Result false') // "Result false"
[1,2,'toto']:ifNIN(2):show('Result true'):elseShow('Result false') // "Result false"

ifTE( type )

NEWv4.4.0+

Tests the type of the operand's value.

params desciption type
type can be "string", "number", "integer", "boolean", "binary", "object", "array" String

Examples

0:ifTE('string'):show('Result true'):elseShow('Result false') // "Result false"
[23]:ifTE('string'):show('Result true'):elseShow('Result false') // "Result false"
{'id':3}:ifTE('string'):show('Result true'):elseShow('Result false') // "Result false"
null:ifTE('string'):show('Result true'):elseShow('Result false') // "Result false"
[]:ifTE('string'):show('Result true'):elseShow('Result false') // "Result false"
{}:ifTE('string'):show('Result true'):elseShow('Result false') // "Result false"
'10':ifTE('string'):show('Result true'):elseShow('Result false') // "Result true"
'homer':ifTE('string'):show('Result true'):elseShow('Result false') // "Result true"
'':ifTE('string'):show('Result true'):elseShow('Result false') // "Result true"
true:ifTE('boolean'):show('Result true'):elseShow('Result false') // "Result true"
false:ifTE('boolean'):show('Result true'):elseShow('Result false') // "Result true"
'0':ifTE('boolean'):show('Result true'):elseShow('Result false') // "Result false"
'false':ifTE('boolean'):show('Result true'):elseShow('Result false') // "Result false"
'0':ifTE('binary'):show('Result true'):elseShow('Result false') // "Result true"
'1':ifTE('binary'):show('Result true'):elseShow('Result false') // "Result true"
false:ifTE('binary'):show('Result true'):elseShow('Result false') // "Result true"
'false':ifTE('binary'):show('Result true'):elseShow('Result false') // "Result true"
10.5:ifTE('number'):show('Result true'):elseShow('Result false') // "Result true"
'10.5':ifTE('number'):show('Result true'):elseShow('Result false') // "Result false"

show( message )

v2.0.0+

Print a message if the condition is true. It should be used with other formatters to print conditional content.

params desciption type
message message to print

Examples

'Carbone.io':show() // "Carbone.io"

elseShow( message )

v2.0.0+

Print a message if the condition is false. It should be used with other formatters to print conditional content.

params desciption type
message message to print

ifEmpty( message, continueOnSuccess )

v0.12.5+

Test if data is empty (null, undefined, [], {}, ...). The new formatter ifEM should be used instead of this one.

params desciption type
message message to print if JSON data is empty String
continueOnSuccess [optional], if true, next formatter will be called even if the condition is true Boolean

Examples

null:ifEmpty('D'oh!') // "D'oh!"
[]:ifEmpty('D'oh!') // "D'oh!"
{}:ifEmpty('D'oh!') // "D'oh!"
'':ifEmpty('D'oh!') // "D'oh!"
0:ifEmpty('D'oh!') // 0
'homer':ifEmpty('D'oh!') // "homer"
[23]:ifEmpty('D'oh!') // [23]
{'id':3}:ifEmpty('D'oh!') // {"id":3}

ifEqual( value, messageIfTrue, continueOnSuccess )

v0.13.0+

Test if a value equals a variable. The new formatter ifEQ should be used instead of this one.

params desciption type
value value to test String, Integer, Boolean
messageIfTrue message to print if the value equals JSON data String
continueOnSuccess [optional], if true, next formatter will be called even if the condition is true Boolean

Examples

100:ifEqual(100, 'bingo') // "bingo"
100:ifEqual(101, 'bingo') // 100
'homer':ifEqual('homer', 'bingo') // "bingo"
'homer':ifEqual('bart', 'bingo') // "homer"
'':ifEqual('', 'bingo') // "bingo"
null:ifEqual(100, 'bingo') // null
null:ifEqual(null, 'bingo') // "bingo"
0:ifEqual(100, 'bingo') // 0

ifContain( value, messageIfTrue, continueOnSuccess )

v0.13.0+

Test if a string or an array contains a value. The new formatter ifIN should be used instead of this one.

params desciption type
value value to search String, Integer, Boolean
messageIfTrue message to print if JSON data contains the value String
continueOnSuccess [optional], if true, next formatter will be called even if the condition is true Boolean

Examples

'your beautiful eyes':ifContain('beauti', 'bingo') // "bingo"
'your beautiful eyes':ifContain('leg', 'bingo') // "your beautiful eyes"
'your beautiful eyes':ifContain('eyes', 'bingo') // "bingo"
'':ifContain('eyes', 'bingo') // ""
'your beautiful eyes':ifContain('', 'bingo') // "bingo"
[100,120,20]:ifContain(120, 'bingo') // "bingo"
[100,120,20]:ifContain(99, 'bingo') // [100,120,20]
['your','eyes']:ifContain('eyes', 'bingo') // "bingo"
[]:ifContain('eyes', 'bingo') // []

Date manipulation

Since the version 3.0.0, Carbone uses DayJS to convert dates instead of MomentJS.
Date formatting with Dayjs remains the same as Momentjs.

The tag {c.now} can be used in your template to get the current date in UTC. It is returned only if the complement object option.complement is not overwrited by custom data.

formatD( patternOut, patternIn )

UPDATEDv3.0.0+

Format dates. It takes an output date pattern as an argument. Date patterns are available on this section.
It is possible to change the timezone through the option options.timezone and the lang through options.lang.
List of timezones: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.

params desciption type
patternOut output format String
patternIn [optional] input format, "ISO 8601" by default String

Examples

// with options = {
//   "lang": "en",
//   "timezone": "Europe/Paris"
// }
'20160131':formatD('L') // "01/31/2016"
'20160131':formatD('LL') // "January 31, 2016"
'20160131':formatD('LLLL') // "Sunday, January 31, 2016 12:00 AM"
'20160131':formatD('dddd') // "Sunday"
// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
'2017-05-10T15:57:23.769561+03:00':formatD('LLLL') // "mercredi 10 mai 2017 14:57"
'2017-05-10 15:57:23.769561+03:00':formatD('LLLL') // "mercredi 10 mai 2017 14:57"
'20160131':formatD('LLLL') // "dimanche 31 janvier 2016 00:00"
'20160131':formatD('dddd') // "dimanche"
// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
'20160131':formatD('dddd', 'YYYYMMDD') // "dimanche"
1410715640:formatD('LLLL', 'X') // "dimanche 14 septembre 2014 19:27"
// with options = {
//   "lang": "fr",
//   "timezone": "Asia/Singapore"
// }
'20160131':formatD('dddd', 'YYYYMMDD') // "dimanche"
1410715640:formatD('LLLL', 'X') // "lundi 15 septembre 2014 01:27"

formatI( patternOut, patternIn )

v4.1.0+

Format intervals / duration. List of format name :

  • human+
  • human
  • millisecond(s) or ms
  • second(s) or s
  • minute(s) or m
  • hour(s) or h
  • year(s) or y
  • month(s) or M
  • week(s) or w
  • day(s) or d
params desciption type
patternOut output format: human, human+, milliseconds, seconds, ... String
patternIn [optional] input unit: milliseconds, seconds, ... String

Examples

// with options = {
//   "lang": "en",
//   "timezone": "Europe/Paris"
// }
2000:formatI('second') // 2
2000:formatI('seconds') // 2
2000:formatI('s') // 2
3600000:formatI('minute') // 60
3600000:formatI('hour') // 1
2419200000:formatI('days') // 28
// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
2000:formatI('human') // "quelques secondes"
2000:formatI('human+') // "dans quelques secondes"
-2000:formatI('human+') // "il y a quelques secondes"
// with options = {
//   "lang": "en",
//   "timezone": "Europe/Paris"
// }
2000:formatI('human') // "a few seconds"
2000:formatI('human+') // "in a few seconds"
-2000:formatI('human+') // "a few seconds ago"
// with options = {
//   "lang": "en",
//   "timezone": "Europe/Paris"
// }
60:formatI('ms', 'minute') // 3600000
4:formatI('ms', 'weeks') // 2419200000
// with options = {
//   "lang": "en",
//   "timezone": "Europe/Paris"
// }
'P1M':formatI('ms') // 2592000000
'P1Y2M3DT4H5M6S':formatI('hour') // 10276.085

addD( amount, unit, patternIn )

v3.0.0+

Add a time to a date. Available units: day, week, month, quarter, year, hour, minute, second and millisecond.
Units are case insensitive, and support plural and short forms.

params desciption type
amount The amount Number
unit The unit String
patternIn [optional] input format, ISO8601 by default String

Examples

// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
'2017-05-10T15:57:23.769561+03:00':addD('3', 'day') // "2017-05-13T12:57:23.769Z"
'2017-05-10 15:57:23.769561+03:00':addD('3', 'month') // "2017-08-10T12:57:23.769Z"
'20160131':addD('3', 'day') // "2016-02-03T00:00:00.000Z"
'20160131':addD('3', 'month') // "2016-04-30T00:00:00.000Z"
'31-2016-01':addD('3', 'month', 'DD-YYYY-MM') // "2016-04-30T00:00:00.000Z"

subD( amount, unit, patternIn )

v3.0.0+

Subtract a time to a date. Available units: day, week, month, quarter, year, hour, minute, second and millisecond.
Units are case insensitive, and support plural and short forms.

params desciption type
amount The amount Number
unit The unit String
patternIn [optional] input format, ISO8601 by default String

Examples

// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
'2017-05-10T15:57:23.769561+03:00':subD('3', 'day') // "2017-05-07T12:57:23.769Z"
'2017-05-10 15:57:23.769561+03:00':subD('3', 'month') // "2017-02-10T12:57:23.769Z"
'20160131':subD('3', 'day') // "2016-01-28T00:00:00.000Z"
'20160131':subD('3', 'month') // "2015-10-31T00:00:00.000Z"
'31-2016-01':subD('3', 'month', 'DD-YYYY-MM') // "2015-10-31T00:00:00.000Z"

startOfD( unit, patternIn )

v3.0.0+

Create a date and set it to the start of a unit of time.

params desciption type
unit The unit String
patternIn [optional] input format, ISO8601 by default String

Examples

// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
'2017-05-10T15:57:23.769561+03:00':startOfD('day') // "2017-05-10T00:00:00.000Z"
'2017-05-10 15:57:23.769561+03:00':startOfD('month') // "2017-05-01T00:00:00.000Z"
'20160131':startOfD('day') // "2016-01-31T00:00:00.000Z"
'20160131':startOfD('month') // "2016-01-01T00:00:00.000Z"
'31-2016-01':startOfD('month', 'DD-YYYY-MM') // "2016-01-01T00:00:00.000Z"

endOfD( unit, patternIn )

v3.0.0+

Create a date and set it to the end of a unit of time.

params desciption type
unit The unit String
patternIn [optional] input format, ISO8601 by default String

Examples

// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
'2017-05-10T15:57:23.769561+03:00':endOfD('day') // "2017-05-10T23:59:59.999Z"
'2017-05-10 15:57:23.769561+03:00':endOfD('month') // "2017-05-31T23:59:59.999Z"
'20160131':endOfD('day') // "2016-01-31T23:59:59.999Z"
'20160131':endOfD('month') // "2016-01-31T23:59:59.999Z"
'31-2016-01':endOfD('month', 'DD-YYYY-MM') // "2016-01-31T23:59:59.999Z"

diffD( toDate, unit, patternFromDate, patternToDate )

v4.4.0+

Compute the difference between two dates and get an interval. List of available output units for the interval:

  • day(s) or d Day of Week (Sunday as 0, Saturday as 6)
  • week(s) or w Week of Year
  • quarter(s) or Q Quarter
  • month(s) or M Month (January as 0, December as 11)
  • year(s) or y Year
  • hour(s) or h Hour
  • minute(s) or m Minute
  • second(s) or s Second
  • millisecond(s) or ms Millisecond
params desciption type
toDate to date String, Number
unit The output unit: day, week, ... see the list above. Milliseconds by default. String
patternFromDate [optional] The pattern of fromDate, ISO8601 by default String
patternToDate [optional] The pattern of toDate, ISO8601 by default String

Examples

'20101001':diffD('20101201') // 5270400000
'20101001':diffD('20101201', 'second') // 5270400
'20101001':diffD('20101201', 's') // 5270400
'20101001':diffD('20101201', 'm') // 87840
'20101001':diffD('20101201', 'h') // 1464
'20101001':diffD('20101201', 'weeks') // 8
'20101001':diffD('20101201', 'days') // 61
'2010+10+01':diffD('2010=12=01', 'ms', 'YYYY+MM+DD', 'YYYY=MM=DD') // 5270400000

convDate( patternIn, patternOut )

UNRECOMMENDED FOR USEv1.0.0+

Format dates

params desciption type
patternIn input format String
patternOut output format String

Examples

// with options = {
//   "lang": "en",
//   "timezone": "Europe/Paris"
// }
'20160131':convDate('YYYYMMDD', 'L') // "01/31/2016"
'20160131':convDate('YYYYMMDD', 'LL') // "January 31, 2016"
'20160131':convDate('YYYYMMDD', 'LLLL') // "Sunday, January 31, 2016 12:00 AM"
'20160131':convDate('YYYYMMDD', 'dddd') // "Sunday"
1410715640:convDate('X', 'LLLL') // "Sunday, September 14, 2014 7:27 PM"
// with options = {
//   "lang": "fr",
//   "timezone": "Europe/Paris"
// }
'20160131':convDate('YYYYMMDD', 'LLLL') // "dimanche 31 janvier 2016 00:00"
'20160131':convDate('YYYYMMDD', 'dddd') // "dimanche"

Date formats

Date Formats DaysJS

UPDATEDv3.0.0+

Format Output Description
X 1360013296 Unix Timestamp
x 1360013296123 Unix Millisecond Timestamp
YY 18 Two-digit year
YYYY 2018 Four-digit year
M 1-12 The month, beginning at 1
MM 01-12 The month, 2-digits
MMM Jan-Dec The abbreviated month name
MMMM January-December The full month name
D 1-31 The day of the month
DD 01-31 The day of the month, 2-digits
d 0-6 The day of the week, with Sunday as 0
dd Su-Sa The min name of the day of the week
ddd Sun-Sat The short name of the day of the week
dddd Sunday-Saturday The name of the day of the week
H 0-23 The hour
HH 00-23 The hour, 2-digits
h 1-12 The hour, 12-hour clock
hh 01-12 The hour, 12-hour clock, 2-digits
m 0-59 The minute
mm 00-59 The minute, 2-digits
s 0-59 The second
ss 00-59 The second, 2-digits
SSS 000-999 The millisecond, 3-digits
Z +05:00 The offset from UTC, ±HH:mm
ZZ +0500 The offset from UTC, ±HHmm
A AM PM
a am pm

List of localized formats

Because preferred formatting differs based on language, there are a few tokens that can be used to format a date based on report language.

There are upper and lower case variations on the same formats. The lowercase version is intended to be the shortened version of its uppercase counterpart.

Format English Locale Sample Output
LT h:mm A 8:02 PM
LTS h:mm:ss A 8:02:18 PM
L MM/DD/YYYY 08/16/2018
LL MMMM D, YYYY August 16, 2018
LLL MMMM D, YYYY h:mm A August 16, 2018 8:02 PM
LLLL dddd, MMMM D, YYYY h:mm A Thursday, August 16, 2018 8:02 PM
l M/D/YYYY 8/16/2018
ll MMM D, YYYY Aug 16, 2018
lll MMM D, YYYY h:mm A Aug 16, 2018 8:02 PM
llll ddd, MMM D, YYYY h:mm A Thu, Aug 16, 2018 8:02 PM
Source: https://day.js.org/docs/en/display/format

Translations

COMMUNITY FEATURE

Carbone automatically translates dates and numbers with the formatN and formatD formatters according to the current lang passed when rendering the report.

To create multilingual reports, static text can also be translated using the {t( )} tags.

When the report is rendered, all text between {t( )} is replaced with its corresponding translation found in a separate key-value JSON map. Carbone automatically selects the key-value map that matches the lang attribute ('en-us', 'en-gb', ...). If a translation key is not found, Carbone prints the key in the final report.

The key-value map must be passed to Carbone when rendering the report. The method differs between the Open Source and Enterprise editions:

Method for Cloud and On-Premise Enterprise Edition

Here is an HTTP body example that shows how to send the key-value map.

"data"         : {},
"convertTo"    : "pdf",
"lang"         : "en-us", // target lang of the report
"translations" : {
  "en-us" : {             // corresponding translation
    "frites"     : "French fries",
    "Monsieur X" : "John Doe"
  },
  "en-gb" : {
    "frites": "chips"
  }
}

Method for the Community Edition

On startup, the Community Edition loads all JSON files found in the lang subdirectory of the templatePath. This template path can be set with carbone.set({templatePath : '/var/lib/templates' }).

Here is an example of the contents of JSON files on disk:

  templatesPath
    |- lang
      |- en-us.json : { "frites" : "French fries", "Monsieur X" : "John Doe"}
      |- en-gb.json : { "frites" : "chips" }

How to generate key-value JSON map?

These CLI tools can be used to find all {t( )} in all templates to create or update the key-value map:

# Enterprise Edition
./carbone-ee translate --lang en-us --path /var/lib/all-my-templates
# Community Edition
./node_modules/.bin/carbone translate --lang en-us --path /var/lib/all-my-templates

In both cases, Carbone parses all templates found in the --path directory and creates or updates the json file named --lang in the subdirectory lang of the template path.

The Carbone Studio Cloud will provide a new tool soon.

Example french translation

The following example is a report translated into French. It is possible to embed translation tags within other tags.

lang/fr.lang

{
  "apples": "Pommes",
  "meeting": "rendez-vous",
  "monday": "lundi",
  "tuesday": "mardi",
}

Data

data = {
  id: 2
}

options = {
  lang: "fr"
};

Template

{t('meeting')}

{t('apples')}

{d.id:ifEQ(2):show( {t('monday')} ):elseShow( {t('tuesday')} )}

Result

rendez-vous

pommes

lundi

Charts

ENTERPRISE FEATURE NEWv4.0.0+

Generate dynamic charts into documents with two solutions:

  • Native charts of LibreOffice or MS Word: design and configure visualisations from your text editor
  • Echart 5 charts as images: design and configure visualisations from your JSON data-set

Native charts

Native charts define how to create graphical visualisations from numerical data within your text editor. The format defines the data set to be used for the visual display and several different types of graphical displays (such as line charts, pie charts, etc.).

Tutorials:

Important notes:

  • The feature is now supported only for ODT, DOCX, and PDF documents.
    ODT/DOCX templates can generate PDF/ODT/DOCX documents.
  • Create charts only from your text editor: Word or LibreOffice. Then you have to insert the data series in the XLSX/ODS sheet provided by your text editor.
  • On LibreOffice, the {bindChart()} tag is required (details and examples on the LibreOffice tutorial).
  • It is not possible to copy-paste charts created from an external XLSX/ODS file.
  • It is not possible to link external spreadsheets as dynamic series of data.
  • Chart styling must be defined only within text editors (titles, colors, captions, etc...). It is not possible to edit the styling from a JSON data-set, unlike Echarts 5 charts.

List of supported charts

For MS Word:

Format Types Supported
2D/3D Column clustured, stacked, percent stacked
2D/3D Bar clustured, stacked, percent stacked
2D/3D Line line, stacked, percent stacked, line with markers, stacked line with markers, percent stacked line with markers
2D/3D Area area, stacked, percent stacked
2D/3D Pie pie
2D/3D Pie pie of pie, bar of pie
Doughtnut Doughtnut
Combo Clustured Column/Line, Stacked Area
Hierarchi sunburst chart, treemap chart
Statistical histogram, box and whisker
Waterfall waterfall, funnel, stock, surface, radar
Scatter scatter, bubbles
Map Map

For LibreOffice:

Format Types Supported
Column normal, stacked, percent stacked
Bar normal, stacked, percent stacked
Pie normal, exploded, donut, exploded donut
Area normal, stacked, percent stacked
Line point only, points and lines, lines only, 3D Lines
Scatter point only, points and lines, lines only, 3D Lines
Net point only, points and lines, lines only, Filled
Column and Line normal, stacked
Stock 1, 2, 3, 4
Bubble normal

Caption

  • Working
  • Not available

Tutorial to create dynamic charts on MS Word

The tutorial shows how to create a line chart within a DOCX/PDF document, it will represent the weather temperature during a short period.

  1. Prepare a JSON dataset by grouping temperatures history by dates (minimum, maximum, average):
{
  "temps": [
    {
      "date": "01/07/2022",
      "min": 13,
      "max": 28,
      "avg": 20.5
    },
    {
      "date": "02/07/2022",
      "min": 13,
      "max": 29,
      "avg": 21
    },
    {
      "date": "03/07/2022",
      "min": 14,
      "max": 31,
      "avg": 22.5
    },
    {
      "date": "04/07/2022",
      "min": 16,
      "max": 32,
      "avg": 24
    }
  ]
}
  1. Create a new DOCX template on MS Word. Insert a line chart: click on the Insert tab > Chart Menu > Line > 2D Line.

Tutorial insert charts with MS Word

  1. As soon as the chart is created, Word opens an Excel spreadsheet to edit charts values.

Tutorial insert charts with MS Word

  1. Delete default values, and insert temperature values by looping through the temps list. Each loop expression that contains [i] on the first row must be followed on the next line by the expression [i+1]. Dynamic charts do not support having only one [i+1] for multiple [i] expressions.

Tutorial insert charts with MS Word

  1. Generate the report as PDF or DOCX, and the chart is filled!

Tutorial insert charts with MS Word

Tutorial to create dynamic charts on LibreOffice

The tutorial shows how to create a pie chart within an ODT document. It will represent the quantity of cheese purchased by french households in 2019.

  1. Prepare a JSON data-set by grouping values by cheese types:
{
  "cheeses": [
    {
      "type": "Camembert",
      "purchasedTonnes": 47503
    },
    {
      "type": "Emmental",
      "purchasedTonnes": 152468
    },
    {
      "type": "Compté",
      "purchasedTonnes": 32925
    },
    {
      "type": "Goat's Buchette",
      "purchasedTonnes": 32095
    },
    {
      "type": "Mozzarella",
      "purchasedTonnes": 31066
    }
  ]
}
  1. Create a new ODT template on LibreOffice. Insert a chart: click on the Home tab > Chart button. A column chart will appear, Change the chart format by clicking on the button chart type on the chart toolbar. Tutorial insert charts with LibreOffice

  2. Inject the data dynamically thanks to a loop and the bindChart formatter: on the chart toolbar, click on Data Table. A popup will appear to edit chart values. The process of injecting dynamic data is different with LibreOffice: it is impossible to inject tag {d.value} inside table cells. However, it is possible only on the first column Categories, that's why the category name is written, then followed by bindChart tags. The {bindChart} formatter is used to bind a variable to a reference in the table cell, expressed in other words, "the value X in the chart must be replaced by the tag Y".

    Tutorial insert charts with LibreOffice Screenshot explanations:
    • On the first cell, the following expression is writen: {d.cheeses[i].type} {bindChart(3)=d.cheeses[i].purchasedTonnes}. The cheese type is printed with {d.cheeses[i].type}, then bindChart is used to bind the variable d.cheeses[i].purchasedTonnes to the cell that has the value 3. It means, the purchasedTonnes value will be printed instead of 3.
    • On the first cell of the second row, the expression {d.cheeses[i+1].type} {bindChart(4)=d.cheeses[i+1].purchasedTonnes} is written. The bindChart will replace the 4 value by the d.cheeses[i+1].purchasedTonnes variable.

  3. Generate the report as PDF or DOCX, and the Pie is filled! Tutorial insert charts with LibreOffice

Echarts 5 charts

Carbone embeds Apache Echarts: a rich, elegant and powerful visualisation library. A chart configuration must be provided within your JSON data-set. It must include: the chart format, numerical values, and extra options. Carbone will inject the chart as an SVG image into the document. Important notes:

  • ECharts visualisations are supported for PDF, ODT, ODS, ODP, ODG, DOCX, and XLSX documents.
  • To generate a DOCX or XLSX reports from a DOCX or XLSX templates: you must pass the rendering option "hardRefresh": true, such as: { "convertTo": "docx", "hardRefresh": true }. Otherwise, the chart won't generate. An alternative solution is to create an ODT or ODS template. Summary:
    • ✅ Supported: ODT to DOCX/ODT/PDF or ODS to XLSX/ODS/PDF
    • ✅ Supported: DOCX to DOCX/ODT/PDF or XLSX to XLSX/ODS/PDF with the option "hardRefresh": true.
    • ❌ Not supported: DOCX to DOCX or XLSX to XLSX without the option "hardRefresh": true.
  • Chart configuration doesn't support external dependencies for now (maps, js scripts).
  • Translations are supported: cs, de, en, es, fi, fr, it, ja, ko, pl, pt-br, ro, ru, si, th, zh.
  • Chart styling must be defined within the JSON data-set. As the generated chart is an image, it is not possible to edit the styling within the template, unlike native charts.

Tutorial to insert a line chart into a document

  1. In a template, insert a picture as a reference slot. Template with chart
  2. Place a tag in the alternative text of the picture: {d.chartOptions:chart} with the formatter :chart. Without :chart, the attribute {d.chartOptions} will be considered as an image.
  3. Design your JSON data-set by adding an object named chartOptions. The object must contain Echarts options and your data. Here is the configuration for a line chart:
{
  "chartOptions" : {
    "type"    : "echarts@v5", // Carbone Option - REQUIRED: Carbone supports only "echarts@v5" but we may support newer versions and other libraries in the future.
    "width"   : 600,          // Carbone Option - OPTIONAL: Image width
    "height"  : 400,          // Carbone Option - OPTIONAL: Image Height
    "theme"   : null,         // Carbone Option - OPTIONAL: default or object `theme` generated by https://echarts.apache.org/en/theme-builder.html
    "option"  : {             // Echart options - REQUIRED
      "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"
      }]
    }
  }
}
  1. PDF generated with the chart:

PDF report with a chart

The report is generated 🥳 Now, you can build any type of charts:

If you experience any limitations, please contact us on the chat.

Aggregators

ENTERPRISE FEATURE EXPERIMENTALv4.6.0+

An aggregate formatter calculates a set of values and returns a single value.
For example, the average :aggAvg takes a list of values and returns the average.

The following aggregators are available:

  • aggSum returns the sum of all values in a set.
  • aggAvg returns the average of a set.
  • aggMin returns the minimum value in a set.
  • aggMax returns the maximum value in a set.
  • aggCount returns the number of items in a set.
  • cumSum the cumulative sums, also known as running totals, returns the total sum of data as it grows with series.
  • cumCount assigns a sequential integer to each row in a list

Aggregators can be printed as standalone expressions or can be part of loops to compute custom grouping clauses, e.g.: sub-totals, cumulative totals.

It is an experimental feature that contains known issues, and corrections are on the way.

aggSum

:aggSum returns the sum of all values in a set.

Data

{
  "cars": [
    {"brand":"Lu","qty":1,"sort":1},
    {"brand":"Fa","qty":4,"sort":4},
    {"brand":"Vi","qty":3,"sort":3},
    {"brand":"Fa","qty":2,"sort":2},
    {"brand":"To","qty":1,"sort":1},
    {"brand":"Vi","qty":10,"sort":5}
  ]
}

Template

Total:
{d.cars[].qty:aggSum}
Total by sorting "sort" values above 1:
{d.cars[sort>1].qty:aggSum}
Total by multiplying the "qty" by the "sort" attribute:
{d.cars[sort>1].qty:mul(.sort):aggSum:formatC}

Result

Total:
21
Total by sorting "sort" values above 1:
19
Total by multiplying the "qty" by the "sort" attribute:
81

The aggSum aggregator can be used within loops, for instance:

  • sum-up people's salaries by departments:
    {d.departments[i].people[].salary:aggSum}
    {d.departments[i].people[].salary:aggSum(.i)} (alternative)
  • Global sum of all departments, all people's salary:
    {d.departments[i].people[i].salary:aggSum}
    {d.departments[i].people[i].salary:aggSum(0)} (alternative)

Change the partition with dynamic parameters:

  • Sum salaries by people by age, regardless of departments
    {d.departments[i].people[i].salary:aggSum(.age)}
  • Sum salaries by people by age and gender, regardless of departments
    {d.departments[i].people[i].salary:aggSum(.age, .gender)}

aggAvg

aggAvg returns the average of a set

Data

{
  "cars": [
    {"brand":"Lu","qty":1,"sort":1},
    {"brand":"Fa","qty":4,"sort":4},
    {"brand":"Vi","qty":3,"sort":3},
    {"brand":"Fa","qty":2,"sort":2},
    {"brand":"To","qty":1,"sort":1},
    {"brand":"Vi","qty":10,"sort":5}
  ]
}

Template

Average quantity:
{d.cars[].qty:aggAvg}
Average quantity by sorting "sort" values above 1:
{d.cars[sort>1].qty:aggAvg}
Average quantity by multiplying the "qty" by the "sort" attribute:
{d.cars[sort>1].qty:mul(.sort):aggAvg:formatC}

Result

Average quantity:
3.5
Average quantity by sorting "sort" values above 1:
4.75
Average quantity by multiplying the "qty" by the "sort" attribute:
13.50

aggMin

aggMin returns the minimum value in a set

Data

{
  "cars": [
    {"brand":"Lu","qty":1,"sort":1},
    {"brand":"Fa","qty":4,"sort":4},
    {"brand":"Vi","qty":3,"sort":3},
    {"brand":"Fa","qty":2,"sort":2},
    {"brand":"To","qty":1,"sort":1},
    {"brand":"Vi","qty":10,"sort":5}
  ]
}

Template

Minimum quantity:
{d.cars[].qty:aggMin}
Minimum quantity by sorting "sort" values above 1:
{d.cars[sort>1].qty:aggMin}
Minimum quantity by multiplying the "qty" by the "sort" attribute:
{d.cars[sort>1].qty:mul(.sort):aggMin:formatC}

Result

Minimum quantity:
1
Minimum quantity by sorting "sort" values above 1:
2
Minimum quantity by multiplying the "qty" by the "sort" attribute:
4

aggMax

aggMax returns the maximum value in a set

Data

{
  "cars": [
    {"brand":"Lu","qty":1,"sort":1},
    {"brand":"Fa","qty":4,"sort":4},
    {"brand":"Vi","qty":3,"sort":3},
    {"brand":"Fa","qty":2,"sort":2},
    {"brand":"To","qty":1,"sort":1},
    {"brand":"Vi","qty":10,"sort":5}
  ]
}

Template

Maximum quantity:
{d.cars[].qty:aggMax}
Maximum quantity by sorting "sort" values above 1:
{d.cars[sort>1].qty:aggMax}
Maximum quantity by multiplying the "qty" by the "sort" attribute:
{d.cars[sort>1].qty:mul(.sort):aggMax:formatC}

Result

Maximum quantity:
10
Maximum quantity by sorting "sort" values above 1:
10
Maximum quantity by multiplying the "qty" by the "sort" attribute:
50

aggCount

aggCount returns the number of items in a set

Data

{
  "cars": [
    {"brand":"Lu","qty":1,"sort":1},
    {"brand":"Fa","qty":4,"sort":4},
    {"brand":"Vi","qty":3,"sort":3},
    {"brand":"Fa","qty":2,"sort":2},
    {"brand":"To","qty":1,"sort":1},
    {"brand":"Vi","qty":10,"sort":5}
  ]
}

Template

Count quantity items:
{d.cars[].qty:aggCount}
Count quantity items by sorting "sort" values above 1:
{d.cars[sort>1].qty:aggCount}

Result

Count quantity items:
6
Count quantity items by sorting "sort" values above 1:
4

cumSum

The cumulative sums, as known as running totals, returns the total sum of data as it grows with series.

Data

{
  "departments": [
    { "type": "1", "people": [{ "salary": 1000 }] },
    { "type": "2", "people": [{ "salary": 500 }, { "salary": 200 }] },
    { "type": "4", "people": [{ "salary": 1500 }] },
    { "type": "5", "people": [{ "salary": 200 }] }
  ]
}

Template

The cumulative total of salaries by departments:
{d.departments[i].people[].salary:cumSum}
{d.departments[i+1]}



The cumulative total of salaries by departments and by people:
{d.departments[i].people[i].salary:cumSum}
{d.departments[i+1].people[i+1]}

Result

The cumulative total of salaries by departments:
1000
1700
3200
3400

The cumulative total of salaries by departments and by people:
1000
1500
1700
3200
3400

cumCount

cumCount assigns a sequential integer to each row in a list

A dynamic variable partition can be passed to cumCount(partition) the row number for each partition starts with one and increments by one.

Data

{
  "cars": [
    {"brand":"Lu", "group": 1},
    {"brand":"Fa", "group": 1},
    {"brand":"Vi", "group": 1},
    {"brand":"Fa", "group": 2},
    {"brand":"To", "group": 2},
    {"brand":"Vi", "group": 2}
  ]
}

Template

{d.cars[i].qty:cumCount()} {d.cars[i].qty:cumCount(.group)} {d.cars[i].brand}
{d.cars[i+1]}

Result

1 1 Lu
2 2 Fa
3 3 Vi
4 1 Fa
5 2 To
6 3 Vi

Colors

:color

ENTERPRISE FEATURENEWv4.17.0+

The :color formatter adjusts element colors in a document and is currently supported for DOCX, ODT, ODP, PDF, and HTML files. The formatter syntax is:

:color(scope, type)

The first argument, scope, specifies the element to be color-changed:

  • p default for paragraphs
  • cell for table cells
  • row for table rows
  • shape for shapes (e.g., squares, rectangles)

The second argument, type, defines the property to be modified and supports:

  • text default for the text color (supported scopes: p, cell, row).
  • highlight for the background color of texts (supported scopes: p, cell, row).
  • background for the background color (supported scopes: cell, row, and shape).
  • border for shape borders color (supported scope: shape).

Important notes:

  • The color provided must only be 6-digit hex color notation with or without hashtag, lowercase or uppercase: #FF0000 or FF0000. Carbone replaces wrong color values with light gray (#888888).
  • The :color manages priority between overlapping areas. A color applied to the current paragraph has a higher priority than a color applied to the current shape, cell and row.
  • For HTML, the color is applied with the style attribute only to the selected HTML tag (tr, td and p).
  • For ODP (text, table, and shapes) and ODT (shapes only), the :color formatter requires that a non-default style is already applied to the target element in the template (for example, a custom text color).
  • Complex nested tables with colors on sub-tables are not fully supported.
  • highlight is not managed in docx template
  • The :color cannot be used with aliases

:color examples

Text color replacement

The following example changes the text color to red.

Data

{
  "error": "#FF0000", // red
}

Template

Result

Conditional text color

The following example change the text color conditionally: {d.test:ifEQ('OK'):show(.success):elseShow(.error):color(p)}.

Details:

  • If the test value is equal to "OK", the changed-color will be d.success (color "#007700").
  • If the test value is not equal to "OK", the color will be d.error (color "#FF000").
  • The :color(p) applies the color on the text of the current paragraph.

Data

{
  "test": "OK",
  "success": "#007700",
  "error": "#FF000"
}

Template

Result

Conditional text color on a table row

The example change the text color conditionally of the current table row.

On the following the Carbone tag {d.tests[i].result:ifEQ(ok):show(#000000):elseShow(d.error):color(row, text) is used on the template:

  • If the result value is equal to "ok", the changed-color will be black #000000. The argument passed is a string, and not a value sourced from the JSON.
  • If the result value is not equal to "ok", the color will be d.error.
  • the :color(row, text) change the color on text located on the current row.

Data

{
  "error": "#FF0000",
  "tests": [
    { "name": "Security Training","result": "ok" },
    { "name": "Code Auditing","result": "20 Vulnerabilities found" },
    { "name": "Firewall Testing","result": "ok" }
  ]
}

Template

Result

Combine color conditions

The example change the table row background color and text color conditionally.

Two tags are used on the following template:

  • {d.tests[i].result:ifEQ(ok):show(#000000):elseShow(#FFFFFF):color(row, text)}: if result is equal to ok, the text color of the current row is changed to black #000000, otherwise it takes the white color #FFFFFF.
  • {d.tests[i].result:ifEQ(ok):show(#FFFFFF):elseShow(d.error):color(row, background)}: if the result is equal to ok, the row keeps a white background #FFFFFF, otherwise it take a the red color sourced from the JSON d.error.

Data

{
  "error": "#FF0000",
  "tests": [
    { "name": "Security Training","result": "ok" },
    { "name": "Code Auditing","result": "20 Vulnerabilities found" },
    { "name": "Firewall Testing","result": "ok" }
  ]
}

Template

Result

bindColor

ENTERPRISE FEATUREUNRECOMMENDED FOR USEv3.0.0+

The bindColor initially served for dynamically changing colors within documents. However, employing the new :color formatter is a more concise and straightforward solution for coloring documents.

The bindColor tag serves as a mechanism to dynamically replace a color reference in a template with a new color sourced from a JSON dataset. The following elements can be colored:

  • Text and background colors
  • Table cells background
  • Shapes backgrounds and lines colors for DOCX only. Write the bindColor tag in the document body, not in the alternative text of shapes. The replaced color format must be solely RGB.

Formatter breakdown:

{bindColor(myColorToBind, myFormat) = myVar}

Arguments details:

  • myColorToBind: This is a temporary hexadecimal color applied to text, background, or cell background. It is used by Carbone to identify the color that needs replacement. The hexadecimal value is case-insensitive, and the hashtag at the beginning is optional. Note that there is an exception regarding dynamic background colors on MS Word DOCX documents.
  • myFormat: This argument represents the new color format expected from the myVar tag. The available color formats are specified below.
  • myVar: This is the tag that corresponds to the new color. The color referenced by myColorToBind will be replaced with the color associated with myVar.

Important Notes:

  • Unlike regular tags, {bindColor()} can be placed anywhere in the document, such as the header, footer, or even below a repetition [i+1].
  • It's important to note that even if the myVar last argument is part of a repetition, the bindColor tag can still be used effectively.

For further understanding, you can look at the example bellow or download a template example with dynamic colors.

bindColor examples

Data

data = {
  color: "#FF0000", // red
  color2: "#00FF00", // green
  color3: "#0000FF" // blue
};

Template

ODT template content:

Basic dynamic color

Details:

  • Manage get the #00FFFF reference color and will be replaced by #FF0000 coming from the {d.color} tag.
  • dynamic get the #FF00FF reference color and will be replaced by #00FF00 coming from the {d.color2} tag.
  • color get in background the reference color #0000FF and will be replaced by #00FF00 coming from the {d.color3} tag.

Result

Basic dynamic color result

Color Formats

There are 4 color formats:

#hexa

So data looks like:

data = {
  color: "#FF0000"
};

hexa

So data looks like:

data = {
  color: "FF0000"
};

color

So data looks like:

data = {
  color: "red"
};

rgb

So data looks like:

data = {
  color: {
    r: 255, // Between 0 and 255
    g: 0, // Between 0 and 255
    b: 0 // Between 0 and 255
  }
};

hsl

So data looks like:

data = {
  color: {
    h: 300, // Between 0 and 360
    s: 50, // Between 0 and 100 or 0 and 1
    l: 50 // Between 0 and 100 or 0 and 1
  }
};

Microsoft Word exception

In Microsoft Word, only a color name can be used to replace the background color of a text.
17 colors are available, which are:

yellow    | green     | cyan      | magenta   | blue | red
darkBlue  | darkCyan  | darkGreen | darkMagenta |  darkRed | darkYellow
darkGray  | lightGray | black     | white     | transparent

You can still use hexadecimal colors for the text and cells backgrounds on Microsoft Word.

Data

data = {
  color: "darkYellow",
  color2: "green",
  color3: "11BBCC"
};

Template

Microsoft get in background the red color, Word get in background the magenta color and background get the #FFFF00 color

Word color exception

Result

Word color exception result

Color in table

Each line in a table can be colorized by one color:

Data

data =  {
    user: [
        {
            firstname: "Jean",
            lastname: "Dujardin",
            color: {
                r: 255,
                g: 0;
                b: 0
            }
        },
        {
            firstname: "Omar",
            lastname: "Sy",
            color: {
                r: 0,
                g: 255,
                b: 0
            }
        }
    ]
}

Template

The first line get the #0000FF color and the second line get the #00FFFF color. Each line get the color corresponding to the color key in each user object.

Table color

Result

Table color result

Forms

ENTERPRISE FEATURE UPDATEDv3.3.0+

Dynamic Text fields

Editable text fields are used for collecting user-provided information. Important notes:

  • The template must be an ODT file edited with LibreOffice
  • The text field edition is only supported by ODT and PDF documents.
  • Other file formats (XLSX, DOCX, ODS, etc...) do not support editable text field, nor when an ODT file is created with MS Word.

Tutorial: Create a dynamic text box using LibreOffice

  1. On the top menu, click on form > Text box
  2. Draw the input on the document body.
  3. On the form toolbar, disable "design mode".
  4. Write a Carbone tag inside the text box {d.value}.
  5. Generate the document as PDF or ODT with Carbone, and voilà!

Dynamic input example

Dynamic Checkboxes

Editable checkboxes provides users the ability to choose between two distinct values, and can be used in complex forms and checklists, multiple solutions:

  • Solution 1: Clickable checkboxes can be created only in PDFs and ODT reports (e.g. Example 1). The template must be an ODT file edited with Libre Office. Inserting a input element into another file format (XLSX, DOCX, ODS, etc...) will not make it clickable, neither when an ODT file is created with MS Word.
  • Solution 2: use Unicode Characters or Emojis (e.g. Example 2).
  • Solution 3: Create custom checkboxes from SVG (e.g. Example 3)

Example 1: Create a clickable checkbox in an ODT template using Libre Office

  1. On the top menu, click on "form" > "Check box"
  2. Insert the check box on the ODT template
  3. Right Click on the check box and click on "Control Properties"
  4. Insert the Carbone tag on the "Name" property. The checkbox is ticked (checked) when the value is a Boolean with the value "true", a non empty string, a non empty array or a non empty object.

Note: it is not possible to create a list of checkboxes, otherwhise an error is returned.

Dynamic checkbox PDF ODT DOCX

Example 2: Create a checkbox in any documents

With the following expression, if the value is true, it shows the , otherwhise ⬜️. Emojies can be replaced with Unicode Characters.

{d.value:ifEQ(true):show(✅):elseShow(⬜️)} // Emojies
{d.value:ifEQ(true):show(☑):elseShow(☐)} // Unicode Characters 1
{d.value:ifEQ(true):show(☒):elseShow(☐)} // Unicode Characters 2

Example 3: Create custom checkboxes from images

This example uses dynamic images to print custom checkboxes dynamically from SVG. The following JSON data-set is used:

{
  "value": false,
  "img1": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGc+PHJlY3Qgc3Ryb2tlPSIjMDAwIiBpZD0ic3ZnXzEiIGhlaWdodD0iMzAiIHdpZHRoPSIzMCIgeT0iMCIgeD0iMCIgZmlsbD0iIzA4ODc0RCIvPjwvZz48L3N2Zz4=",
  "img2": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGc+PHJlY3Qgc3Ryb2tlPSIjMDAwIiBpZD0ic3ZnXzEiIGhlaWdodD0iMzAiIHdpZHRoPSIzMCIgeT0iMCIgeD0iMCIgZmlsbD0iI0ZGRkZGRiIvPjwvZz48L3N2Zz4="
}

SVG images img2 and img1 are formatted as Data-URLs:

  • Encode the SVG image as base64.
  • Add the expression data:image/svg+xml;base64, before the base64.

On the template, insert a squared placeholder image. Then select the image and right-click > properties > alternative text > write the following expression to print a colored checkbox {d.value:ifEQ(true):show(.img1):elseShow(.img2)}. Expression details:

  • d.value is the checkbox value
  • :ifEQ(true) conditional formatter checking if the value is equal true
  • :show(.img1) if the condition is true, the .img1 svg image is printed
  • :show(.img1) if the condition is false, the .img2 svg image is printed
Dynamic checkbox PDF ODT DOCX

Hyperlinks

ENTERPRISE FEATURE v3.0.0+

Hyperlinks can be applied dynamically to elements (texts, images, tables, list) for DOCX, ODT, ODS, and XLSX documents. Right-click on the element and select "hyperlinks". Insert the Carbone tag and validate. Important notes:

  • It is no issue if the text editor replaces automatically curly braces with special characters %256B.
  • It is not possible insert a mix of static URL and an URL coming from a tag, such as https://carbone.io{d.url}.
    In this case, Carbone removes the https://carbone.io part and keep only {d.url}.
  • The compatibility is slightly different for XLSX files: the tag should not be written with curly braces.
    For example: {d.url} should be reduced to d.url. If http:// appears before d.url, it will work.

Before injecting hyperlinks dynamically on reports, Carbone verifies if the URL has a valid format.

It should contain:

  • Optional: a protocol, either http:// or https://. If it doesn't exist Carbone, adds the protocol https://.
  • Required: a root domain of length between 2 and 256.
  • Required: a top level domain like .com, .org, .fr, other...

In case of error, the link is replaced by: https://carbone.io/documentation.html#hyperlink-validation

defaultURL formatter

If a dynamic hyperlink or a HTML anchor tag is injected into a report and the URL verification fails, the formatter :defaultURL can be used to replace the default error URL. If the provided URL isn't valid, it is replaced by https://carbone.io/documentation.html#hyperlink-validation.

When inserting dynamic hyperlinks, chain the :defaultURL formatter to the tag, such as:

{d.url:defaultURL('https://carbone.io')}

It is possible to pass another tag as an argument:

{d.url:defaultURL(.urlOnError)}

When using the :html formatter, the :defaultURL should be placed before:

{d.content:defaultURL(.urlOnError):html}

Barcodes

ENTERPRISE FEATURE v3.4.6+

Carbone supports 107 barcodes for PDF, DOCX, ODT, XLSX, ODS, ODP, PPTX, and ODG documents.
There are 2 solutions to insert a barcode:

  • As dynamic image: the recommended solution to generate industry standard barcodes.
  • As font: the previous solution not recommended to use, and only 4 barcodes are supported

Both solution needs the tag :barcode(type) in the template, check examples below for each solution. The prototype of the formatter:

barcode( type )

Generate a barcode as an image or as a font.

params desciption type
type Barcode type: ean13, ean8, code128, code39, gs1-128, qrcode, swissqrcode, and more... String

Barcodes as image

Generating barcode as image is the same logic as dynamic pictures, it goes into 3 steps:

  1. Insert a temporary picture in your template (white JPEG image, or cat PNG image), it will be used by Carbone to know the location to insert the barcode.
  2. Write the Carbone tag followed by the barcode formatter into the alternative text, description, or title of the image. Check the dynamic pictures documentation for details. The format should be: {d.value:barcode(type)}
  3. Insert the type of barcode as a first argument of the formatter :barcode, check the list of supported barcodes. Finally, generate the document and voilà!

Barcode Options are available and can be pass to the barcode formatter as a second argument, such as: :barcode(type, options1, options2, ...). Check the list of barcodes options.

Barcodes as image example

This example shows how to insert 3 barcodes as images: ean13, qrcode, and gs1. First retrieve your barcode data as JSON from you database.

Data

const data = {
  urlQrCode: "http://carbone.io/",
  productCodeBarEan13: "2112345678900",
  productGs1: "(01)95012345678903(3103)000123"
};

Template

Edit your template with your text editor, insert 3 temporary images, and for each image, write as an alternative text Carbone tags:

Barcode template

  • For the first image: {d.urlQrCode:barcode(qrcode)}
  • For the second image: {d.productCodeBarEan13:barcode(ean13)}
  • For the third image: {d.productGs1:barcode(gs1-128)}

To scale the barcode, it is possible to chain the formatter :imageFit, such as: {d.urlQrCode:barcode(qrcode):imageFit(contain)}. Click to learn more about imageFit formatter.

Result

Barcode result

List of supported barcodes

Barcode nameType
EAN-5 (5 digit addon)ean5
EAN-2 (2 digit addon)ean2
EAN-13ean13
EAN-8ean8
UPC-Aupca
UPC-Eupce
ISBNisbn
ISMNismn
ISSNissn
Code 128code128
GS1-128gs1-128
GS1-14ean14
SSCC-18sscc18
Code 39code39
Code 39 Extendedcode39ext
Italian Pharmacodecode32
Pharmazentralnummer (PZN)pzn
Code 93code93
Code 93 Extendedcode93ext
Interleaved 2 of 5 (ITF)interleaved2of5
ITF-14itf14
Deutsche Post Identcodeidentcode
Deutsche Post Leitcodeleitcode
GS1 DataBar Omnidirectionaldatabaromni
GS1 DataBar Stackeddatabarstacked
GS1 DataBar Stacked Omnidirectionaldatabarstackedomni
GS1 DataBar Truncateddatabartruncated
GS1 DataBar Limiteddatabarlimited
GS1 DataBar Expandeddatabarexpanded
GS1 DataBar Expanded Stackeddatabarexpandedstacked
GS1 North American Coupongs1northamericancoupon
Pharmaceutical Binary Codepharmacode
Two-track Pharmacodepharmacode2
Code 25code2of5
Industrial 2 of 5industrial2of5
IATA 2 of 5iata2of5
Matrix 2 of 5matrix2of5
COOP 2 of 5coop2of5
Datalogic 2 of 5datalogic2of5
Code 11code11
BC412bc412
CodabarrationalizedCodabar
USPS Intelligent Mailonecode
USPS POSTNETpostnet
USPS PLANETplanet
Royal Mail 4 State Customer Coderoyalmail
AusPost 4 State Customer Codeauspost
Royal Dutch TPG Post KIXkix
Japan Post 4 State Customer Codejapanpost
MSI Modified Plesseymsi
Plessey UKplessey
Telepentelepen
Telepen Numerictelepennumeric
PosiCodeposicode
Codablock Fcodablockf
Code 16Kcode16k
Code 49code49
Channel Codechannelcode
Flattermarkenflattermarken
Custom 1D symbologyraw
Custom 4 state symbologydaft
Miscellaneous symbolssymbol
PDF417pdf417
Compact PDF417pdf417compact
MicroPDF417micropdf417
Data Matrixdatamatrix
Data Matrix Rectangulardatamatrixrectangular
Data Matrix Rectangular Extensiondatamatrixrectangularextension
Royal Mail Mailmarkmailmark
QR Codeqrcode
Swiss QR Codeswissqrcode
Micro QR Codemicroqrcode
Rectangular Micro QR Coderectangularmicroqrcode
MaxiCodemaxicode
Aztec Codeazteccode
Compact Aztec Codeazteccodecompact
Aztec Runesaztecrune
Code Onecodeone
Han Xin Codehanxin
DotCodedotcode
Ultracodeultracode
GS1 Composite 2D Componentgs1-cc
EAN-13 Compositeean13composite
EAN-8 Compositeean8composite
UPC-A Compositeupcacomposite
UPC-E Compositeupcecomposite
GS1 DataBar Omnidirectional Compositedatabaromnicomposite
GS1 DataBar Stacked Compositedatabarstackedcomposite
GS1 DataBar Stacked Omnidirectional Compositedatabarstackedomnicomposite
GS1 DataBar Truncated Compositedatabartruncatedcomposite
GS1 DataBar Limited Compositedatabarlimitedcomposite
GS1 DataBar Expanded Compositedatabarexpandedcomposite
GS1 DataBar Expanded Stacked Compositedatabarexpandedstackedcomposite
GS1-128 Compositegs1-128composite
GS1 Data Matrixgs1datamatrix
GS1 Data Matrix Rectangulargs1datamatrixrectangular
GS1 QR Codegs1qrcode
GS1 DotCodegs1dotcode
HIBC Code 39hibccode39
HIBC Code 128hibccode128
HIBC Data Matrixhibcdatamatrix
HIBC Data Matrix Rectangularhibcdatamatrixrectangular
HIBC PDF417hibcpdf417
HIBC MicroPDF417hibcmicropdf417
HIBC QR Codehibcqrcode
HIBC Codablock Fhibccodablockf
HIBC Aztec Codehibcazteccode

List of barcodes options

Option must be passed as a second argument in any order, and must keep the format "optionName:value", such as: {d.number:barcode(qrcode, width:300, height:100, includetext:false, scale:1)}.

Option name Type Description
width integer Width a number as millimeters. Example: {d.number:barcode(qrcode, width:300)}. To scale the barcode, it is possible to chain the formatter :imageFit, such as: {d.number:barcode(qrcode, width:300):imageFit(contain)}.
height integer Height a number as millimeters. Example: {d.number:barcode(qrcode, height:10)}. To scale the barcode, it is possible to chain the formatter :imageFit, such as: {d.urlQrCode:barcode(qrcode, height:10):imageFit(contain)}.
scale integer Quality of the barcode as a number between 1 to 10. It multiplies the width and height, creating a bigger resolution barcode. Example: {d.number:barcode(qrcode, scale:3)}
includetext boolean Show the text and takes a Boolean as value, true or false. Example: {d.number:barcode(qrcode, includetext:false)}.
textsize integer Change the text size. Example: {d.number:barcode(qrcode, textsize:20)}
textxalign string Change the alignment of the text horizontally. Takes only 4 values: left, center, right, or justify. The default value is center. Example: {d.number:barcode(qrcode, textxalign:right)}
textyalign string Change the alignment of the text vertically. Takes only 3 values: below, center, or above. The default value is below. Example: {d.number:barcode(qrcode, textyalign:above)}.
rotate string Rotate the barcode and the text. Takes only 4 values (case insensitive): N for not rotated, R for 90 degree right rotation, L for 90 degree left rotation, I of 180 degree rotation. Example: {d.number:barcode(qrcode, rotate:R)}.
barcolor string Color of bars as hexadecimal #RRGGBB (6 characters required & case insensitive). Example: {d.number:barcode(qrcode, barcolor:#1FDE25)}.
textcolor string Color of the text as hexadecimal #RRGGBB (6 characters required & case insensitive). Example: {d.number:barcode(qrcode, textcolor:#1FDE25)}.
backgroundcolor string Color of the background as hexadecimal #RRGGBB (6 characters required & case insensitive). Example: {d.number:barcode(qrcode, backgroundcolor:#1FDE25)}.
eclevel string Specify the error correction level: L for Low, M for Medium (default), Q for Quality and H for High. Option ONLY FOR QRCODES, Micro QR Code, GS1 QR Code, HIBC QR Code, or Swiss QR Code. Example: {d.number:barcode(qrcode, eclevel:Q)}.

Barcodes as font

NOT RECOMMENDED FOR USE: It is recommended to use barcodes as image.

Using font, only 4 barcodes are supported: ean8, ean13, ean128, and code39
You need to install fonts in order to use the barcode formatter:

Thanks to grandzebu.net for the fonts.

Barcodes as font example

Here is an example of generating 4 types of barcodes on a ODT document. It is working with DOCX, XLSX, ODS documents and more...

Data

const data = {
  productValueEan13: "8056459824973",
  productValueEan8: "35967101",
  productValueCode39: "GSJ-220097",
  productValueCode128: "0312345600001"
};

Template

First, insert to your template the following tags and formatters:

{ d.productValueEan13:barcode(ean13) }
{ d.productValueEan8:barcode(ean8) }
{ d.productValueCode39:barcode(code39) }
{ d.productValueCode128:barcode(ean128) }

Then, you have to apply the barcode FONT to the FIRST curly bracket (download link just above). Here is the template before rendering:

Barcode format

Result

Barcode format Result

Pictures

ENTERPRISE FEATURE v3.0.0+

Cautions: This feature only works with PDF, ODT, ODS, ODP, ODG, PPTX, XLSX, and DOCX files.

Dynamic pictures can be a public URL or a Base64 Data URI. For the 2 solutions, a temporary picture has to be inserted in the template and the tag has to be written as an alternative text. Finally, during rendering, Carbone replaces the temporary picture by the new picture provided by the tag. The image is resized as 'fillWidth' by default, learn more about the imageFit formatter to change the value.

If an error occurs for some reasons (fetch failed, image type not supported), a replacement error image is used.

The place to insert the tag on the temporary picture may change depends on the file format:

  • ODS, ODP and ODG files: set the tag on the image title
  • ODT file: set the tag on the image alternative text, image description
  • DOCX and XLSX files: set the tag either on the image title, image description, or alternative text

The accepted images types are jpeg, png, and gif. svg are working only for ODS/ODT reports.

Images exceptions

  • It is not possible to create images loops in PPTX, XLSX, and ODG files, it produces an invalid report.
  • For ODP, it is not possible to create a loop of images inside a single slide, but it is possible to create a images loops through 2 slides or more.

Image anchor

The image anchor type changes the rendering behavior and particular cases are not valid.

On Libre Office

To update the anchor type of an image on Libre Office: Right clic on the image > Properties > Type > Anchor

Anchor Type Carbone behavior
As Character It is the best choice: the replacement of a single image or a loop of images is possible.
To Page / To Paragraphe / To Character The image is floating, it not possible to create a loop of images, only a single image replacement is accepted.

On Word

To update the position type of an image on Word: Double left clic on the image > Picture Format tab > Position

Position Type Carbone behavior
In line with text It is the best choice: the replacement of a single image or a loop of images is possible.
With text wrapping The image is floating, it not possible to create a loop of images, only a single image replacement is accepted.

imageFit formatter

Cautions: This formatter only works with ODT and DOCX files.

The imageFit formatter sets how the image should be resized to fit its container.
An argument has to be passed to the formatter:

  • fillWidth : The replaced image is sized to fill the element’s content-box width. The aspect ratio does not change.
  • contain: The replaced image is scaled to maintain its aspect ratio while fitting within the element’s content-box (the temporary image).
  • fill: The replaced image is sized to fill the element’s content-box (the temporary image). The entire image will fill the box of the previous image. If the object's aspect ratio does not match the aspect ratio of its box, then the object will be stretched to fit.
{d.image:imageFit(contain)}
// or
{d.image:imageFit(fill)}
// or
{d.image} // 'fillWidth' is set by default

Picture from a public URL

Here is the data set for the example of inserting a picture into an ODT report:

{
  "dog": "http://link.to/the/picture"
}

First, insert a temporary picture on the template.
Then, write the tag {d.dog} in the alternative text of the picture.
To access to the alternative text on Libre Office: Right-click on the image > properties > alternative text.

Replace the alternative text by the corresponding tag

This will replace the temporary picture by the one linked in the "dog" data.
Finally, after Carbone rendering, the new image from the URL should appear on the report!

It is also possible to display a list of pictures by writing a loop, here is the template used in the following example: List of images with carbone step 0

here is the data set:

{
  "flags": [
    {
      "name": "French",
      "picture": "http://link.to/the/flag"
    },
    {
      "name": "German",
      "picture": "http://link.to/the/flag"
    },
    {
      "name": "Italy",
      "picture": "http://link.to/the/flag"
    }
  ]
}

Insert the first repetition tag on the description of the first picture:

List of images with carbone step 1

Then on the second picture, insert the second repetition tag [i+1]:

List of images with carbone step 2

Finally, after the Carbone rendering, the list of images appears on the report!

List of images with carbone step 3

Picture from a base64 Data URI

Before continuing, check out this documentation to learn more about Data URIs.

Here is the data set for the example of inserting a picture into an ODS report:

{
  "frenchFlagImage": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQABLAEsAAD/4QGERXhpZgAATU0AKgAAAAgACQEPAAIAAAAGAAAAegEQAAIAAAANAAAAgAESAAMAAAABAAEAAAEaAAUAAAABAAAAjgEbAAUAAAABAAAAlgEoAAMAAAABAAIAAAExAAIAAAAUAAAAngEyAAIAAAAUAAAAsodpAAQAAAABAAAAxgAAAABDYW5vbgBDYW5vbiBFT1MgNkQAAAAAASwAAAABAAABLAAAAAFBZG9iZSBQaG90b3Nob3AgNy4wADIwMTc6MTE6MjAgMTE6MzE6MTQAAAmCmgAFAAAAAQAAATiCnQAFAAAAAQAAAUCIJwADAAAAAgDIAACQAwACAAAAFAAAAUiSCgAFAAAAAQAAAVygAQADAAAAAQABAACgAgAEAAAAAQAAAGSgAwAEAAAAAQAAAD6kNAACAAAAFwAAAWQAAAAAAAAAAQAAAFAAAAAEAAAAATIwMTY6MDM6MjYgMTM6MDg6MDcAAAAAaQAAAAFFRjI0LTEwNW1tIGYvNEwgSVMgVVNNAAD/4Qn2aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiBwaG90b3Nob3A6RGF0ZUNyZWF0ZWQ9IjIwMTYtMDMtMjZUMTM6MDg6MDciIHhtcDpNb2RpZnlEYXRlPSIyMDE3LTExLTIwVDExOjMxOjE0IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCA3LjAiLz4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+AP/tAGBQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAJxwBWgADGyVHHAIAAAIAAhwCPAAGMTMwODA3HAI3AAgyMDE2MDMyNgA4QklNBCUAAAAAABDW6DtHA0U5WqoLeQsJGPlt/8AAEQgAPgBkAwERAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAgICAgICAgICAwMDAwMDAwMDA//bAEMBAQEBAQEBAQEBAQICAQICAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA//dAAQADf/aAAwDAQACEQMRAD8A/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//Q/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//R/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//S/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//T/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//U/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//V/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//W/nfr/twP8vwoAKALFp/x9W3/AF8Q/wDoxazrfwqv+F/ka0P49D/r5H/0pH+lR/wQt/5RQfsbf9iD4h/9WH4xr/lf/aEf8pjeOP8A2MaP/qFhj/QXw5/5IvIv+vcv/Tkz9aK/jM+2CgAoA/lN/wCDiL/kr37MX/ZJ/i9/6nfwsr+KPpdfBw//ANeJ/wDp6kf72fsef+TfeOP/AGPsp/8AUHMz+dGv4WP9jAoAKACgD//Z"
}

First, insert a temporary picture on the template.
Then, write the tag {d.frenchFlagImage} in the title picture property.
To access to the title on Libre Office: Right-click on the image > Description > Title.

picture from a base64 data URI image

After the Carbone rendering, the new image from the data URI should appear on the report!

Picture Example - Word document

Prepare a dataset with a public URL or a Data URI corresponding to the image.

URL:

{
  "image": "http://link.to/you/picture"
}

Data URI:

{
  "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
}

The first step is to update the alternative text of the image and set the {d.image} tag.

Step 1 Word Dynamic picture

Then the rendering can tested in Carbone studio by adding the corresponding value in the JSON data field.

Step 2 Word Dynamic picture

Picture Example - Special Shape

It is possible to insert dynamic Picture as Shapes for DOCX and PDF document, such as rectangle with rounded corners, triangle, oval, arrow and the whole DOCX library. Here are the steps to insert a dynamic picture as Shape:

  1. On Microsoft Word, insert a shape into the document body: Click on the Insert tab > Shapes icon > Select the shape you need.
  2. Draw the shape into the document body: To modify the angle radius of rounded rectangles in Microsoft Word, you can utilize the yellow handle located at the top left corner of the shape. Simply drag this yellow handle to adjust the roundness of the corners to your desired angle.
  3. Fill the shape with an image: Click on the Shape format > Shape fill more icon > Pictures... > Select a picture from your computer, it will be used as placeholder image for Carbone. The image must be a JPG or PNG format.
  4. Set the Carbone Tag: To dynamically insert a picture into the document, you need to include a Carbone tag in the alternative text of the shape. Follow these steps: right-click on the shape, select View Alt Text..., and insert the Carbone tag.
  5. Generate the document with Carbone: The picture from your JSON dataset will be incorporated into the template to produce a document.
  6. OPTION: Change the image dimension by using the imageFit formatter: The imageFit formatter sets how the image should be resized to fit its container.
Tutorial to create dynamic images as Shapes (pictures with rounded corners, or Oval pictures)

HTML

ENTERPRISE FEATURE UPDATEDv4.1.0+

The :html formatter renders HTML content on DOCX, ODT and PDF documents only.

It is possible to render the following tags: <br>, <b>, <strong>, <i>, <em>, <u>, <s>, <p>, <a href="URL">, <ol>, <ul>, <li>, <del> and <img>. Unsupported tags, attributes and styles are skipped and not rendered for now.

Important notes:

  • When the anchor tag <a href="URL"> is included, Carbone has to verify the URL format before injecting it into the document. Learn more about hyperlink validation.
  • If a font family or/and a font size is applied on a HTML tag, the content will be rendered with the same font properties. Carbone doesn't keep text alignment on the rendered HTML content.
  • When the HTML tag is placed inside a paragraph and mixed with other content, the content will be pushed in a new paragraph above or below the rendered HTML, e.g. HTML Example 2.
  • To render HTML lists, such as <ol>/<li> or <ul>/<li>, create a bullet list on the template then delete it, then the generated report will render the list. A new Carbone version is coming to no longer do this operation.
  • NEW The image tag <img> is supported and rendered into DOCX/ODT/PDF documents.
    • The image source attribute can be an URL or Data-URL, such as <img src="https://carbone.io/img/favicon.png"/> or <img src="data:image/jpeg;base64,/9j...."/>
    • The image size is rendered based on width and height attributes provided by the HTML tag, such as <img src="" width="300" height="100"/>. Values must be pixels. If width or height attributes are missing, the size of 5cm (1.96in) is applied by default while retaining the image aspect ratio.

HTML Example 1

Data

{
  "name" : '<b>raptor</b>',
  "description" : 'The engine is <u>powered</u> by <i>cryogenic liquid methane</i><br>and<br><b><i>liquid oxygen</i></b> (LOX),<br><s>rather than the RP-1 kerosene and LOX</s>.'
}

Template

{d.name:html}
{d.description:html}

Result

raptor
The engine is powered by cryogenic liquid methane
and
liquid oxygen (LOX),
rather than the RP-1 kerosene and LOX.

HTML Example 2

Data

{
  "name" : 'Banana',
  "description" : '<b>is an elongated, edible fruit</b>'
}

Template

The famous fruit {d.name} {d.description:html}, botanically a berry.

Result

The famous fruit Banana
is an elongated, edible fruit
, botanically a berry.