Which Carbone service should I use to generate reports?
CarboneJS is an open source document generator, it provides:
* Enterprise Features (dynamic images, charts, colors, et cetera...): are available through the Carbone Cloud API or Carbone On-premise.
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.
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.
Sandbox Plan
plan is available, giving you 100 credits per month. If you need more credits, you can upgrade your subscription.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}
Upload a template into your personal storage space with one of the two solutions:
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.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.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.
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:
Voilà, your first document is generated! 🥳
Next steps:
Carbone studio is a Web interface used to:
Carbone Studio is available at: https://studio.carbone.io.
Read our tutorial on the Carbone help center.
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:
The live reload is not available on Firefox because the browser does not support the Native File System API
for now.
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.
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.
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.
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
{d.firstname} {d.lastname}
{d.number:formatN}
to format a number, many formatters are available!{d.list[i].total} {d.list[i+1]}
{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:
{
d
.name
: it can be infinite object depth, such as .object.list[i].value
}
Combined with features of LibreOffice™ or MS Office™, you can easily create documents with:
Schema to summarize how Carbone works:
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.
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 |
idml |
Community Features | ||||||||||
Substitutions | ||||||||||
Repetitions | ||||||||||
Formatters | ||||||||||
Translations | ||||||||||
Enterprise Features | ||||||||||
Conditions | ||||||||||
Aggregators | ||||||||||
Colors | ||||||||||
Pictures | ||||||||||
Charts | ||||||||||
Barcodes | ||||||||||
Hyperlinks | ||||||||||
HTML | ||||||||||
Forms |
Caption
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.
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:
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.Tools
> AutoCorrect
> Localized Options
. Then make sure the "replace" checkboxes for single and double quote are disabled.To use a specific Font on a report, based on each services:
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.
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.
{
"firstname": "John",
"lastname": "Doe"
}
Hello {d.firstname} {d.lastname} !
Hello John Doe !
If a dataset contains sub-objects, Carbone can access them using the Dot Notation to go deeper in the object tree.
{
"firstname": "John",
"type": {
"id": 1,
"name": "human"
}
}
{d.firstname} is a {d.type.name}
John is a human
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:
[i=0]
or [0]
.[i=1]
or [1]
An array is reachable using the Square Bracket Notation []
.
[
{ "movie": "Inception" },
{ "movie": "Matrix" },
{ "movie": "Back to the future" }
]
My preferred movie is {d[i=1].movie}
My preferred movie is Matrix
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.
[
{ "name": "Inception", "year": 2010 },
{ "name": "Matrix", "year": 1999 },
{ "name": "BTTF", "year": 1985 }
]
The movie of 1999 was {d[year=1999].name}
The movie of 1999 was Matrix
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 }
]
The oldest movie was {d[i=-1].name}.
The oldest movie was BTTF.
Access properties of the parent object with two points ..
(or more) when you need to print a parent property using filters in nested arrays:
{
"country": "USA",
"movies": [
{ "name": "Inception", "year": 2010 },
{ "name": "Matrix", "year": 1999 },
{ "name": "BTTF", "year": 1985 }
]
}
{d.movies[year=1999]..country}
USA
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.
[
{ "name": "Matrix" , "year": 1999, "meta": { "type": "SF" } },
{ "name": "The Green Mile", "year": 1999, "meta": { "type": "Drama" } }
]
The movie of 1999 was {d[year=1999, meta.type='SF'].name}
The movie of 1999 was Matrix
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).
{#... = ...}
to define an alias{$...}
to use this aliasAliases can be defined anywhere in the document, at the end, or at the beginning. They are parsed and removed by Carbone before rendering.
{
"name": "Cars",
"wheels": 4
}
{#myAlias = d.wheels}
{d.name} need {$myAlias} wheels!
Cars need 4 wheels!
Aliases accept an unlimited number of parameters between parentheses, like a function in many languages. Each parameter must start by $
.
[
{ "name": "chicken", "weekday": 1 },
{ "name": "fish" , "weekday": 2 }
]
{#mealOf($weekday) = d[weekday = $weekday].name}
Tuesday, we eat {$mealOf(2)}.
Tuesday, we eat fish.
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.
In this example, we want to travel an array of cars.
{
"cars" : [
{"brand" : "Lumeneo"},
{"brand" : "Tesla" },
{"brand" : "Toyota" }
]
}
Cars |
---|
{d.cars[i].brand} |
{d.cars[i+1].brand} |
Cars |
---|
Lumeneo |
Tesla |
Toyota |
Carbone manages nested arrays (unlimited depth). Here is an example where a whole portion of a document is repeated.
[
{
brand: "Toyota",
models: [{ size: "Prius 2" }, { size: "Prius 3" }]
},
{
brand: "Tesla",
models: [{ size: "S" }, { size: "X" }]
}
];
Models |
---|
{d[i].models[i].size} |
{d[i].models[i+1].size } |
Models |
---|
Prius 2 |
Prius 3 |
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}
.
COMMUNITY FEATURE v4.8.0+
The bidirectional loop performs iterations in 2 directions, creating additional columns and rows.
There are a few restrictions:
Here is a real life example of a bidirectional loop.
{
titles : [{ name: "Toyota" }, { name: "Tesla" }, { name: "Hopium" }],
cars : [
{ models : [ "S", "Prius 1", "Prototype" ] },
{ models : [ "3", "Prius 2", "" ] },
{ models : [ "X", "Prius 3", "" ] }
]
}
{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]} |
Toyota | Tesla | Lumeneo |
---|---|---|
Prius 1 | S | Prototype |
Prius 2 | 3 | |
Prius 3 | X |
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).
{
"cars" : [
{ "brand" : "Lumeneo" , "power" : 3 },
{ "brand" : "Tesla" , "power" : 1 },
{ "brand" : "Toyota" , "power" : 2 }
]
}
Cars |
---|
{d.cars[power].brand} |
{d.cars[power+1].brand} |
Cars |
---|
Tesla |
Toyota |
Lumeneo |
A custom iterator can be used to select distinct rows according to the attribute's value.
[
{ type: "car", brand: "Tesla" },
{ type: "plane", brand: "Airbus" },
{ type: "plane", brand: "Boeing" },
{ type: "car", brand: "Toyota" }
];
Vehicles |
---|
{d[type].brand} |
{d[type+1].brand} |
Vehicles |
---|
Tesla |
Airbus |
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.
[
{ type: "car", brand: "Tesla" },
{ type: "plane", brand: "Airbus" },
{ type: "plane", brand: "Boeing" },
{ type: "car", brand: "Toyota" }
];
Cars |
---|
{d[type , i ].brand} |
{d[type+1, i+1].brand} |
Vehicles |
---|
Tesla |
Toyota |
Airbus |
Boeing |
You can use conditional operators to filter arrays: >
, <
, >=
, <=
, =
, !=
.
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.
[
{ name: "John", age: 20 },
{ name: "Eva", age: 18 },
{ name: "Bob", age: 25 },
{ name: "Charly", age: 30 }
];
People |
---|
{d[i , age > 19, age < 30].name} |
{d[i+1, age > 19, age < 30].name} |
People |
---|
John |
Bob |
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" }
];
People |
---|
{d[i , type='rocket'].name} |
{d[i+1, type='rocket'].name} |
People |
---|
Falcon 9 |
Falcon Heavy |
Filtering the loop index is also possible as the following example:
[
{ name: "Falcon 9" },
{ name: "Model S" },
{ name: "Model 3" },
{ name: "Falcon Heavy" }
];
People |
---|
{d[i, i < 2].name} |
{d[i+1, i < 2].name} |
People |
---|
Falcon 9 |
Model S |
Iterate over objects if you need keys or values:
{
myObject : {
paul : '10',
jack : '20',
bob : '30'
}
}
People name | People age |
---|---|
{d.myObject[i].att} | {d.myObject[i].val} |
{d.myObject[i+1].att} | {d.myObject[i+1].val} |
People name | People age |
---|---|
paul | 10 |
jack | 20 |
bob | 30 |
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]
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.
{
"name" : "JOHN",
"birthday" : "2000-01-31"
}
My name is {d.name:lowerCase:ucFirst}.
I was born on {d.birthday:convDate(YYYY-MM-DD, LL)}.
My name is John.
I was born on Monday 31th 2000.
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
v0.12.5+
Lower case all letters
Examples
'My Car':lowerCase() // "my car"
'my car':lowerCase() // "my car"
null:lowerCase() // null
1203:lowerCase() // 1203
v0.12.5+
Upper case all letters
Examples
'My Car':upperCase() // "MY CAR"
'my car':upperCase() // "MY CAR"
null:upperCase() // null
1203:upperCase() // 1203
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
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!"
v4.23.0+
Stringify the object/array
Examples
[{'id':2,'name':'homer'},{'id':3,'name':'bart'}]:printJSON() // "[{\"id\":2,\"name\":\"homer\"},{\"id\":3,\"name\":\"bart\"}]"
'my car':printJSON() // "\"my car\""
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
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"
v4.1.0+
It renders carriage return \r\n
and line feed \n
into documents instead of printing them as a string.
Importante notes:
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"
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 "
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"]
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"
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"
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"
NEWv4.12.0+
add a prefix to a text
params | desciption | type |
---|---|---|
textToPrepend | text to prepend | string |
Examples
'abcdef':prepend('123') // "123abcdef"
NEWv4.12.0+
Add a suffix to a text
params | desciption | type |
---|---|---|
textToAppend | text to append | string |
Examples
'abcdef':append('123') // "abcdef123"
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"
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
v4.23.2+
Translate the text using the translation dictionnary
v4.23.8+
Preserve character reference
By default, Carbone removes all forbidden characters before injecting data into XML (e.g., &, >, <, �, ...).
As a result, an injected character reference such as §
( = §
) would be transformed into &#xa7;
in XML.
This formatter prevents that transformation, preserving the character reference.
This function is useful for specific XML generation scenarios where the direct character cannot be used (e.g., non-UTF-8 charset),
and the character reference must be retained.
It accepts numeric (e.g., d
) and hexadecimal formats (e.g., 򡃯
), in either lower or upper case.
Formatters can be chained with dynamic parameters to create complex operations, check out the example.
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
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
v1.2.0+
Format number according to the locale.
Applying a number of decimals depends on the report type:
precision
parameter passed to the formatter.params | desciption | type |
---|---|---|
precision | Number of decimals | Number |
Examples
// with options = {
// "lang": "en-us"
// }
'10':formatN() // "10.000"
'1000.456':formatN() // "1,000.456"
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 €"
v1.2.0+
Add two numbers
Examples
1000.4:add(2) // 1002.4
'1000.4':add('2') // 1002.4
v1.2.0+
Substract two numbers
Examples
1000.4:sub(2) // 998.4
'1000.4':sub('2') // 998.4
v1.2.0+
Multiply two numbers
Examples
1000.4:mul(2) // 2000.8
'1000.4':mul('2') // 2000.8
v1.2.0+
Divide two numbers
Examples
1000.4:div(2) // 500.2
'1000.4':div('2') // 500.2
NEWv4.5.2+
Compute modulo
params | desciption | type |
---|---|---|
value | Y | Number |
Examples
4:mod(2) // 0
3:mod(2) // 1
NEWv4.12.0+
Get absolute value
Examples
-10:abs() // 10
-10.54:abs() // 10.54
10.54:abs() // 10.54
'-200':abs() // 200
v4.22.8+
Rounds towards closest higher integer number
Examples
10.05123:ceil() // 11
1.05:ceil() // 2
-1.05:ceil() // -1
v4.22.8+
Rounds towards closest lower integer number.
Examples
10.05123:floor() // 10
1.05:floor() // 1
-1.05:floor() // -2
UNRECOMMENDED FOR USEv1.0.0+
Converts a number to an INT
UNRECOMMENDED FOR USEv1.0.0+
Converts a number with English specifications (decimal separator is '.')
UNRECOMMENDED FOR USEv1.0.0+
Converts a number into string, keeping only
UNRECOMMENDED FOR USEv1.0.0+
Converts a number with French specifications (decimal separator is ',')
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 |
{
"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}
]
}
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()}
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
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"
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
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 |
It is a system to hide and show a part of the document, the list of formatters:
ifEqual
ifContain
ifEmpty
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.
{
"status1" : 1,
"status2" : 2,
"status3" : 3
}
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) }
one = "one"
two = "two"
three = "unknown"
ENTERPRISE FEATURE UPDATEDv4.22.10+
Use the drop
formatter to delete elements from a document. Currently, this feature is exclusively accessible for DOCX/ODT/ODS/ODP/PPTX/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,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.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.item
to drop items in a bulleted or numbered list. This option is exclusively accessible for ODP and ODT templates, and the tag must be positioned within an item.Important Notes:
drop
formatter is used, the Carbone tag value is not printed, and chained formatters are not executed.ODS
template supports only the :drop(row)
and :drop(img)
.HTML
template supports only the :drop(table)
, :drop(row)
, and :drop(p)
.PPTX
template supports only the :drop(table)
, :drop(row)
, :drop(shape)
, :drop(chart)
, :drop(img)
, and :drop(p)
:keep
is the opposite of the "drop" operation.What are the advantages of choosing drop
over hideBegin
/hideEnd
?
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.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.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.item
for items in a bulleted or numbered list. This option is exclusively accessible for ODP and ODT templates, and the tag must be positioned within an item.Important Notes:
keep
formatter is used, the Carbone tag value is not printed, and chained formatters are not executed.ODS
template supports only the :keep(row)
and :keep(img)
.HTML
template supports only the :keep(table)
, :keep(row)
, and :keep(p)
.PPTX
template supports only the :keep(table)
, :keep(row)
, :keep(shape)
, :keep(chart)
, :keep(img)
, and :keep(p)
:drop
is the opposite of the "keep" operation.What are the advantages of choosing keep
over 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:
showBegin
/showEnd
. It is recommended to only use break-lines (shift
+ enter
) between showBegin
and showEnd
(learn more).Examples
{
"toBuy" : true
}
Banana{d.toBuy:ifEQ(true):showBegin}
Apple
Pineapple
{d.toBuy:showEnd}grapes
Banana
Apple
Pineapple
grapes
Note
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:
hideBegin
/hideEnd
. It is recommended to only use break-lines (shift
+ enter
) between hideBegin
and hideEnd
(learn more).Examples
{
"toBuy" : true
}
Banana{d.toBuy:ifEQ(true):hideBegin}
Apple
Pineapple
{d.toBuy:hideEnd}grapes
Banana
grapes
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 |
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 |
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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 |
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}
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
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') // []
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.
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 | 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"
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') // 2628000000
'P1Y2M3DT4H5M6S':formatI('hour') // 10296.085
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"
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"
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"
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"
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 Yearquarter(s)
or Q
Quartermonth(s)
or M
Month (January as 0, December as 11)year(s)
or y
Yearhour(s)
or h
Hourminute(s)
or m
Minutesecond(s)
or s
Secondmillisecond(s)
or ms
Millisecondparams | 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
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"
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 |
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 |
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.
The following example is a report translated into French. It is possible to embed translation tags within other tags.
{
"apples": "Pommes",
"meeting": "rendez-vous",
"monday": "lundi",
"tuesday": "mardi",
}
data = {
id: 2
}
options = {
lang: "fr"
};
{t('meeting')}
{t('apples')}
{d.id:ifEQ(2):show( {t('monday')} ):elseShow( {t('tuesday')} )}
rendez-vous
pommes
lundi
ENTERPRISE FEATURE NEWv4.0.0+
Generate dynamic charts into documents with two solutions:
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:
{bindChart()}
tag is required (details and examples on the LibreOffice tutorial).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
The tutorial shows how to create a line chart within a DOCX/PDF document, it will represent the weather temperature during a short period.
{
"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
}
]
}
Insert
tab > Chart
Menu > Line
> 2D Line
.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.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.
{
"cheeses": [
{
"type": "Camembert",
"purchasedTonnes": 47503
},
{
"type": "Emmental",
"purchasedTonnes": 152468
},
{
"type": "Compté",
"purchasedTonnes": 32925
},
{
"type": "Goat's Buchette",
"purchasedTonnes": 32095
},
{
"type": "Mozzarella",
"purchasedTonnes": 31066
}
]
}
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.
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
".
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.
Generate the report as PDF or DOCX, and the Pie is filled!
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:
"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:"hardRefresh": true
."hardRefresh": true
.cs
, de
, en
, es
, fi
, fr
, it
, ja
, ko
, pl
, pt-br
, ro
, ru
, si
, th
, zh
.{d.chartOptions:chart}
with the formatter :chart
. Without :chart
, the attribute {d.chartOptions}
will be considered as an image.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"
}]
}
}
}
The report is generated 🥳 Now, you can build any type of charts:
If you experience any limitations, please contact us on the chat.
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:
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
returns the sum of all values in a set.
{
"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}
]
}
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}
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:
{d.departments[i].people[].salary:aggSum}
{d.departments[i].people[].salary:aggSum(.i)}
(alternative){d.departments[i].people[i].salary:aggSum}
{d.departments[i].people[i].salary:aggSum(0)}
(alternative)Change the partition with dynamic parameters:
{d.departments[i].people[i].salary:aggSum(.age)}
{d.departments[i].people[i].salary:aggSum(.age, .gender)}
aggAvg
returns the average of a set
{
"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}
]
}
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}
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
returns the minimum value in a set
{
"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}
]
}
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}
Minimum quantity:
1
Minimum quantity by sorting "sort" values above 1:
2
Minimum quantity by multiplying the "qty" by the "sort" attribute:
4
aggMax
returns the maximum value in a set
{
"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}
]
}
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}
Maximum quantity:
10
Maximum quantity by sorting "sort" values above 1:
10
Maximum quantity by multiplying the "qty" by the "sort" attribute:
50
aggCount
returns the number of items in a set
{
"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}
]
}
Count quantity items:
{d.cars[].qty:aggCount}
Count quantity items by sorting "sort" values above 1:
{d.cars[sort>1].qty:aggCount}
Count quantity items:
6
Count quantity items by sorting "sort" values above 1:
4
The cumulative sums, as known as running totals, returns the total sum of data as it grows with series.
{
"departments": [
{ "type": "1", "people": [{ "salary": 1000 }] },
{ "type": "2", "people": [{ "salary": 500 }, { "salary": 200 }] },
{ "type": "4", "people": [{ "salary": 1500 }] },
{ "type": "5", "people": [{ "salary": 200 }] }
]
}
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]}
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
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.
{
"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}
]
}
{d.cars[i].qty:cumCount()} {d.cars[i].qty:cumCount(.group)} {d.cars[i].brand}
{d.cars[i+1]}
1 1 Lu
2 2 Fa
3 3 Vi
4 1 Fa
5 2 To
6 3 Vi
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 paragraphscell
for table cellsrow
for table rowsshape
for shapes (e.g., squares, rectangles)part
for text section (the color is applied within the same styled of the element).The second argument, type
, defines the property to be modified and supports:
text
default for the text color (supported scopes: p
, cell
, row
, part
).highlight
for the background color of texts (supported scopes: p
, cell
, row
, part
). background
for the background color (supported scopes: cell
, row
, and shape
).border
for shape borders color (supported scope: shape
).Important notes:
#FF0000
or FF0000
. Carbone replaces wrong color values with light gray (#888888).: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.:color
formatter requires that a non-default style is already applied to the target element in the template (for example, a custom text color).highlight
is not managed in docx template:color
cannot be used with aliasesThe following example changes the text color to red.
{
"error": "#FF0000", // red
}
The following example change the text color conditionally: {d.test:ifEQ('OK'):show(.success):elseShow(.error):color(p)}
.
Details:
test
value is equal to "OK", the changed-color will be d.success
(color "#007700"). test
value is not equal to "OK", the color will be d.error
(color "#FF000").:color(p)
applies the color on the text of the current paragraph.{
"test": "OK",
"success": "#007700",
"error": "#FF000"
}
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:
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.result
value is not equal to "ok", the color will be d.error
.:color(row, text)
change the color on text located on the current row.{
"error": "#FF0000",
"tests": [
{ "name": "Security Training","result": "ok" },
{ "name": "Code Auditing","result": "20 Vulnerabilities found" },
{ "name": "Firewall Testing","result": "ok" }
]
}
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
.{
"error": "#FF0000",
"tests": [
{ "name": "Security Training","result": "ok" },
{ "name": "Code Auditing","result": "20 Vulnerabilities found" },
{ "name": "Firewall Testing","result": "ok" }
]
}
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:
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:
{bindColor()}
can be placed anywhere in the document, such as the header, footer, or even below a repetition [i+1].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.
data = {
color: "#FF0000", // red
color2: "#00FF00", // green
color3: "#0000FF" // blue
};
ODT template content:
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.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
}
};
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 = {
color: "darkYellow",
color2: "green",
color3: "11BBCC"
};
Microsoft
get in background the red
color, Word
get in background the magenta
color and background
get the #FFFF00
color
Each line in a table can be colorized by one color:
data = {
user: [
{
firstname: "Jean",
lastname: "Dujardin",
color: {
r: 255,
g: 0;
b: 0
}
},
{
firstname: "Omar",
lastname: "Sy",
color: {
r: 0,
g: 255,
b: 0
}
}
]
}
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.
ENTERPRISE FEATURE UPDATEDv3.3.0+
Editable text fields are used for collecting user-provided information. Important notes:
form
> Text box
{d.value}
.Editable checkboxes provides users the ability to choose between two distinct values, and can be used in complex forms and checklists, multiple solutions:
Note: it is not possible to create a list of checkboxes, otherwhise an error is returned.
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
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:
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 printedENTERPRISE 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:
%256B
.https://carbone.io{d.url}
. In this case, Carbone removes the https://carbone.io
part and keep only {d.url}
.{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:
http://
or https://
. If it doesn't exist Carbone, adds the protocol https://
..com
, .org
, .fr
, other...In case of error, the link is replaced by: https://carbone.io/documentation.html#hyperlink-validation
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}
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:
Both solution needs the tag :barcode(type)
in the template, check examples below for each solution. The prototype of the formatter:
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 |
Generating barcode as image is the same logic as dynamic pictures, it goes into 3 steps:
{d.value:barcode(type)}
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.
This example shows how to insert 3 barcodes as images: ean13
, qrcode
, and gs1
.
First retrieve your barcode data as JSON from you database.
const data = {
urlQrCode: "http://carbone.io/",
productCodeBarEan13: "2112345678900",
productGs1: "(01)95012345678903(3103)000123"
};
Edit your template with your text editor, insert 3 temporary images, and for each image, write as an alternative text Carbone tags:
{d.urlQrCode:barcode(qrcode)}
{d.productCodeBarEan13:barcode(ean13)}
{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.
Barcode name | Type |
---|---|
EAN-5 (5 digit addon) | ean5 |
EAN-2 (2 digit addon) | ean2 |
EAN-13 | ean13 |
EAN-8 | ean8 |
UPC-A | upca |
UPC-E | upce |
ISBN | isbn |
ISMN | ismn |
ISSN | issn |
Code 128 | code128 |
GS1-128 | gs1-128 |
GS1-14 | ean14 |
SSCC-18 | sscc18 |
Code 39 | code39 |
Code 39 Extended | code39ext |
Italian Pharmacode | code32 |
Pharmazentralnummer (PZN) | pzn |
Code 93 | code93 |
Code 93 Extended | code93ext |
Interleaved 2 of 5 (ITF) | interleaved2of5 |
ITF-14 | itf14 |
Deutsche Post Identcode | identcode |
Deutsche Post Leitcode | leitcode |
GS1 DataBar Omnidirectional | databaromni |
GS1 DataBar Stacked | databarstacked |
GS1 DataBar Stacked Omnidirectional | databarstackedomni |
GS1 DataBar Truncated | databartruncated |
GS1 DataBar Limited | databarlimited |
GS1 DataBar Expanded | databarexpanded |
GS1 DataBar Expanded Stacked | databarexpandedstacked |
GS1 North American Coupon | gs1northamericancoupon |
Pharmaceutical Binary Code | pharmacode |
Two-track Pharmacode | pharmacode2 |
Code 25 | code2of5 |
Industrial 2 of 5 | industrial2of5 |
IATA 2 of 5 | iata2of5 |
Matrix 2 of 5 | matrix2of5 |
COOP 2 of 5 | coop2of5 |
Datalogic 2 of 5 | datalogic2of5 |
Code 11 | code11 |
BC412 | bc412 |
Codabar | rationalizedCodabar |
USPS Intelligent Mail | onecode |
USPS POSTNET | postnet |
USPS PLANET | planet |
Royal Mail 4 State Customer Code | royalmail |
AusPost 4 State Customer Code | auspost |
Royal Dutch TPG Post KIX | kix |
Japan Post 4 State Customer Code | japanpost |
MSI Modified Plessey | msi |
Plessey UK | plessey |
Telepen | telepen |
Telepen Numeric | telepennumeric |
PosiCode | posicode |
Codablock F | codablockf |
Code 16K | code16k |
Code 49 | code49 |
Channel Code | channelcode |
Flattermarken | flattermarken |
Custom 1D symbology | raw |
Custom 4 state symbology | daft |
Miscellaneous symbols | symbol |
PDF417 | pdf417 |
Compact PDF417 | pdf417compact |
MicroPDF417 | micropdf417 |
Data Matrix | datamatrix |
Data Matrix Rectangular | datamatrixrectangular |
Data Matrix Rectangular Extension | datamatrixrectangularextension |
Royal Mail Mailmark | mailmark |
QR Code | qrcode |
Swiss QR Code | swissqrcode |
Micro QR Code | microqrcode |
Rectangular Micro QR Code | rectangularmicroqrcode |
MaxiCode | maxicode |
Aztec Code | azteccode |
Compact Aztec Code | azteccodecompact |
Aztec Runes | aztecrune |
Code One | codeone |
Han Xin Code | hanxin |
DotCode | dotcode |
Ultracode | ultracode |
GS1 Composite 2D Component | gs1-cc |
EAN-13 Composite | ean13composite |
EAN-8 Composite | ean8composite |
UPC-A Composite | upcacomposite |
UPC-E Composite | upcecomposite |
GS1 DataBar Omnidirectional Composite | databaromnicomposite |
GS1 DataBar Stacked Composite | databarstackedcomposite |
GS1 DataBar Stacked Omnidirectional Composite | databarstackedomnicomposite |
GS1 DataBar Truncated Composite | databartruncatedcomposite |
GS1 DataBar Limited Composite | databarlimitedcomposite |
GS1 DataBar Expanded Composite | databarexpandedcomposite |
GS1 DataBar Expanded Stacked Composite | databarexpandedstackedcomposite |
GS1-128 Composite | gs1-128composite |
GS1 Data Matrix | gs1datamatrix |
GS1 Data Matrix Rectangular | gs1datamatrixrectangular |
GS1 QR Code | gs1qrcode |
GS1 DotCode | gs1dotcode |
HIBC Code 39 | hibccode39 |
HIBC Code 128 | hibccode128 |
HIBC Data Matrix | hibcdatamatrix |
HIBC Data Matrix Rectangular | hibcdatamatrixrectangular |
HIBC PDF417 | hibcpdf417 |
HIBC MicroPDF417 | hibcmicropdf417 |
HIBC QR Code | hibcqrcode |
HIBC Codablock F | hibccodablockf |
HIBC Aztec Code | hibcazteccode |
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)} .
|
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:
ean8
or ean13
barcode, click to download.ean128
barcode, click to download.code39
barcode, click to download.Thanks to grandzebu.net for the fonts.
Here is an example of generating 4 types of barcodes on a ODT document. It is working with DOCX, XLSX, ODS documents and more...
const data = {
productValueEan13: "8056459824973",
productValueEan8: "35967101",
productValueCode39: "GSJ-220097",
productValueCode128: "0312345600001"
};
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:
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 titleODT
file: set the tag on the image alternative text, image descriptionDOCX
and XLSX
files: set the tag either on the image title, image description, or alternative textThe accepted images types are jpeg
, png
, and gif
. svg
are working only for ODS/ODT reports.
PPTX
, XLSX
, and ODG
files, it produces an invalid report.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.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. |
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
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.
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:
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:
Then on the second picture, insert the second repetition tag [i+1]
:
Finally, after the Carbone rendering, the list of images appears on the report!
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.
After the Carbone rendering, the new image from the data URI should appear on the report!
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.
Then the rendering can tested in Carbone studio by adding the corresponding value in the JSON data field.
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:
Insert
tab > Shapes
icon > Select the shape you need.View Alt Text...
, and insert the Carbone tag.imageFit
formatter: The imageFit formatter sets how the image should be resized to fit its container.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:
<a href="URL">
is included, Carbone has to verify the URL format before injecting it into the document. Learn more about hyperlink validation.<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.<img>
is supported and rendered into DOCX/ODT/PDF documents.<img src="https://carbone.io/img/favicon.png"/>
or <img src="data:image/jpeg;base64,/9j...."/>
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.{
"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>.'
}
{d.name:html}
{d.description:html}
raptor
The engine is powered by cryogenic liquid methane
and
liquid oxygen (LOX),rather than the RP-1 kerosene and LOX.
{
"name" : 'Banana',
"description" : '<b>is an elongated, edible fruit</b>'
}
The famous fruit {d.name} {d.description:html}, botanically a berry.
The famous fruit Banana
is an elongated, edible fruit
, botanically a berry.