Understanding Carbone

Ecosystem

Carbone is a solution to render report. You may encounter several terms through our documentation like Carbone, Carbone JS, Carbone Account, Carbone Render, or Carbone Studio, here is what they actually mean:

  • Carbone is the project it is not a product itself
  • Carbone JS is the open-source NodeJS library which is used to render reports available on Github. It is the core element of the project
  • Carbone Account (https://account.carbone.io) is where you manage your subscriptions, invoices and users, etc. You have to create an account in order to use our online services.
  • Carbone Render (https://render.carbone.io) is the high-availability API binded to Carbone JS with more features, you can use it freely but you will be limited to 100 renders per month. You will have to purchase a bigger plan if you need more renders.
  • Carbone Studio (https://studio.carbone.io) is a user interface binded to Carbone Render to help you organize and build your reports. You can create categories, tags, get a preview of your render. We also provide a template versioning system.

You have to create a Carbone account before using Carbone Render or Carbone Studio, click here to create your account and access services.

What's next ?

Now that you know what Carbone is and what the available services are, you should be ready to use Carbone. Choose the solution you want to use:

Data privacy

Privacy by design

We do not log data sent to Carbone Render or Carbone Studio.

Your data are used only to generate your report.

The generated report and its link are destroyed about 60 minutes after rendering. We may improve this in the near future by deleting the rendered report just after download.

Data retention

Each call to our APIs generates a log with your IP, API endpoint and performance metrics. We keep only 7 days of these logs.

Templates uploaded through Carbone Render are automatically deleted after 365 days if not used, or if you do not delete them yourself.

Templates uploaded through Carbone Studio are kept until you decide to delete them yourself.

Your personal information (name, company, address, ...) is used only for invoicing.

Data localization

Carbone Render, Carbone Studio, Backups, databases are hosted by OVH, a french company.

The DNS anycast service used by *.carbone.io redirects you automatically to the closest rendering server according to your IP.

External services

We depend on external providers, which are all GPDR-compliant. We are doing our best to send them as low information as possible.

  • Stripe.com (US) for payment
  • Crisp.chat (FR) for support and live chat
  • Sendinblue.com (FR) for emailing
  • Analytics.google.com (US) only for this website (not Carbone Render, not Carbone Studio).

Quick start

Getting started with Carbone JS

If you want to use Carbone on your own, with your own server, you are in the right place.
Just a reminder, Carbone is working only on the server side.

Minimum Requirements

  • NodeJS 8.x+
  • Runs on OSX, Linux (servers and desktop), and coming soon on Windows

Optional

  • LibreOffice server if you want to use the document converter and generate PDF. Without LibreOffice, you can still generate docx, xlsx, pptx, odt, ods, odp, html as long as your template is in the same format.

Use the Carbone JS API

1 - Install it

$ yarn add carbone
// OR
$ npm install carbone

2 - Copy-paste this code in a new JS file, and execute it with node

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 Render service.

Getting started with Carbone Render

Carbone Render is a secure, reliable and fast service to use Carbone anywhere with a REST JSON API.

Requirements

  • You have to be logged in your Carbone account. Click here to create your account or here to sign in.
  • Have a valid API token. The token is located in Carbone Account in the API access section for Admin users.

Steps

  • 1 : Upload a template
  • 2 : Render a report from the template

1 - Upload a template

You can upload your template with two methods :

  • manually using Carbone Studio : new report (Report > New). Your template is stored and will be always available for Carbone Render.
  • programmatically using Carbone Render. In that case, the template is automatically deleted if it is not used within 365 days and it does not appear in Carbone Studio.

Maybe you want to learn how to build a template, or you can use the default template with gave you when you created your account and jump to the next step.

Which product to use between Carbone Studio and Carbone Render?

If you want to store your templates in your git repository or your database of your application so templates match with data coming from your business APIs, we recommend you to use Carbone Render. You can even compute the ID of the template yourself before uploading it. You can upload an unlimited number of templates with that method. Templates are automatically deleted if not used within 365 days. You must develop a mechanism to re-upload it if you get an error when rendering the report after that time.

If you do not want to store the template in your application, we recommend you to use Carbone Studio. In that case, the number of templates is limited (you can buy more) but the template will never be deleted automatically.

2 - Render a report from the template

Your template is uploaded on Carbone Render, great. Now you have to retrieve the corresponding Template ID. The Template ID is the unique identifier of a template.

If you uploaded your template with Carbone Studio, the Template ID is in the version's information modal (in the top right corner of the screen, in the toolbar, once you opened your version). If you uploaded your template with Carbone Render, the Template ID is returned by the API, or you can compute it yourself (sha256 of file). Check the API reference to see how the hash is created.

You are ready to call the Carbone Render API to render your report.

We do not log the data you send to Carbone Render or Carbone Studio. The generated report and link are destroyed one hour after rendering. We could eventually log your data only if you allow us to do it, and only for debugging purpose.

Carbone Studio

Live reloading

The live reloading allows you to design your template faster. When you want to design a template, you upload your template once. Then you can update either your JSON or your template in Word or LibreOffice and see the results in the studio.

This feature only works on navigators based on Chrome (Google Chrome, Brave)

How to activate the live reloading?

We use a chrome feature called Native File System API. It may not be enabled on your computer so you have to do it.

To enable it, follow one those links corresponding to your navigator.

Then click on the dropdown menu next to Native File System API and select Enabled. You will need to relaunch your navigator to make it works.

Enable Native File System API

That's it. Now, upload a template in the studio and update it in Word or LibreOffice, it will automatically been update in Carbone Studio.

Building a template

This section aims to teach you how we built the templating engine and how to use it. If you do not care about technical details and you just want to learn how to build a template, you can jump here.

Philosophy

Carbone is a powerfull 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 markers.

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 does it work?

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

  • Replacing a field
  • Formatting data
  • Repeating a document portion (a table row or anything else)
  • Looping on unlimited nested arrays
  • Conditionally displaying a data based on a test expression

The syntax is easy to learn. It is like using a JSON Array or Object in Javascript.

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
  • ...

To clearly understand how Carbone works, you can take a look at the following schema

Carbone workflow

How to read this documentation

This documentation is made for report designers. There are five parts sorted by level of complexity.

Last indication, the titles of the features followed by the icon "" means it is available only on Carbone Studio or Carbone Render.

The first thing to do is to open your document editor (Libreoffice, Microsoft Word, Microsoft Excel etc.). Once you did it, you will be able to start building your template. You may need Carbone Studio to test your template.

Supported files and features list

Carbone supports a lot of document types as templates. But some of them have limited features. The Carbone team and the community are working to avoid this.

Template Formats XML xHTML ODT ODS ODP DOCX XLSX PPTX
Substitutions
Repetitions
Formatters
Translations
Dynamic colors
Dynamic Pictures
Output format xml html odt doc
docx pdf
txt
ods xlsx
xls csv
pdf txt
odp ppt
pptx pdf
odt doc
docx pdf
txt
ods xlsx
xls csv
pdf txt
odp ppt
pptx pdf

Caption

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

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

Quotation Marks Issue

When creating a template on a text editor and if you're using double quotes to filter attributes d.list[attributeValue="productstate"], you may encounter some issues. The text editor may replace quotes with ones that cannot be interpreted by Carbone. To use the right quotes, you have to perform the following manipulation and use only simple quotes:

  • MS word: On the File tab, click Options. Click Proofing, and then click AutoCorrect Options. In the AutoCorrect dialog box, do the following: Click the AutoFormat As You Type tab, and under Replace as you type, select or clear the "Straight quotes" with “smart quotes” check box. Click the AutoFormat tab, and under Replace, 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. Here is a screenshot of the Libre Office configuration window:

Quotation Marks issue config

Substitutions

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

Basic

This is the most basic example. Do I need to explain it?

Data

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

Template

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

Result

Hello John Doe !

Accessing sub-objects

Of course, if your data contains sub-objects, you 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.

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

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-directionnal loop

Data

[
  {
    brand: "Toyota",
    models: [{ name: "Prius 2" }, { name: "Prius 3" }]
  },
  {
    brand: "Tesla",
    models: [{ name: "S" }, { name: "X" }]
  },
  {
    brand: "Lumeneo",
    models: [{ name: "Smera" }, { name: "Néoma" }]
  }
];

Template

{d[i].models[i].brand} {d[i+1].models[i].brand}
{d[i].models[i].name} {d[i+1].models[i].name}
{d[i].models[i+1].name } {d[i+1].models[i+1].name }

Result

Toyota Tesla Lumeneo
Prius 2 S Smera
Prius 3 X Néoma

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 marker and all other markers 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

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

Formatters

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 is not possible to insert array as dynamic parameter.

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

String manipulation

lowerCase( )

Lower case all letters

Examples

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

upperCase( )

Upper case all letters

Examples

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

ucFirst( )

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( )

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

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 )

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( )

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( )

Convert carriage return and line feed to XML-specific code in rendered document

Compatible with odt, and docx (beta)

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 )

Slice a string with a begin and an end

params desciption type
begin Zero-based index at which to begin extraction. Integer
end Zero-based index before which to end extraction Integer

Examples

"foobar":substr(0, 3) // "foo"
"foobar":substr(1) // "oobar"
"foobar":substr(-2) // "ar"
"foobar":substr(2, -1) // "oba"

Number manipulation

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

convCurr( target, source )

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.

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 )

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 )

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 )

Format currency numbers

Currencies are defined by the locale (options.lang). It can be overwritten by
options.currencySource and options.currencyTarget for one report in Carbone.render
or globally with Carbone.set

When options.lang === 'fr-FR' the currencySource and currencyTarget equals by default EUR.

If the formatter convCurr() is used before, formatC prints the corresponding target currency used in convCurr().

By default, it prints with the currency symbol only, but you can use other output formats:

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

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 $"
// with options = {
//   "lang": "fr-fr",
//   "currency": {
//     "source": "EUR",
//     "target": "EUR",
//     "rates": {
//       "EUR": 1,
//       "USD": 2
//     }
//   }
// }
"1000.456":formatC() // "1 000,46 €"

add( )

Add two numbers

Examples

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

sub( )

Substract two numbers

Examples

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

mul( )

Multiply two numbers

Examples

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

div( )

Divide two numbers

Examples

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

int( ) // DEPRECATED

Converts a number to an INT

toEN( ) // DEPRECATED

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

toFixed( ) // DEPRECATED

Converts a number into string, keeping only decimals

toFR( ) // DEPRECATED

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

Array manipulation

arrayJoin( separator )

Flatten an array of String or Number

params desciption type
separator [optional] item separator (, by default) String

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

arrayMap( objSeparator, attributeSeparator, attributes )

Flatten an array of objects

It ignores nested objects and arrays

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 )

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

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

Barcode manipulation

AVAILABLE ONLY ON CARBONE RENDER & CARBONE STUDIO

You need to install some fonts in order to use the barcode formatter:

Thanks to grandzebu.net for the fonts.

barcode( type )

Translate a barcode to specific font code.

You have to apply the barecode font to your text in order to display the barcode.

params desciption type
type Barcode type: ean13, ean8, ean128 or code39 String

Examples

"8056459824973":barcode("ean13") // "8APGOPJ*icejhd+"
"9780201134476":barcode("ean13") // "9HSKCKB*bdeehg+"
"3754 KC 75":barcode("ean128") // "ÒEVÍ KC 75)Ó"
"DR39":barcode("ean128") // "ÑDR39xÓ"
"35967101":barcode("ean8") // ":DFJG*hbab+"
"96385074":barcode("ean8") // ":JGDI*fahe+"
"GSJ-220097":barcode("code39") // "*GSJ-220097*"
"ASDFGH-.$/+% ":barcode("code39") // "*ASDFGH-.$/+% *"

Barcodes examples

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 = {
  productEan13: "8056459824973",
  productEan8: "35967101",
  productCode39: "GSJ-220097",
  productEan128: "0312345600001"
};

Template

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

{ d.productCodeBar13:barcode(ean13) }
{ d.productCodeBar8:barcode(ean8) }
{ d.productCode39:barcode(code39) }
{ d.productEan128: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

Conditioned output

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.

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"

and( value )

NEW 2.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 )

NEW 2.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( )

NEW 2.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( )

NEW 2.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 )

NEW 2.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 )

NEW 2.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 )

NEW 2.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 )

NEW 2.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 )

NEW 2.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 )

NEW 2.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 )

NEW 2.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 )

NEW 2.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"

show( message )

NEW 2.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 )

NEW 2.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

len( )

NEW 2.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

ifEmpty( message, continueOnSuccess )

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 )

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 )

Test if a string or an array contains a value. The new formatter ifIN should be used instead of this previous 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") // []

showBegin()/showEnd()

NEW 2.0.0+

show a text block between showBegin and showEnd if condition is true

Examples

Data

{
  "toBuy" : true
}

Template

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

Result

Banana
Apple
Pineapple
grapes

hideBegin()/hideEnd()

NEW 2.0.0+

hide text block between hideBegin and hideEnd if condition is true

Examples

Data

{
  "toBuy" : true
}

Template

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

Result

Banana
grapes

Date manipulation

Carbone uses MomentJS to convert date. List of all tokens are described here.

formatD( patternOut, patternIn )

Format dates

Since 1.2.0, by default, it considers the input format is "ISO 8601"

params desciption type
patternOut output format String
patternIn [optional] input format, ISO8601 by default String

Examples

// with options = {
//   "lang": "en"
// }
"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"
// }
"2017-05-10T15:57:23.769561+03:00":formatD("LLLL") // "mercredi 10 mai 2017 12:57"
"2017-05-10 15:57:23.769561+03:00":formatD("LLLL") // "mercredi 10 mai 2017 12:57"
"20160131":formatD("LLLL") // "dimanche 31 janvier 2016 00:00"
"20160131":formatD("dddd") // "dimanche"
// with options = {
//   "lang": "fr"
// }
"20160131":formatD("dddd", "YYYYMMDD") // "dimanche"
1410715640:formatD("LLLL", "X") // "dimanche 14 septembre 2014 17:27"

convDate( patternIn, patternOut ) // DEPRECATED

Format dates

params desciption type
patternIn input format String
patternOut output format String

Examples

// with options = {
//   "lang": "en"
// }
"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 5:27 PM"
// with options = {
//   "lang": "fr"
// }
"20160131":convDate("YYYYMMDD", "LLLL") // "dimanche 31 janvier 2016 00:00"
"20160131":convDate("YYYYMMDD", "dddd") // "dimanche"

convert( ) // DEPRECATED

Token list of date formatter

Token Output
Month M 1 2 ... 11 12
Mo 1st 2nd ... 11th 12th
MM 01 02 ... 11 12
MMM Jan Feb ... Nov Dec
MMMM January February ... November December
Quarter Q 1 2 3 4
Qo 1st 2nd 3rd 4th
Day of Month D 1 2 ... 30 31
Do 1st 2nd ... 30th 31st
DD 01 02 ... 30 31
Day of Year DDD 1 2 ... 364 365
DDDo 1st 2nd ... 364th 365th
DDDD 001 002 ... 364 365
Day of Week d 0 1 ... 5 6
do 0th 1st ... 5th 6th
dd Su Mo ... Fr Sa
ddd Sun Mon ... Fri Sat
dddd Sunday Monday ... Friday Saturday
Day of Week (Locale) e 0 1 ... 5 6
Day of Week (ISO) E 1 2 ... 6 7
Week of Year w 1 2 ... 52 53
wo 1st 2nd ... 52nd 53rd
ww 01 02 ... 52 53
Week of Year (ISO) W 1 2 ... 52 53
Wo 1st 2nd ... 52nd 53rd
WW 01 02 ... 52 53
Year YY 70 71 ... 29 30
YYYY 1970 1971 ... 2029 2030
Y 1970 1971 ... 9999 +10000 +10001
Note: This complies with the ISO 8601 standard for dates past the year 9999
Week Year gg 70 71 ... 29 30
gggg 1970 1971 ... 2029 2030
Week Year (ISO) GG 70 71 ... 29 30
GGGG 1970 1971 ... 2029 2030
AM/PM A AM PM
a am pm
Hour H 0 1 ... 22 23
HH 00 01 ... 22 23
h 1 2 ... 11 12
hh 01 02 ... 11 12
k 1 2 ... 23 24
kk 01 02 ... 23 24
Minute m 0 1 ... 58 59
mm 00 01 ... 58 59
Second s 0 1 ... 58 59
ss 00 01 ... 58 59
Fractional Second S 0 1 ... 8 9
SS 00 01 ... 98 99
SSS 000 001 ... 998 999
SSSS ... SSSSSSSSS 000[0..] 001[0..] ... 998[0..] 999[0..]
Z -07:00 -06:00 ... +06:00 +07:00
ZZ -0700 -0600 ... +0600 +0700
Unix Timestamp X 1360013296
Unix Millisecond Timestamp x 1360013296123

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.

Time LT 8:30 PM
Time with seconds LTS 8:30:25 PM
Month numeral, day of month, year L 09/04/1986
l 9/4/1986
Month name, day of month, year LL September 4 1986
ll Sep 4 1986
Month name, day of month, year, time LLL September 4 1986 8:30 PM
lll Sep 4 1986 8:30 PM
Month name, day of month, day of week, year, time LLLL Thursday, September 4 1986 8:30 PM
llll Thu, Sep 4 1986 8:30 PM

Source: http://momentjs.com/docs/#/displaying/format/

Translations

Special markers {t( )} can be used to create multi-language reports.

Carbone parses all your template and generates a JSON lang file on the lang directory. Then, change the lang of your report when generating it with the option variable. If a new translation from another language is needed, insert a new JSON file on the lang directory rename it with the language abbreviation.

Example french translation

The following example is a report translated into french

lang/fr.lang

{
  "apples": "Pommes",
  "meeting": "rendez-vous"
}

Data

options = {
  lang: "fr"
};

Template

{t('meeting')}

{t('apples')}

Result

rendez-vous

pommes

Example spanish translation

The following example is a report translated into spanish:

lang/es.lang

{
  "apples": "Manzanas",
  "meeting": "cita"
}

Data

options = {
  lang: "es"
};

Template

{t('meeting')}

{t('apples')}

Result

cita

manzanas

Insert translations into formatters

Is it possible to insert translation markers into formatters. Here is a template example:

lang/fr.lang

{
  "apple": "pomme"
}

Data

let data = {
  selelectedFruit: "pomme"
};

let options = {
  lang: "fr"
};

Template

{d.selectedFruit:ifEqual({t(‘apple’)}, “this is an apple!”)}

Result

this is an apple!

Manage color dynamically

AVAILABLE ONLY ON CARBONE RENDER & CARBONE STUDIO

The marker {bindColor()} can be used to manage dynamic color on ODT, DOCX, and ODS files. It is not available for XLSX. It is possible to set text color and background color on the same text.

A bindColor marker looks like this :

{bindColor(myColorToBind, myFormat) = myVar}

Details of the arguments:

  • myColorToBind: A temporary hexadecimal color applied to a text or background, it is used by Carbone to identity the color to replace. The hexadecimal is case insensitive and the hashtag at the beginning is optional. Be aware of an exception concerning applying dynamic background color using MS Word.
  • myFormat: It is the new color format expected from the marker myVar. Here is the list of available color formats.
  • myVar: The marker that corresponds to the new color.

Color Examples

Data

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

Template

Basic dynamic color

Manage get the #00FFFF color
dynamic get the #FF00FF color
color get in background the color #0000FF.

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

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

Dynamic pictures

AVAILABLE ONLY ON CARBONE RENDER & CARBONE STUDIO

Cautions: This feature only works with ODT, ODS, 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 marker has to be written as an alternative text. Finally, during rendering, Carbone replaces the temporary picture by the new picture provided by the marker.

If an error occurs for some reason (fetch failed, image type not supported), a replacement image is used with the message "invalid image".

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

  • ODS file: set the marker on the image title
  • ODT file: set the marker on the image alternative text, image description
  • DOCX and XLSX files: set the marker 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.

It is not possible to create images loops in XLSX and ODS files, it produces an invalid report.

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:

  • 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.

If the formatter is not defined, the image is resized as 'contain' by default. Example:

{d.image:imageFit(contain)}
// or
{d.image:imageFit(fill)}
// or
{d.image} // 'contain' 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 marker {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 marker

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 marker on the description of the first picture:

List of images with carbone step 1

Then on the second picture, insert the second repetition marker [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 marker {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!

Base64 pictures

DEPRECIATED FEATURE: Use a Data URI to insert a base64 picture instead.

It is possible to provide a picture directly in the data field as a base64 Data URIs like the example below. In the data field, the first part of the Data URIs data:image/jpeg;base64, have to be removed. Do not forget the $ prefix in the data field.

A temporary picture has to be inserted in the template with the marker as temporary text. In the example, {d.dog} is going to be the marker specified in the alternative text.

Base64 images can only be set only at the root of a JSON object, not in an array nor in a child object.

Data field example with a base64 image

{
  "dog": "$base64dog",
  "$base64dog": {
    "data": "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",
    "extension": "jpg"
  }
}

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} marker.

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