This is an n8n community node. It lets you use the Carbone JS library in your n8n workflows.
Carbone is a report generator that lets you render JSON data into DOCX, PDF, XLSX and more formats:
n8n is a fair-code licensed workflow automation platform.
NOTE: Carbone is licensed under the Carbone Community License (CCL), which says that "Roughly speaking, as long as you are not offering Carbone Community Edition Software as a hosted Document-Generator-as-a-Service like Carbone Cloud, you can use all Community features for free.". AFAICT, this means that this plugin must also be distributed under CCL, and that you can't install it in a N8N instance and then use it to provide document generation as a service. You can use it for "your own internal business purposes" and "value-added products or services", as long as they aren't primarily document generation services.
Installation
Operations
Compatibility
Usage
Resources
Follow the installation guide in the n8n community nodes documentation.
Must receive an input item with both $json
and $binary
keys. The $json
key may be used to compose the "context", which will be provided to the templating engine. The $binary
key should contain a DOCX document that contains a valid Carbone template.
This operation can take "advanced options", which are passed directly to Carbone's rendering engine. See Carbone's docs for information about each option. They appear in the Options dropdown, at the bottom of the Render operation:
NOTE: This operation requires LibreOffice to be installed. If using the native NPM install, you should install LibreOffice system-wide. If using the Docker images, this operation doesn't seem to work :(
This node must receive items with a binary property containing a DOCX document. The selected document will be rendered into a PDF file using the LibreOffice renderer, since according to one of the Carbone authors, "I tried to avoid LibreOffice because I wanted a tool really light and highly performant. But after many researches, I have never found a solution to convert a document more reliable than LibreOffice and which can run everywhere."
This node takes two inputs, like the Merge node. Unlike the merge node, both inputs must have the same amount of elements. The node will output a stream with the same amount of items, where every output item is the result of merging one element from Input 1
and one element from Input 2
, by position (i.e., first with first, second with second, and so on).
This plugin has been developed and tested on N8N version 0.228.2. It should work with older versions too, but it hasn't been tested.
The Render
node needs to receive data items with both JSON and binary data. The binary data is the template, and the JSON data will be the context passed to the template.
To generate these data items, you'll probably want to use a Merge Node, with Mode set to Combine and Combination Mode set to Multiplex. This generates a cross-join of all data in Input A and Input B (i.e., every possible combination of items). If you only input one item in the template, you'll get one item out for each different JSON context.
See below for an example:
The Merge node receives 3 items in its Input A, and 1 item in its input B. Every possible combination yields 3*1 = 3 output combinations, each with the same template but different JSON content.
The data items output by the node will also have JSON and binary data. The JSON data is the context that was used for rendering the document (which is not necessarily the same as the node's input $json
key), and the binary data is another DOCX document, rendered from the input Template.
The workflow above is a more complicated version of the workflow, which uses everything that this node provides:
- The rendering part is the same as above: the JSON contexts are generated somehow, the template is read from disk and replicated on every context, and then the Carbone node is used to render the documents
- Every rendered document is converted to PDF
- A "cover page" (a static PDF document) is read from disk and replicated on every PDF document (using the same Merge pattern as in the Simple Example above)
- The Merge PDF node is used to add the cover letter at the start of every PDF document
Here's how the data looks at different points in the path (look for the yellow notes with letters):
A. The original template, which refers to a property in the context called a
. Imagine that it's, say, the name of a user.
B. The document after being rendered, here with the context {"a": 1}
C. The same document, converted to a PDF file
D. The "cover page"
E. The document, now with the cover page added. Note that it has two pages, coming from two different documents
- n8n community nodes documentation
- General Carbone docs
- A short tutorial on template design
- Detailed docs on template design
NOTE: As of now, I haven't been able to make Carbone work with LibreOffice in Docker, so it probably won't work with N8N Docker deployments. If you manage to make it work, please open an issue!
As of now, if you need to convert rendered documents to PDF, I recommend either a) using native (NPM) deployments for N8N, o b) using a standalone Docker container that exposes some sort of REST API, deploying it alongside the N8N container, and using the HTTP Request node to interface with it.
May I suggest Gotenberg, just for the awesome portrait of a Gutenberg gopher? Note that I can't vouch for the security of that service, but it seems legit and active.
Using the node with a native N8N instance (i.e., one installed with npm
) is relatively easy: install LibreOffice using the OS's utilities, if required (e.g. apt install libreoffice-common
on Ubuntu), then install the node with npm
or using the N8N UI, and then use it.
Docker deployments add some complexity, since you can't necessarily rely on the NPM packages persisting across container restarts, and you need to install the LibreOffice package, if required, in the container image, not on your host OS.
Here's a checklist of changes that are required to make the node work on Docker deployments:
- Ensure that the
/home/node/.n8n
directory (on the container) is mapped to a volume. If using plain Docker, use-v host_dir_or_vol_name:/home/node/.n8n
as part of thedocker run
command (note that N8N's Docker quickstart already does this, and maps that folder to~/.n8n
on your host device). If using Docker Compose, ensure that you have an entry under thevolumes
array that has- host_dir_or_vol_name:/home/node/.n8n
as its value - Install the node as normal (i.e. by going to the
Settings>Community Nodes
page and searching forn8n-nodes-carbonejs
). Ensure that the node appears in the host-mapped volume, under thenode_modules
directory. -
If you wish to use the Convert to PDF action, you'll additionally need to add the LibreOffice system library to the Docker image and recompile it.-
Create aDockerfile
with the following contents:FROM n8nio/n8n:latest RUN apk --update --no-cache --purge add libreoffice-common
-
If using plain Docker, rundocker build . -t n8nio/n8n:latest-libreoffice
-
From there on, run N8N asdocker run <...> n8nio/n8n:latest-libreoffice
-
If using Docker Compose, change the service declaration:services: n8n: image: n8nio/n8n:latest-libreoffice # Declare a new tag build: . # Add this line
-
From there on, rundocker-compose build
before runningdocker-compose up
, or usedocker-compose up -b
-
Whenever you need to update the N8N version, remember to rundocker pull n8nio/n8n:latest
first, as otherwise the build process will use a cached base image
-
Since the LibreOffice-Carbone-N8N-Alpine stack is currently not working, I'd recommend using a standalone dedicated container to do DOCX→PDF conversions, in the spirit of small, dedicated microservices and such.
A cursory Google search turns up Gotenberg, "A Docker-powered stateless API for PDF files." As usual, do your own research, deploying untrusted containers may make your lunch disappear, and all the usual warnings.
See their docs for Docker Compose information:
services:
# Your other services (i.e. N8N)
gotenberg:
image: gotenberg/gotenberg:7
Then, use the LibreOffice module to convert files:
- Create a HTTP Request node in the N8N workflow
- Set the method to
POST
- Set the URL to
http://gotenberg:3000/forms/libreoffice/convert
- Enable the
Send Body
toggle - Set the Body Content Type to
Form-Data
- Set the Parameter Type of the first Body parameter to
n8n Binary Data
- Set the Name to
files
(it looks like it could also be something else, but set it tofiles
just to match the Gotenberg docs) - Set the Input Data Field Name to the name of the binary field holding the DOCX file (could be
data
)
See below for a screenshot of a working configuration:
By default, this configuration will override the incoming file with the response's (PDF) file. If you wish to preserve both files:
- In the HTTP Request node, click the Add Option button at the bottom, then click the Response option
- In the new section that appears, set the Response Format to
File
- In the new Put Output in Field textfield that appears, set it to a name that is different to the name of the input file (e.g., if the input file is in
data
, set it todata_pdf
or something)
More information here.
You must have a local (non-Docker) installation of N8N.
- Clone this repo
npm i
- Make changes as required
npm run build
npm link
- Go to N8N's install dir (
~/.n8n/custom/
on Linux), then runnpm link n8n-nodes-carbonejs
n8n start
. If you need to start the N8N instance on another port,N8N_PORT=5679 n8n start
- There's no need to visit the web UI to install the node: it's already installed since it lives in the correct directory
- After making changes in the code and rebuilding, you'll need to stop N8N (Ctrl+C) and restart it (
n8n start
) - For faster changes, instead of rebuilding the code each time, run
npm run dev
. This will start the TypeScript compiler in watch mode, which will recompile the code on every change. You'll still need to restart N8N manually, though.
Use when someone has reported an issue, to give that person a way to test the fix without having to release a version that may not fix their issue. Especially useful when the issue can't be reproduced locally.
- Temporarily edit the
package.json
file to another version, e.g.v1.2.3-bugfix123
- Run
npm ci && npm run build && npm publish --tag prerelease
- This will build and upload a new release to NPM, without marking it as the latest release! This is important because otherwise other users of the node will start getting prompts to update their installations, which isn't correct
- Tell the user that may be interested in testing the changes to force-install that new version
- If/when the user confirms that the version fixes the issue, release for everyone: see below
- Bump the version in
package.json
. We use SemVer. - Add an entry to the top of
CHANGELOG.md
describing the changes. - Push changes, open a PR and merge it to master branch (if developing on another branch)
- Create a release. This will kick off the CI which will build and publish the package on NPM