The docker image allows you to run docxtemplater with all 18 docxtemplater modules contained in the ENTREPRISE plan, without having to configure them. The modules are already configured to sane defaults (and customizable) and work well with each other.
We have developped manys sdks in multiple languages to allow you to generate your documents from those languages :
After downloading the docker image, you can find all sdks in the sdk/ folder for example : sdk/php/.
The API exposes an HTTP POST route to do the generation that takes as input the template file and the data to be used for the generation.
The API outputs the generated file as docx, pptx or xlsx.
The docker image can be used to generate documents from other languages (Java, .Net), since it only requires to do one HTTP Call.
View the OpenAPI Specification of the API
This example requires you to have input.docx, data.json and it exports output.docx
# creates a new file descriptor 3 that redirects to 1 (STDOUT)
exec 3>"output.docx"
# Run curl in a separate command,
# capturing output of -w "%{http_code}" into HTTP_STATUS
# and sending the content to this command's STDOUT with -o >(cat >&3)
curl --silent \
-X POST \
-F "data=@data.json" \
-F "filecomment=This is an image file" \
-F "doc=@input.docx" \
-w "%{http_code}" \
"$host/api/v1/generate" \
-o >(cat >&3) >/tmp/httpstatus || code="$?"
It is also possible to get the list of tags in a template (no generation done, by using following script :
curl --silent \
-X POST \
-F "doc=@input.docx" \
"$host/api/v1/retrieve-tags" || code="$?"
It is possible to get the list of tags separated using the structure of the document :
curl --silent \
-X POST \
-F "doc=@input.docx" \
"$host/api/v1/retrieve-tags?format=separated" || code="$?"
This will return :
for pptx : tags separated by slide :
[
{
"slide": 1,
"tags": {
"title": {},
"description": {}
}
},
{
"slide": 2,
"tags": {
"users": {
"id": {},
"name": {},
"occupation": {},
"age": {}
}
}
}
]
for xlsx, separated by sheets :
[
{
"sheetName": "Sheet1",
"target": "xl/worksheets/sheet1.xml",
"tags": {
"name": {},
"items.length": {},
"totalPrice": {},
"discount": {},
"items": {
"name": {},
"quantity": {},
"unit_price": {},
"quantity \* unit_price.value": {}
}
}
}
]
for docx, separated by headers/footers and document :
{
"headers": [
{
"target": "word/header1.xml",
"tags": {
"last_name": {},
"first_name": {},
"phone": {},
"description": {}
}
}
],
"footers": [
{
"target": "word/footer1.xml",
"tags": {
"last_name": {},
"first_name": {},
"phone": {}
}
}
],
"document": {
"target": "word/document.xml",
"tags": {
"last_name": {},
"first_name": {}
}
}
}
It is possible to configure your instance by editing the configuration.js file.
The configuration.js file provides extension points to customize the behavior of the Docxtemplater Docker server. The package comes in with a default configuration.js, all functions defined in that file are no-ops by default — they are provided as examples (in comments) and do not affect the server's behavior.
You can safely remove any function definition you don't use; their presence or absence has no impact unless you implement logic inside them.
The server maintains backwards compatibility, so configuration files written for previous versions of the Docker image will continue to function as expected.
There are different parts that you can configure.
By implementing transformExpressionParserOptions, you can add angular filters that you want to be able to use in your template :
const configuration = {
/* Other methods ... */
transformExpressionParserOptions(
options /* , requestContext */
) {
/*
* You can add custom filters like this :
*
* options.filters.upper = function(input) {
* // This condition should be used to make sure that if your input is undefined, your output will be undefined as well and will not throw an error
*
* if(!input)
* return input;
* return input.toUpperCase();
* }
*
* You can also change other configuration options
*
* Here you can change for example the evaluateIdentifier function.
*
* const hasOwnProperty = Object.prototype.hasOwnProperty;
* options.evaluateIdentifier = (tag, scope) => {
* if (hasOwnProperty.call(scope, tag) && scope[tag] != null) {
* return scope[tag];
* }
* if (tag.indexOf("_") !== -1) {
* tag = tag.replace(/_/g, ".");
* if (hasOwnProperty.call(scope, tag) && scope[tag] != null) {
* return scope[tag];
* }
* }
* };
*/
return options;
},
};
By implementing configureModule, you can modify the configuration of a given module.
const configuration = {
/* Other methods ... */
/*
* Configures specific Docxtemplater modules
* @param {string} moduleName - The name of the module to configure
* @param {Object} moduleOptions - The options for the specified module
* @returns {Object} The configured module options
*/
configureModule(moduleName, options) {
/*
* List of modules that can be configured (moduleName) :
*
* "xlsx", "html-xlsx", "html-pptx", "pptx-sub", "table-merge", "word-run",
* "table-vertical", "table-grid", "paragraph-placeholder", "html", "qr", "chart",
* "styling", "footnotes", "table", "subsection", "subtemplate", "segment",
* "table-grid-pptx", "slides", "image", "error", "meta"
*
* Here you can configure each of the docxtemplater modules
* For example to fetch urls for the image module :
*/
const https = require("https");
const http = require("http");
const Stream = require("stream").Transform;
if (moduleName === "image") {
options.getImage = function (url) {
return new Promise((resolve, reject) => {
(url.substr(0, 5) === "https" ? https : http)
.request(url, (response) => {
if (response.statusCode !== 200) {
return reject(
new Error(
`Request to ${url} failed, status code: ${response.statusCode}`
)
);
}
const data = new Stream();
response.on("data", (chunk) => {
data.push(chunk);
});
response.on("end", () => {
resolve(data.read());
});
response.on("error", (e) => {
reject(e);
});
})
.end();
});
};
}
return options;
},
};
By implementing configureDocxtemplater, you can attach additional modules that you have developped yourself.
const configuration = {
/* Other methods ... */
configureDocxtemplater(doc) {
/*
* Here you can configure the docxtemplater instance before compiling/rendering
* For example :
*
* doc.attachModule(new MyCustomModule());
*/
return doc;
},
};
By implementing postrender or prerender, you can run some code right before or right after the rendering of your document.
For example, you could use :
const configuration = {
/* Other methods ... */
prerender(doc, modulesObject) {
/*
* The below condition is necessary, because some modules are not
* available depending on the filetype (txt/docx/pptx/xlsx)
*/
if (modulesObject.meta) {
/*
* Here you can run code right before rendering
* Right before doc.renderAsync(data)
*/
modulesObject.meta.readonly();
}
},
postrender(doc, modulesObject) {
/*
* The below condition is necessary, because some modules are not
* available depending on the filetype (txt/docx/pptx/xlsx)
*/
if (modulesObject.meta) {
/*
* Here you can run code right after rendering
* Right after doc.renderAsync(data)
*/
modulesObject.meta.dropLastPageIfEmpty();
}
},
};
Filters make it possible to transform data before showing it to the user using the same concept as unix pipes : it follows the syntax of { data | filterName:args }.
| Input | Output | Description |
|---|---|---|
| {-2 | abs } | 2 | Get absolute value (always positive) |
| {2.5 | round } | 3 | Rounding |
| {2.5 | ceil } | 3 | Ceil (get value always rounded up) |
| {2.3 | ceil } | 3 | Ceil (get value always rounded up) |
| {2.8 | floor } | 2 | Floor (get value always rounded down) |
| {2.8 | toFixed } | '2' | Stringify value with optional precision |
| {2.8 | toFixed } | '2' | Stringify value with optional precision |
| {2.855 | toFixed:1 } | '2.8' | Stringify value with optional precision |
| Input | Output | Description |
|---|---|---|
| {["a", "b"] | join:"," } | "a,b" | Join array of strings using a delimiter |
| {"a b" | split:" " } | ["a", "b"] | Split string by a delimiter |
| {"edgar" | upperCase } | "EDGAR" | Uppercase a string |
| {"edgar" | upper} | "EDGAR" | Uppercase a string |
| {"Edgar" | lowerCase } | "edgar" | Lowercase a string |
| {"Edgar" | lower} | "edgar" | Lowercase a string |
| {" abc " | trim } | "abc" | Remove spaces at start and end |
| {" abc " | trimStart } | "abc " | Remove spaces at start |
| {" abc " | trimEnd } | " abc" | Remove spaces at end |
{ -2 | abs } => return 2
| Input | Output | Description |
|---|---|---|
| {[1,2,3] | min } | 1 | Smallest value |
| {[1,2,3] | max } | 3 | Largest value |
| {[1,2,3] | sum } | 6 | Sum of multiple numbers |
| {users | sumBy:"age" } | 120 | Sum by key |
| {[1,2,3] | mean } | 2 | Mean of a set of values |
| {users | meanBy:"age" } | 30 | Mean by key |
| Input | Output | Description |
|---|---|---|
| { _now_ } | Returns the current date | |
| { "2022-01-01T16:00:43+01:00" | formatDate:"YY-M-D" } | 2022-1-1 | YY means Year, M and D for month and date |
| { "2022-01-01T16:00:43+01:00" | formatDate:"h:mm:ss" } | 16:00:43 | Shows the time part |
| { "2022-01-01T16:00:43+01:00" | addDays:2 | formatDate:"YY-M-D" } | 2022-1-3 | Adds two days to a date |
| { "2022-01-01T16:00:43+01:00" | subtractDays:2 | formatDate:"YY-M-D" } | 2021-12-30 | Subtracts two days from a date |
| { "2022-01-01T16:00:43+01:00" | addMonths:2 | formatDate:"YY-M-D" } | 2022-3-1 | Adds two months to a date |
| { "2022-01-01T16:00:43+01:00" | subtractMonths:2 | formatDate:"YY-M-D" } | 2021-11-1 | Subtracts two months to a date |
| { "2022-01-01T16:00:43+01:00" | addYears:2 | formatDate:"YY-M-D" } | 2024-1-1 | Adds two years to a date |
| { "2022-01-01T16:00:43+01:00" | subtractYears:2 | formatDate:"YY-M-D" } | 2020-1-1 | Subtracts two years from a date |
| Input | Output | Description |
|---|---|---|
| {users | first } | { name: "John" } | Get first element in array |
| {users | last } | { name: "Mary" } | Get last element in array |
| {users | where:"age>18" } | filteredUsers | Filter a collection using an expression |
| {firstUser | get:"name" } | "John" | Get a value using an expression. Can also be written as {firstUser.name}, but can be useful for long expressions |
| {users | sortBy:"age" } | sortedUsers | Sort by a specific field |
With leftJoin, rightJoin, innerJoin, fullJoin, you can create joins between two datasets.
This will map the "id" property of a user together with the "orderId" property of an order.
In this sample, the user will be available as the "left" property, and the order as the "right" property.
When you use a join, a "left" and a "right" object is created (the right object can be empty if a user has no orders).
The values that start with _ and end with _ are special "magic values".
Currently, the only value that exists is _now_, which will return the current date which can be formatted like this :
Any other identifier with the pattern _identifier_ is reserved for future API versions. Examples include:
_xxx__time_Note: Attempting to use reserved magic values that haven't been implemented might cause issues if you upgrade later on.
You can use the following env variables :
MAX_FILES_COUNT [default 6] allows you to control how many files can be sent to the server for templating in a request, this is useful if you send multiple subtemplates along with your main doc file. This can be set to "-1" to allow an infinite number of files.By default, the docker container will store :
/var/docxstore/:uuid/doc => contains the docx file for a given generationAll three of this things can be set to not be logged.
STORE_TEMPLATES env variable to false.LOG_REQUESTS env variable to false.LOG_ERRORS env variable to false.So if you want to stop storing any kind of logs, do the following :
STORE_TEMPLATES=false LOG_REQUESTS=false LOG_ERRORS=false ./run.bash
You can host the docker image on an EC2 Amazon instance for example, or on a virtual machine that you own.
Recommended specs would be :
A decent CPU, the docker image uses only one core (no parallelism in docxtemplater), so you don't "need" a 16 CPU server. (But you can have one instance of the docker image for each CPU if you expect very high load). Docxtemplater is CPU bound, meaning that to improve the speed of your docker instance, the most useful thing to do is to upgrade your CPU
2GB of Ram or more for each instance of the docker image
SSD and good network quality cannot hurt as well.
The algorithm for image sizing since version 3.21.0 will :
Calculate the intrinsic image size in pixel of the image coming from your data source.
Calculate the width and height of the container of the {%image} text.
It will then scale the image down if the width of the intrinsic image exceeds that of its container. It will not scale the image up to avoid scaled up images with low quality.
You can also decide to scale up or down some images using following syntax :
{%image | scale:2} If the intrinsic image size is 100px/100px, it will scale it up to 200px/200px.
{%image | scale:.5} If the intrinsic image size is 100px/100px, it will scale it down to 50px/50px.
{%image | maxWidth:100} This will set the maxWidth to 100px, hence the image will not be able to stretch over more than 100px in the word document. If the intrinsic image size is 200px/200px, the final size will be 100px/100px. If the intrinsic size is 60px/60px, the image will not be scaled up, and the final size will be 60px/60px.
{%image | maxWidth:"1in"} This will set the maxWidth to 1 inch, hence the image will not be able to stretch over more than 1 inch in the word document
It is possible to use a custom certificate by adding your certificate in same folder as the "Dockerfile", simply put your certificate in that folder with the name : ca-certificate.crt.
That certificate will then be used during the package installation and for requests done using the Imagemodule, using following data for example :
{
"image": "https://docxtemplater.com/puffin.png"
}
This option is available since version 3.40.0
For some particular templates with huge data, it will be faster to run using sync mode.
With sync mode, image loading and qrcode loading will not be possible.
You can enable sync mode by adding the "?sync=true" query parameter to the request.
openapi: 3.0.0
info:
version: 1.0.0
title: Docxtemplater docker api
description: A docker API that allows to render a docx/pptx/xlsx template with JSON data.
externalDocs:
url: https://docxtemplater.com/docker/
paths:
/api/v1/generate:
post:
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
data:
type: string
description: The JSON data to apply to the template
example: '{ "name": "John" }'
doc:
type: string
format: binary
description: The docx/pptx/xlsx template
delimiters:
type: string
description: Delimiters that should be used instead of {user}
example: "[[ ]]"
required:
- doc
- data
parameters:
- in: query
name: delimiters
schema:
type: string
description: Delimiters that should be used instead of {user}
example: "[[ ]]"
- in: query
name: imagesize
schema:
type: string
description: Max Image size, in format "<width>x<height>", in pixels, that will not be eceeded
example: "200x300"
responses:
"200":
description: Successful document generation
headers:
Content-Disposition:
schema:
type: string
example: attachment; filename="generated.docx"
x-error-location:
description: Indicates whether the returned document contains error annotations
schema:
type: boolean
example: true
content:
application/vnd.openxmlformats-officedocument.wordprocessingml.document:
schema:
oneOf:
- description: Normal generated document
type: string
format: binary
- description: Generated document with error annotations
type: string
format: binary
"400":
description: Client error (e.g., missing files or invalid input)
content:
application/json:
schema:
type: object
properties:
error:
type: object
required:
- message
- code
properties:
message:
type: string
example: "No files sent"
code:
type: string
example: "NO_FILES_SENT"
field:
type: string
nullable: true
example: "files"
"500":
description: Internal server error
content:
application/json:
schema:
type: object
properties:
error:
type: object
required:
- message
- code
properties:
message:
type: string
example: "Error getting inspect module"
code:
type: string
example: "INTERNAL_ERROR"
field:
type: string
nullable: true
example: null
/api/v1/retrieve-tags:
post:
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
doc:
type: string
format: binary
description: The docx/pptx/xlsx template
required:
- doc
parameters:
- in: query
name: delimiters
schema:
type: string
description: Delimiters that should be used instead of {user}
example: "[[ ]]"
responses:
"200":
description: The list of the tags that are defined in the docxtemplate
If you use the docker image for xlsx files and you generate a lot of contents in your xlsx, you can use the following in your configuration.js :
const configuration = {
configureModule(moduleName, options) {
if (moduleName === "xlsx") {
options.keepRowHeight = "always";
}
/* Customize other modules if needed here */
return options;
},
/*
* Customize other functions if needed here
* I recommend to keep all functions as they are by default so that
* you can easily implement them.
*/
};
This setting can be activated since version 3.41.0 of the docker version.
The underlying reason why this makes generation faster and drawbacks are explained here
You can disable a module by using the transformModules configuration key, like this for example to disable the qrcode module.
const configuration = {
transformModules(modules /* , requestContext */) {
return modules.filter((m) => m.name !== "QrcodeModule");
},
};
Since version 3.44.0, if you disable the qrcode module, the generation will be faster.
Update html-module-3.59.1
With the HTML + image module, we now utilize the latest HTML module (3.59.0) and only define the getSrcSize function.
This ensures that the width calculation of img tags within a table is now accurate. For instance, you can use:
<style>
td img {
width: 80%;
}
</style>
If you don't specify anything, the default is to apply a max-width of 100%, and by default, use the intrinsic width/height of the image itself (all png images have a width and height in pixels).
get/last/first filtersRemove all <none> Docker images when executing ./run.bash.
In the past, the <none> images would build up over time.
Fix bugs in run.bash introduced in 3.42.1.
Drop a stale file in container : /usr/app/src/app/package-lock.json
This file was never used at all, the only files that are used for install are :
/usr/app/yarn.lock and /usr/app/package.json.
This stale file would trigger warnings for AWS Inspector
Normalize error messages on /api/v1/generate route.
Now, all errors with status 400 will look like this :
{
"error": {
"message": "No files sent",
"code": "NO_FILES_SENT",
"field": "files"
}
}
Some improvements for Snyk :
stack property when having error for /api/v1/retrieve-tags for txt filesUpdate SDKS in all 12 supported languages to add following improvements :
In configuration.js :
We added a backwards-compatible change : we added an argument to all functions called "requestContext", which allows you to retrieve the data, and the underlying request (to get the query params), so that you could for example have a query param to enable/disable angular filters, …
JSON Response
We added the Content-Type: "application/json" header to our JSON responses.
Make it possible to configure all modules.
The following modules can now be configured with configureModule :
xlsx
html-xlsx
html-pptx
pptx-sub
table-merge
word-run
table-vertical
table-grid
paragraph-placeholder
html
qr
chart
styling
footnotes
table
subsection
subtemplate
segment
table-grid-pptx
slides
image
error
meta
Previously, some modules were not configurable using configuration.js
{@$pageBreakExceptLast}Bugfix error caused by invalid document if using the req.query.silent="true" parameter.
Add format=separated query parameter to /api/v1/retrieve-tags route.
Use it like this :
/api/v1/retrieve-tags?format=separated
absroundceilfloortoFixed:precisionsplitjointrimtrimStarttrimEndupperlowerminmaxsumsumBymeanmeanByleftJoinrightJoininnerJoinfullJoinaddDayssubtractDaysaddMonthssubtractMonthsaddYearssubtractYearsformatDatebuildHttpHeaderspostrender and prerender to be able to run steps for metamodule for example before render._now_ which will return the current date. All variables that start with an "" and end with an "" are now reserved for such special values.take filterslice filter with 2 argumentsorderBy filterwhere filter to work with user created filtersUpdate to not overwrite full expressions.filters object
Bugfix to allow to change the expressions.filters
Also allow to use "configureDocxtemplater" with attachModule.
It is recommended to use the new transformModules API, but now, the configureDocxtemplater will work with attachModule again :
Update this :
const CommentModule = require("./comment-module.js");
module.exports = {
/* code */
configureDocxtemplater(doc) {
doc.attachModule(new CommentModule());
/*
* Here you can configure the docxtemplater instance before compiling/rendering
* For example :
*
* doc.attachModule(new MyCustomModule());
*/
return doc;
},
};
To this :
const CommentModule = require("./comment-module.js");
module.exports = {
/* code */
transformModules(modules) {
modules.push(new CommentModule());
return modules;
},
};
Fix output of /retrieve-tags and /retrieve-structured-tags when the template is invalid, such as :
Fix CVE-2024-54152
(Upgrades to fixed package angular-expressions@1.4.3).
See this github advisory.
configureDocxtemplater APIIf you're using configureDocxtemplater to add new modules, please convert your code from :
const CommentModule = require("./comment-module.js");
module.exports = {
/* code */
configureDocxtemplater(doc) {
doc.attachModule(new CommentModule());
/*
* Here you can configure the docxtemplater instance before compiling/rendering
* For example :
*
* doc.attachModule(new MyCustomModule());
*/
return doc;
},
};
To this :
const CommentModule = require("./comment-module.js");
module.exports = {
/* code */
transformModules(modules) {
modules.push(new CommentModule());
return modules;
},
};
Update Dockerfile to allow to use a custom ca-certificate
Add to2d filter for xlsx files
Update retrieve-structured-tags and retrieve-tags to work on invalid template
Update xlsx-module-3.26.1
Update to node 20.18.1
Update to eslint@9, prettier@3.4
Update to docxtemplater@3.51.0
Update table-module-3.23.1
Update subtemplate-module-3.19.0
Update qrcode-module-3.5.0
Update pptx-sub-module-3.1.10
Update meta-module-3.13.0
Update image-module-3.29.4
Update html-module-3.55.1
Update chart-module-3.15.2
.contain filterAllow to configure following modules in the configuration.js file :
module === "chart" condition.module === "xlsx" condition.module === "html-xlsx" condition.module === "footnote" condition.module === "table-full" condition.module === "table-grid" condition.module === "pptx-sub" condition.Fix 400 Bad request when sending an XML file (that starts with <?xml version="1.0" encoding="UTF-8"?>) for text templating.
Allow to change delimiters when using Text templating
Now the output is correct
size, slice, link, chunkBy, chunk, recurignore_unknown_tags optionAdd new sdks :
Update modules :
For example :
will now create an image that spans the whole page (or the whole table cell if the tag is placed in a table).
Add POST /retrieve-tags route
Update subtemplate-module-3.13.0
Update paragraph-placeholder-module-3.4.0
Update meta-module-3.4.2
Update image-module-3.21.1
Update html-module-3.37.3
Update chart-module-3.11.0
STORE_TEMPLATES, LOG_REQUESTS and LOG_ERRORS.Add support to change maxWidth/maxHeight of a given image.
You can now write :
{%image | maxWidth:"1cm"} => max width of 1 centimeter
{%image | maxWidth:"2in"} => max width of 2 inches
{%image | maxWidth:100} => max width of 100px
{%image | scale:2} to create an image which width and height is multiplied by 2.Bugfix to allow to replace images using title field
Fix vulnerability CVE-2020-5219, see advisory and github issue for more detail.
subloop filter (for xlsx module)First publicly released version