Node js word template

docxtemplater

Download count Current tag CDNJS version size gzip size

docxtemplater is a library to generate docx/pptx documents from a docx/pptx template. It can replace {placeholders} with data and also supports loops and conditions. The templates can be edited by non-programmers, for example your clients.

docxtemplater is very robust because of the many fixed issues over the years, and the high quality of tests and code.

Features

Demo Site

  • Replace a {placeholder} by a value
  • Use loops: {#users} {name} {/users}
  • Use loops in tables to generate columns
  • Use conditions (if users.length>3) with angular Parsing
  • Insert custom XML {@rawXml} (for formatted text for example)

Quickstart

  • Get started with docxtemplater on nodejs
  • Get started with docxtemplater in the browser (react, angular, vue, nextjs)

Documentation

The full documentation of the latest version can be found here.

See CHANGELOG.md for information about how to migrate from older versions.

Modules

Functionality can be added with the following paid modules :

  • Image module to add a given image with the syntax: {%image};
  • Html Module to insert formatted text in a docx document with the syntax {~html};
  • XLSX Module to be able to do templating on Excel files (xlsx extension), also with loops and conditions;
  • Chart Module to replace a chart by using data from the JSON object that you give with the syntax {$chart};
  • Html-Pptx Module to insert formatted text in a pptx document with the syntax {~html};
  • Error Location Module to show the errors in the template using Word comments
  • Slides Module to create multiple slides dynamically with the syntax {:users};
  • Subtemplate Module to include an external docx file inside a given docx file with the syntax {:include doc};
  • Subsection Module to include subsections (headers/footers) from an other document with the syntax {:subsection doc};
  • Subtemplate-pptx Module to include an external pptx file inside a given pptx file with the syntax {:include doc};
  • Word-Run Module to include raw runs (<w:r>) inside the document with the syntax {r@wrun}. This makes it possible to include styled text without having to remove the enclosing paragraph like in the {@rawXml} tag;
  • QrCode Module to replace an image, keeping any existing properties;
  • Table Module to create tables from two dimensional data using the syntax {:table data};
  • Meta Module to make a document readonly, add a text watermark or update the margins;
  • Styling Module restyle a paragraph, a cell or a table depending on some data using the syntax {:stylepar style};
  • Footnotes Module to be able to add footnotes to a document using the syntax {:footnotes foot}
  • Paragraph Placeholder Module to simplify conditions that should show or hide a given paragraph using the syntax {?tag}

About docxtemplater

Docxtemplater is my main job, and has been maintained for over 8 years. Expect to get great support if you buy any modules, and also good support on the open-source version.

In this guide we’ll look at how to create a docx template and fill out that document using JavaScript. We’ll do this by using the docxtemplater package. This works in both NodeJS and React.

  1. Installing Requirements
  2. The Code
  3. Filling out the Template with JavaScript
  4. Direct Values
  5. Styling
  6. Lists
  7. Table Rows
  8. Conditional Rendering
  9. Helpful Tips
    • Too Much Spacing
    • Error Finding 1
    • Error Finding 2
    • Tag Names

Installing Requirements

Install the docxtemplater & pizzip packages using the following command:

1
npm i docxtemplater pizzip

The Code

The main code for all the below examples will stay the same. The only thing that will be changing is the dataToAdd Object (line 14). All other lines will stay the same.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const fs = require('fs');
const path = require('path');
const PizZip = require('pizzip');
const Docxtemplater = require('docxtemplater');

// Load the templated docx file
const templateFile = fs.readFileSync(path.resolve(__dirname, 'template.docx'), 'binary');
const zip = new PizZip(templateFile);

try {
	// Attempt to read all the templated tags
	let outputDocument = new Docxtemplater(zip);

	const dataToAdd = {};

	// Set the data we wish to add to the document
	outputDocument.setData(dataToAdd);

	try {
		// Attempt to render the document (Add data to the template)
		outputDocument.render()

		// Create a buffer to store the output data
		let outputDocumentBuffer = outputDocument.getZip().generate({ type: 'nodebuffer' });

		// Save the buffer to a file
		fs.writeFileSync(path.resolve(__dirname, 'OUTPUT.docx'), outputDocumentBuffer);
	}
	catch (error) {
		console.error(`ERROR Filling out Template:`);
		console.error(error)
	}
} catch(error) {
	console.error(`ERROR Loading Template:`);
	console.error(error);
}

Filling out the Template with JavaScript

All of the data that will be added to the template will be contained within a single Object called dataToAdd.

Each key in the dataToAdd Object refers to a tag that was placed in the docx file. Let’s see how to do this:

Direct Values

Direct values simply mean static values that we wish to insert into the document. Some examples would be the date, author, title. Simple text values you wish to add that may change with each document.

Docx File

The Code

1
2
3
const dataToAdd = {
    document_creation_date: (new Date()).toLocaleDateString()
};

Results

Styling

The style that is applied to the tag in the document will be inherited by the value that is inserted:

Docx File

The Code

1
2
3
4
const dataToAdd = {
    document_creation_date: (new Date()).toLocaleDateString(),
    document_author: 'Alexander Wells',
};

Results

Lists

A very useful feature is to be able to template repetitive sections. Perhaps the document is suppose to contain the profiles of some employees. Let’s see how we can use lists to do this:

Docx File

  • Lists are prefixed with the # character to start the list scope, and the / character to end the list scope.

The Code

1
2
3
4
5
6
const dataToAdd = {
    employeeList: [
        { id: 28521, name: 'Frank', age: 34, city: 'Melbourne' },
        { id: 84973, name: 'Chloe', age: 28, city: 'Perth' },
    ]
};

  • When using lists, the value should be a list of Objects.
  • The keys of each Object will refer to the tags used inside the List in the docx file (id, name, age, city).

Results

Table Rows

We can also use lists to create additional rows of a table. Let’s use the employee example again, but this time let’s use a table to represent the data:

Docx File

The Code

1
2
3
4
5
6
7
8
const dataToAdd = {
    employeeList: [
        { id: 28521, name: 'Frank', age: 34, city: 'Melbourne' },
        { id: 84973, name: 'Chloe', age: 28, city: 'Perth' },
        { id: 10349, name: 'Hank', age: 68, city: 'Hobart' },
        { id: 44586, name: 'Gordon', age: 47, city: 'Melbourne' },
    ]
};

Results

Conditional Rendering

Docx File

Another useful feature would be if we could only render certain things if a condition is met. Let’s see how we can do that:

The Code

1
2
3
4
const dataToAdd = {
    showDocumentAuthor: true,
    document_author: 'Alexander Wells',
};

Results

Changing showDocumentAuthor to false means the data inside the conditional statement won’t be rendered.

Helpful Tips

Too Much Spacing

You may notice if you use loops that extra spacing gets added where you don’t want. Let’s look at an example where this is very clear:

1
2
3
4
5
6
7
const dataToAdd = {
    groceryList: [
        { item: 'Eggs' },
        { item: 'Steak' },
        { item: 'Apples' }
    ],
};

  • With visible formatting enabled we can see the issue.
  • An extra line has been inserted between each list element.

We can fix this by enabling by enabling paragraphLoop option when creating our output document:

1
let outputDocument = new Docxtemplater(zip, { paragraphLoop: true });

Error Finding 1

  • One downside of this library is that error locations is actually a paid feature (yes I’m serious), look here.
  • This is an inconvenience and nothing more.
  • I STRONGLY suggest that you do templating in small batches. Add a few tags, test, add a few more, then test again.
  • Please do not spend hours (or even an hour) templating before testing. It will save you a massive headache when the program just says error, and you have to hunt through your document to find the issue.

Error Finding 2

  • Ensure that your tags in the docx file do not contain spaces at the start or end.
  • If you have a list tag called {#employeeList}, ensure there is no whitespace like this {#employeeList }, as this can cause the program to fail.
  • Microsoft Word can automatically add spaces so be aware of that.

Tag Names

  • USE GOOD TAG NAMES!
  • Just like when programming you try and use good variable names, same goes here.
  • A large document may have dozens or hundreds of tags. Using date for a tag name is BAD!
  • Using document_creation_date is much better as it’s clear exactly what date it’s referring to.

docxtemplater

Download count Current tag CDNJS version size gzip size

docxtemplater is a library to generate docx/pptx documents from a docx/pptx template. It can replace {placeholders} with data and also supports loops and conditions. The templates can be edited by non-programmers, for example your clients.

docxtemplater is very robust because of the many fixed issues over the years, and the high quality of tests and code.

Features

Demo Site

  • Replace a {placeholder} by a value
  • Use loops: {#users} {name} {/users}
  • Use loops in tables to generate columns
  • Use conditions (if users.length>3) with angular Parsing
  • Insert custom XML {@rawXml} (for formatted text for example)

Quickstart

  • Get started with docxtemplater on nodejs
  • Get started with docxtemplater in the browser (react, angular, vue, nextjs)

Documentation

The full documentation of the latest version can be found here.

See CHANGELOG.md for information about how to migrate from older versions.

Modules

Functionality can be added with the following paid modules :

  • Image module to add a given image with the syntax: {%image};
  • Html Module to insert formatted text in a docx document with the syntax {~html};
  • XLSX Module to be able to do templating on Excel files (xlsx extension), also with loops and conditions;
  • Chart Module to replace a chart by using data from the JSON object that you give with the syntax {$chart};
  • Html-Pptx Module to insert formatted text in a pptx document with the syntax {~html};
  • Error Location Module to show the errors in the template using Word comments
  • Slides Module to create multiple slides dynamically with the syntax {:users};
  • Subtemplate Module to include an external docx file inside a given docx file with the syntax {:include doc};
  • Subsection Module to include subsections (headers/footers) from an other document with the syntax {:subsection doc};
  • Subtemplate-pptx Module to include an external pptx file inside a given pptx file with the syntax {:include doc};
  • Word-Run Module to include raw runs (<w:r>) inside the document with the syntax {r@wrun}. This makes it possible to include styled text without having to remove the enclosing paragraph like in the {@rawXml} tag;
  • QrCode Module to replace an image, keeping any existing properties;
  • Table Module to create tables from two dimensional data using the syntax {:table data};
  • Meta Module to make a document readonly, add a text watermark or update the margins;
  • Styling Module restyle a paragraph, a cell or a table depending on some data using the syntax {:stylepar style};
  • Footnotes Module to be able to add footnotes to a document using the syntax {:footnotes foot}
  • Paragraph Placeholder Module to simplify conditions that should show or hide a given paragraph using the syntax {?tag}

About docxtemplater

Docxtemplater is my main job, and has been maintained for over 8 years. Expect to get great support if you buy any modules, and also good support on the open-source version.

If you’ve ever needed to generate a lot of documents, you probably know it’s not a quick task to manually create documents, pick the correct templates, format correctly, copy data into them and save in the correct locations. If you only need to do a few, doing it manually is fine but when you’re getting into the tens or hundreds of documents, some automation is needed.

Today, I’ll be showing you how to create documents in batches with data coming from a JSON array. Make sure you check my next post to take this further and create documents from data in an Oracle database.

Prerequisites

You only need a couple of things to get started — Node.js and a Word document to use as a template. I’d also recommend downloading Visual Studio Code as a code editor.

Word Document Template

Start by creating a new directory named WordDocumentGeneration and create a Word document named template.docx. This document will be the basis of all documents created automatically so make sure all the formatting is to your liking.

As part of the automation process, we’re going to replace tags in the document with data. These tags should be surrounded by curly brackets (or braces) and should identify what data to insert. For example, if I want to automatically set the title of a document, I’d create a tag called {title}, which will be replaced with the actual title later on, automatically. These tags can be formatted however you like and can be place anywhere in the document.

For this tutorial, I’ve modified my standard consultancy template to include {title}, {subtitle}, {author} and {body} tags.

Node.js

Node.js is an asynchronous event-driven JavaScript runtime and allows developers to quickly bootstrap ideas with code libraries (or «packages») from npm (Node Package Manager). To generate Word documents automatically, we’re going to be using the docxtemplater package.

Getting Started

Open the directory you created earlier in Visual Studio Code and open the terminal. If you don’t have Visual Studio Code installed, the following steps can be run from a regular terminal or command line.

Start by executing the command npm init to initialise a new npm package. You’ll be asked to enter some details about your package but it’s ok just to leave them with their default values. Your terminal output should look something like below:

billys-macbook:WordDocumentGeneration billy$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (worddocumentgeneration) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Volumes/GoogleDrive/My Drive/WordDocumentGeneration/package.json:

{
    "name": "worddocumentgeneration",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
    },
    "author": "",
    "license": "ISC"
}


Is this OK? (yes) 
billys-macbook:WordDocumentGeneration billy$ 

You’ll notice there is now a file named package.json in your directory. This file holds basic information about your new package, including any dependencies it requires. We need 2 dependencies, docxtemplater for variable substitution logic and pizzip to «unzip» and «rezip» the DOCX file.

Fun fact: a DOCX file is essentially a ZIP archive of XML files that make up a document. Try unzipping one in an application like 7-Zip to see.

To install the required dependencies, execute the following command in your terminal:

npm install docxtemplater pizzip

After the dependencies are downloaded, you should now see a new directory named node_modules, which contains the dependencies, and a new file named package-lock.json, which contains information about exactly what dependencies were installed and how. The package.json file will also have been updated with our dependencies.

Create a new file named index.js and we’re ready to start writing code. At the very top, we’ll import the dependencies we need. This includes the dependencies we installed and a couple of built-in dependencies to assist with file reading and writing.

const PizZip = require('pizzip');
const Docxtemplater = require('docxtemplater');
const fs = require('fs');
const path = require('path');

Once the dependencies are included, we’ll build a JSON array with the data we want to be included in our Word document. The keys for each object must match with the strings we used in our template. For example, {title} in our template will be replaced with title from the JSON object in the array.

const allData = [
    {
        title: 'Automating Word Document Generation',
        subtitle: 'A tutorial of how to generate Word documents using Node.js',
        author: 'Billy Syrett',
        body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere diam sagittis, pellentesque tortor eget, ullamcorper arcu. Pellentesque non porttitor diam, vel aliquet lacus.'
    },
    {
        title: 'Debugging Data in IFS',
        subtitle: 'How to quickly and easily output debugging data in IFS.',
        author: 'Billy Syrett',
        body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere diam sagittis, pellentesque tortor eget, ullamcorper arcu. Pellentesque non porttitor diam, vel aliquet lacus.'
    }
]

The docxtemplater documentation helpfully includes a couple of error handing functions, so we’ll include those. These will catch any compilation errors (e.g. misplaced tags) when using the docxtemplater package.

function replaceErrors(key, value) {
    if (value instanceof Error) {
        return Object.getOwnPropertyNames(value).reduce(function (error, key) {
            error[key] = value[key];
            return error;
        }, {});
    }
    return value;
}

function errorHandler(error) {
    console.log(JSON.stringify({ error: error }, replaceErrxors));
    if (error.properties && error.properties.errors instanceof Array) {
        const errorMessages = error.properties.errors.map(function (error) {
            return error.properties.explanation;
        }).join("n");
        console.log('errorMessages', errorMessages);
    }
    throw error;
}

Start by looping through all elements of our JSON array with a simple for loop, reading the template and initialising the docxtemplater function, catching any errors as we do so.

for (let i = 0; i < allData.length; i++) {
    const data = allData[i];
    const content = fs.readFileSync(path.resolve(__dirname, 'template.docx'), 'binary');
    const zip = new PizZip(content);
    var doc;
    try {
        doc = new Docxtemplater(zip);
    } catch (error) {
        errorHandler(error);
    }

While in our loop, pass in the data and render the document into a buffer.

    doc.setData(data);
    try {
        doc.render()
    }
    catch (error) {
        errorHandler(error);
    }
    const buf = doc.getZip().generate({ type: 'nodebuffer' });

Finally, write the contents of the buffer to a file on disk and output a confirmation to the terminal. I’m writing my file to an output directory (which must already exist), using the title key from the JSON object as the filename. You could easily add another key to your JSON object to specify a different filename and pass it in if needed.

   fs.writeFileSync(path.resolve(__dirname, 'output', `${data.title}.docx`), buf);
   console.log(`"${data.title}.docx" written to disk`);
}

To run the code, execute node . in the terminal and the documents will be generated.

Be sure to check out my next post where I take this a step further and generate documents from data in an Oracle database!

Also, docxtemplater has a load of more features than the one mentioned above, including loops, HTML replacement and images. Visit their site here.

Full Source Code

const PizZip = require('pizzip');
const Docxtemplater = require('docxtemplater');
const fs = require('fs');
const path = require('path');

const allData = [
    {
        title: 'Automating Word Document Generation',
        subtitle: 'A tutorial of how to generate Word documents using Node.js',
        author: 'Billy Syrett',
        body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere diam sagittis, pellentesque tortor eget, ullamcorper arcu. Pellentesque non porttitor diam, vel aliquet lacus.'
    },
    {
        title: 'Debugging Data in IFS',
        subtitle: 'How to quickly and easily output debugging data in IFS.',
        author: 'Billy Syrett',
        body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere diam sagittis, pellentesque tortor eget, ullamcorper arcu. Pellentesque non porttitor diam, vel aliquet lacus.'
    }
]

function replaceErrors(key, value) {
    if (value instanceof Error) {
        return Object.getOwnPropertyNames(value).reduce(function (error, key) {
            error[key] = value[key];
            return error;
        }, {});
    }
    return value;
}

function errorHandler(error) {
    console.log(JSON.stringify({ error: error }, replaceErrxors));
    if (error.properties && error.properties.errors instanceof Array) {
        const errorMessages = error.properties.errors.map(function (error) {
            return error.properties.explanation;
        }).join("n");
        console.log('errorMessages', errorMessages);
    }
    throw error;
}


for (let i = 0; i < allData.length; i++) {
    const data = allData[i];
    const content = fs.readFileSync(path.resolve(__dirname, 'template.docx'), 'binary');
    const zip = new PizZip(content);
    var doc;
    try {
        doc = new Docxtemplater(zip);
    } catch (error) {
        errorHandler(error);
    }

    doc.setData(data);
    try {
        doc.render()
    }
    catch (error) {
        errorHandler(error);
    }
    const buf = doc.getZip().generate({ type: 'nodebuffer' });

    fs.writeFileSync(path.resolve(__dirname, 'output', `${data.title}.docx`), buf);
    console.log(`"${data.title}.docx" written to disk`);
}

open-xml-templating / docxtemplater
Goto Github
PK

View Code? Open in Web Editor
NEW

2.4K
78.0
321.0
27.31 MB

Generate docx, pptx, and xlsx from templates (Word, Powerpoint and Excel documents), from Node.js, the Browser and the command line / Demo: https://www.docxtemplater.com/demo. #docx #office #generator #templating #report #json #generate #generation #template #create #pptx #docx #xlsx #react #vuejs #angularjs #browser #typescript #image #html #table #chart

Home Page: https://www.docxtemplater.com

License: Other

HTML 0.16%
JavaScript 97.56%
Shell 1.60%
TypeScript 0.68%
docx
javascript
generate
template
powerpoint
microsoft
create
docx-generator
excel
office
pptx
word
xlsx
express
expressjs
vue
vuejs
angular

docxtemplater’s Introduction

Download count Current tag CDNJS version size gzip size

docxtemplater is a library to generate docx/pptx documents from a docx/pptx template. It can replace {placeholders} with data and also supports loops and conditions. The templates can be edited by non-programmers, for example your clients.

docxtemplater is very robust because of the many fixed issues over the years, and the high quality of tests and code.

Features

Demo Site

  • Replace a {placeholder} by a value
  • Use loops: {#users} {name} {/users}
  • Use loops in tables to generate columns
  • Use conditions (if users.length>3) with angular Parsing
  • Insert custom XML {@rawXml} (for formatted text for example)

Quickstart

  • Get started with docxtemplater on nodejs
  • Get started with docxtemplater in the browser (react, angular, vue, nextjs)

Documentation

The full documentation of the latest version can be found here.

See CHANGELOG.md for information about how to migrate from older versions.

Modules

Functionality can be added with the following paid modules :

  • Image module to add a given image with the syntax: {%image};
  • Html Module to insert formatted text in a docx document with the syntax {~html};
  • XLSX Module to be able to do templating on Excel files (xlsx extension), also with loops and conditions;
  • Chart Module to replace a chart by using data from the JSON object that you give with the syntax {$chart};
  • Html-Pptx Module to insert formatted text in a pptx document with the syntax {~html};
  • Error Location Module to show the errors in the template using Word comments
  • Slides Module to create multiple slides dynamically with the syntax {:users};
  • Subtemplate Module to include an external docx file inside a given docx file with the syntax {:include doc};
  • Subsection Module to include subsections (headers/footers) from an other document with the syntax {:subsection doc};
  • Subtemplate-pptx Module to include an external pptx file inside a given pptx file with the syntax {:include doc};
  • Word-Run Module to include raw runs (<w:r>) inside the document with the syntax {[email protected]}. This makes it possible to include styled text without having to remove the enclosing paragraph like in the {@rawXml} tag;
  • QrCode Module to replace an image, keeping any existing properties;
  • Table Module to create tables from two dimensional data using the syntax {:table data};
  • Meta Module to make a document readonly, add a text watermark or update the margins;
  • Styling Module restyle a paragraph, a cell or a table depending on some data using the syntax {:stylepar style};
  • Footnotes Module to be able to add footnotes to a document using the syntax {:footnotes foot}
  • Paragraph Placeholder Module to simplify conditions that should show or hide a given paragraph using the syntax {?tag}

About docxtemplater

Docxtemplater is my main job, and has been maintained for over 8 years. Expect to get great support if you buy any modules, and also good support on the open-source version.

docxtemplater’s People

docxtemplater’s Issues

Angular parsing and sections

It seems that sections don’t use the angular parsing.

Test:

var tags = {
  foo: {
    bar: true
  }
}

Document:

{#foo.bar} Hello {/foo.bar}

Output is nothing.

Template inside template

Hi.
Do you plan any mechanism to insert one template to another?
That mechanism might be implemented by the «insert raw xml» api and another function for example:

We would write something like:

var doc = new DocxGen().loadFromFile("xmlInsertionExample.docx");
var doc2 = new DocxGen().loadFromFile("littleTextBlock.docx");
doc.setTags({
    tag1: "Hello World",
    rawXmlTag: doc2.getDocumentBody()
});
...

What do you think about this feature?

Qr code replacing is not working

Qr code replacing is not working when I give image path in the following format : «/home/arun/img.png».

type options for jszip.generate

Hello,
i’am new to nodejs and i’am using docxtemplater on nodejs server.
I whant to pass to client docx file.
here’s my working sample —

var doc = doc.output({type:{base64:false}});
res.setHeader('Content-Type', 'application/vnd.openxmlformats');
res.setHeader("Content-Disposition", "attachment; filename=" + "avanc.docx");
res.status(200).end(doc, 'binary');

I’ve figured that if I put these changes into docxgen.js line 932
result = this.zip.generate(options.type)
, this work quite well, user gets the docx.

Maybe you can put these changes in your code, so we can pass options directly to jszip.generate.

Table Loop not working

I can’t get this to work correctly, even the demo doesn’t work properly (it just puts all of the content in one row).

HyperLink inside table using looping

I’m using docxtemplater in nodejs server to generate reports. I can generate the table using looping. But I want HyperLink elemants in the table, the hyparlink styling works fine. But all the HyperLinks are pointing to the same address, Is it possible to assign different address to each Hypelink using looping.

conditional statements

I looked briefly through the angular parsing and it looks like are ways to do conditional testing in templates, but don’t see any demos. Is there an easy way to implement something like:

{#if foo == bar} I show up only if foo equals bar {/if}

The above being more like Handlebars.

Something like {{ foo == bar | "I am showing up here" }} may also work for my needs, but ultimately I need to show / hide certain sections of a document template based on the input.

node.js api

Could you provide direct node.js api for this library ? It would be very useful !

Doesn’t work with JSZip 2.x

I suspect that the needed changes are minimal. But simply changing instances of files["test.txt"].data to files["test.txt"].asText() is not enough.

qr code replacing. image size

Replaced Images have the same size and aspect ratio equal to that of the qr code. Is it possible to keep the original image size.?

Line breaks result in corrupted docx file

I’m using docxtemplater in the latest version of Chrome and I’m passing in some strings that were originally HTML. When there are line breaks, the output docx file is corrupted, though works fine without line breaks. When I say corrupted, I mean this happens:

screen shot 2014-04-27 at 1 25 16 pm

I can work around this by splitting on line breaks in Javascript and passing them to something like this in the template:

Is this is the recommended way, or is there something I can pass in that will convert line breaks into paragraphs in the docx template?

Doesn’t work with utf-8 symbols in template

If docx template file contains non-latin characters (for example cyrillic characters), they are converted into wrong symbols (something as «Ð�Ñ�овеÑ�ка»). This happens because of incorrect encoding/decoding in base64. How is it possible to fix it?
Text in variables inserted correctly.

Doesn’t work with documents containing SmartArt/Charts and qrcode test enabled

SmartArt objects, Charts are saved as separate xml files in docx. When docxtemplater tries to parse through these objects, it doesn’t find the actual object, but a reference to this objects in document.xml, causing it to throw exception «tagRiD undefined!».
Example? Use any docx file with a chart in it, even without any tag to be replaced, and «config.qrcode»:true in json.
Need the charts to be dynamic, so earlier workaround, #39, is of no use.

Support for IF ELSE

Is it possible to add IF ELSE in templating? For example I want to create a table where row formatting will depend on row data.

Permission denied in log

Hi,
I have this message in my log, but reports are correctly generated!

mkstemp(«/var/www/.execoooQbuRZL») failed: Permission denied

I have no idea how explain that.
Thanks,

html canvas and checkbox

Hi,
I have some questions, does docxtemplater support checkbox?
an other question it can be possible to pass canvas image (toDataURL) to a word document?
if so, how can i do this ?

Thanks

Doesn’t work with image in header/footer when QR code test enabled

When I try to put an image in the Header/Footer of my template, with QR code test enabled, it throws «Error : Rid is not an image».

Links in docx trigerred «no data found in json file»

Here is the error, I identified that this problem is triggered in documents with links included.

docxtemplater tmp2qudpp.json
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image15.png"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image16.png"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image17.png"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image18.png"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image19.png"/><Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image20.png"/><Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image21.png"/><Relationship Id="rId9" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image22.png"/><Relationship Id="rId10" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image23.png"/><Relationship Id="rId11" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image24.png"/><Relationship Id="rId12" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image25.png"/><Relationship Id="rId13" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" Target="header1.xml"/><Relationship Id="rId14" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer" Target="footer1.xml"/><Relationship Id="rId15" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image26.png"/><Relationship Id="rId16" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image27.png"/><Relationship Id="rId17" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image28.png"/><Relationship Id="rId18" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="https://fr.wikipedia.org/wiki/" TargetMode="External"/><Relationship Id="rId19" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="mailto:[email protected]" TargetMode="External"/><Relationship Id="rId20" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" Target="header2.xml"/><Relationship Id="rId21" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer" Target="footer2.xml"/><Relationship Id="rId22" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/><RelationshipId="rId23" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/><Relationship Id="rId24" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>
</Relationships>
could not decode

docxtemplaterclilibmain.js:61
    if (docX[docxFileName]==undefined) throw 'no data found in json file'
                                       ^
no data found in json file

{else} tag for loops

I saw #6, but I was hoping to inquire about the difficulty to add an {else} tag much like the Handlebars equivalent.

I have several variables that are laid out like is_foo_bar that are determined by a user form. While this is great for having conditional areas of content in a document, it becomes messy to try to check for the inverse of the variable. Especially if the variable is falsey to begin with.

Right now, for every variable I create a second variable that is the inverse of the first one, and prefix it with «not_».

For example (this is how I am doing things currently):

{#is_foo_bar}
  ... user picked foo is bar.
{/is_foo_bar}
{#not_is_foo_bar}
  ... user picked foo is not bar.
{/not_is_foo_bar}

What would be awesome:

{#is_foo_bar}
   ... user picked foo is bar.
{else}
  ... user picked foo is not bar.
{/is_foo_bar}

This would also come in handy if you want to display alternate content for when an array that is being looped is empty.

I would love to attempt to add this feature, but was hoping someone might be able to point me in the right direction to implement.

Changing the button value

I’m trying to print an invoice and a credit note using docxtemplater. Both buttons should appear in the same page, where one button says «invoice» and the other «credit». I tried modifying the js file, but the value of the button will always be either «invoice» or «credit». Can’t display both at the same time.Both buttons print the correct documents. It’s just the value of the buttons that can’t be changed. Can you please help me with this.
Thanks

doesn’t install

Hi! I have a problem again.
Module doesn’t install.
Error depends on jasmine.
If I try to install as my user

npm ERR! error rolling back Error: EACCES, unlink '/usr/bin/jasmine-node'

If I try to install as root

npm ERR! Error: Attempt to unlock jasmine-node, which hasn't been locked

Also. A co-worker of mine has tried install node.js and docxtemplater at the clean system. And docxtemplater didn’t install, but other modules (for example express) has been installed successfully.

Any suggestions?

Cyrillic characters coding problem

I’ve tried to use library to generate reports.
But I need to make reports in Russion for our clients.
So if I tried tags like these:

{
    'fields.director': "Пупкина В.И.",
}

It looks within document like this:

It’s coding problem.

Default binary doesn’t output any docx files

Hello again!
Don’t want to be too intrusive, but somehow I was unable to create any output. Although my OS is irrelevant, I’ve got this bug on MacBook Air with MacOS X 10.9.2. I tried installing from npm and manually from your sources.

I’ve found that the problem is with DocxGen.prototype.output: file is not created due to condition if (options.download), which is obviously false when calling output like this.output(true, outputFile) (as in main.js).

This happens because output is function(options), and options gets equal to true. Of course, after that your checks like this actually do nothing, because options is true:

if (options.download == null) {
        options.download = true;
}

Sadly, it seems like some kind of code inconsistence.
By the way, this bug is present in .coffee files as well.

table styling

I really like your tool.
How does one set conditional formatting for table cells?
Alternatively, is there a workaround to embed html-snippets to the result document?

  • sr

Pushing HTML into template tag

Is it possible to push html formatted text into templates?

Examples path is hard coded

Why does the docxgen.js hard code to the examples folder:

DocUtils.config = {
«baseNodePath»: ‘../../examples/’,
«baseClientPath»: ‘../examples/’
};

node.js , get stream from docx , no file created

I don’t want to create a file in server . can I redirect docx stream to standard resp ?
@edi9999 thanks a lot .

&, « and other symbols

Hello again!

I’ve tried to use string like «s» – &amp;@$ in my docx file, and I was unable to make it work correctly.

Specifically, if I pass «s» – &amp;@$ in windows-1251, docx is outputted correctly except that dash is rendered as kind of garbage:
image.

If I use utf8, I get
image

The only difference is win1251/utf8 of string "text": "«s» – &amp;@$" in Json.

Evidently, it is somehow related with #28.
Sadly, I don’t know what to do with this and consider it really, really critical bug. I tried to escape contents of json values with _.escape(), but no luck.

nested JSON objects

Great app! I have a need to be able to nested-object properties in my template. For example, I have a tag object that may look like this:

{
    "name": "Tom",
    "address" : 
         {"state": "TX", 
          "city": "Austin"}
 }

In my Word template I’d like to be able to use the following angular-like syntax:

I tried it with the latest version from npm and it didn’t appear to work — is this a feature that may be added in the future?

Callback for output method

Could you please add callback for output method because fs.writeFile is async.

output = function(download, name, callback) {
                        var result;
                        if (download == null) {
                            download = true;
                        }
                        if (name == null) {
                            name = "output.docx";
                        }

                        this.calcZip();
                        result = this.zip.generate();
                        if (download) {
                            if (env === 'node') {
                                fs.writeFile(process.cwd() + '/' + name, result, 'base64', function(err) {
                                    if (err) {
                                        throw err;
                                    }
                                    if (callback) {
                                        callback();
                                    }
                                    return console.log('file Saved');
                                });
                            } else {
                                document.location.href = "data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64," + result;
                            }
                        }
                        return result;
                    };

Data filters

Hello again!

Just discovered that many developers with Django/AngularJS background find it useful to use data filters in their templates.

Here is angular example and an example from Django.

I think that would be pretty nice feature as it allows to specify some basic presentation logic right in the template and to take it away from business-logic and data representation.

For example, there may be few user-specified report templates in some system, and in one of them you want to have «March 21, 2014» in header and «21.03.2014, 13:50» in report’s entries. On the other hand, in another report template user may want to display dates and times in different representations.

It would not only be a disaster to send different strings by distinguishing templates in business logic, but it would just be impossible because you don’t know requirements for each new template.

Make the loadFromFile static

Make the loadFromFile static:

eg : DocXGen.loadFromFile("test.docx",options)

instead of new DocXGen().loadFromFile("test.docx",options)

If qrCode is true, normal pictures are broken

Array.prototype.max Bug

 Array.prototype.max = function() {
    return Math.max.apply(null, this);
  };

  Array.prototype.min = function() {
    return Math.min.apply(null, this);
  };

global change , this will made my iterator crash

Data filtering and transformation

Hi!
Have you considered Angular filters?
Must be awesome feature: the same data in different representation is very common case in templating, especially with dates.
I see that it can be done on my own, but that’s kind of very important feature used by too many developers — would be convenient to have angular filters by default, out of the box.
If matters, I use docxtemplater on server-side with Node, not on client. By the way, interesting for me, how typical is client-side usage of library?

Use Replace Variables and Raw Xml Insertion

Hi @edi9999 ,
I used code

docx.setTags(tags);
docx.applyTags({table:table,mytable:table1});

then tags show undefined . but following

docx.setTags(tags);
docx.applyTags();
docx.applyTags({table:table,mytable:table1});

instead , insert raw xml won’t work .
What should I do ? thanks a lot

Support for raw XML insertion

I use your library for newsfeed reports generation, and I think it would be great to allow users to substitute «{hisContents} tag with raw XML like this:

<w:r>
    <w:rPr>
        <w:highlight w:val="red"/>
    </w:rPr>
    <w:t>Some highlighted text</w:t>
</w:r>

It is especially useful when {content} is itself large (for example, news entry), because it makes possible for user to control underlying styles, colors and etc in some kind of large text entry. In fact, it is one of very few options for supporting such control over big runtime-generated text pieces.

If I try to do this now, my XML is surrounded by <w:r><w:t>myRawXmlHere/w:t/w:r, and MS Word obviously thinks that file is corrupt.

For distinguishing «raw» xml data I see 2 solutions: you may automatically detect XML by using something like XPath //w:r/w:t/w:r/w:t and act accordingly, or you may use some kind of special tag syntax like {@myXmlData}, which will prevent library from surrounding contents with <w:r><w:t></w:t></w:r>. I prefer latter option, because it has explicit behavior.

Although there is one problem with node replacing: it is possible that single text run contains not only you tag, but some extra data:

<w:r>
    <w:t>Your complex text i</w:t>
</w:r>
<w:r>
    <w:t>s {rawXmlText}.</w:t>
</w:r>

If we are to replace node, we will lose ‘s’ for ‘is’ and ‘.’ after our tag.
So, we have to split runs with tags like that: [left part, static] [middle part, dynamic] [right part, static], where left or right parts may be empty. After that we need to create separate run for left part (if it is non-empty), middle, and for right part. Also, we have to apply run-level formatting of the initial run to them. After that we can just replace initial run with the middle one’s XML. It will lose initial formatting, of course, but I am unsure if it is bad thing for control-seeking user. Maybe it should be an option, I don’t know. If it have to be there — we can just copy non-overlapping attributes from initial run to user-supplied one.

And I don’t know, but maybe the same for paragraphs?

Thank you for great library!

MS Windows’ line endings in global /usr/local/bin/docxtemplater

Hi!

While trying to launch global docxtemplater binary I get
env: noder: No such file or directory
Looks like MS Windows’ file ending ‘rn’.

When I changed line endings to unix style, error disappeared.

Also, I suggest to use path.join or even path.normalize in ./docxgenNode/bin/docxgen.js like this:

path = require('path')

require(path.join(__dirname, '/../../js/docxgen.js'));
require(path.join(__dirname, '/../lib/main.js'));

The reason is that string concatenation like

require(__dirname + '/../lib/main.js');

tends to be kind of dangerous in some cases (for example, when folder separators are not in place). Just to be sure.

Embed XML , use table

@edi9999 , HI

{complexXml:'<w:p><w:pPr><w:rPr><w:color w:val="FF0000"/></w:rPr></w:pPr><w:r><w:rPr><w:color w:val="FF0000"/></w:rPr><w:t>My custom</w:t></w:r><w:r><w:rPr><w:color w:val="00FF00"/></w:rPr><w:t>XML</w:t></w:r></w:p>'}

I used table’xml which you writed at stackoverflow replaced p’xml

<w:tbl>
<w:tblBorders>
  <w:top w:val="dotted" w:sz="18" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
  <w:left w:val="dotted" w:sz="18" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
  <w:bottom w:val="dotted" w:sz="18" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
  <w:right w:val="dotted" w:sz="18" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
  <w:insideH w:val="dotted" w:sz="18" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
  <w:insideV w:val="dotted" w:sz="18" w:space="0" w:color="C0504D" w:themeColor="accent2"/>
</w:tblBorders>
</w:tbl>

but it doesn’t work , can you give a hand . thanks .

Support for non-png QR-code images

Hello!

What do you think about supporting non-png images (such as gif and jpg)?
I am unsure if it is very useful feature (I don’t think you want docxtemplater to become full-featured giant bloatware), but definetly something to consider.

Anyway, I think it is good idea to put clear and explicit description of format limitations in documentation (i.e., explicitly state in demos and docs that currently it works only with PNG). Now this restriction is unclear unless you look at the source code and see pngjs there.

npm installer assumes root user or non-root -g

Hi!

Just tried to install docxtemplater like this:

lr-pc:docx-report lr$ sudo npm -g install docxtemplater
npm http GET https://registry.npmjs.org/docxtemplater
npm http 200 https://registry.npmjs.org/docxtemplater
npm http GET https://registry.npmjs.org/docxtemplater/-/docxtemplater-0.5.2.tgz
npm http 200 https://registry.npmjs.org/docxtemplater/-/docxtemplater-0.5.2.tgz

> [email protected] preinstall /usr/local/lib/node_modules/docxtemplater
> npm install -g jasmine-node && npm install -g browserify

As you can see, it tries to execute npm install -g without root rights.
This is Ok in some cases, but sadly most people don’t own /usr/local/lib/node_modules, because that is how node installer works by default.

Not sure where it happens, because you don’t seem to have this preinstall in your package.json, but anyway it seems to be kind of little annoyance.

Interesting, this thing happens even if user owns /usr/local/lib/node_modules, but runs npm from sudo.

localImageCreator problem

Hi there,

My aim is to replace a qrcode within a loop with multiple images. The application is an Apache Cordova app and therefore I need a custom way to pass the image data to DocxTemplater. I’ve seen some documentation on localImageCreator which looks promising but when I try to add a simple console.log line to the defaultImageCreator function, it doesn’t get returned. I’ve added «gen:» to the beginning of my qrcode as suggested.

I was wondering if this function is still in use? Or is it dead code?

Is there any way to get around this problem?

Thank
David

Cant do anything

I want to do a simple cv constructor. I have a code. What i must to do with that issue. I cant do anything. So many examples i tryed, but nothing. What i need: to get the values from fields and than put it to the my file. I cant change nothing.
123
Here is a code:

<script language="javascript">
    /*<![CDATA[*/
        function load(){
            alert("JS on!");
        }
    /*]]>*/
</script>

Main information :

    <form>

        First name*  <input type="text" id="firstName" name="constructor"><br>

        Last name*  <input type="text" id="lastName" name="constructor"><br>

        Third name* <input type="text" id="thirdName" name="constructor"><br>

        Address* <input type="text" id="address" name="constructor"><br>

        Birthday*  <input type="text" id="birthday" name="constructor"><br>
        Email*  <input type="text" id="email" name="constructor"><br>
        Phone number*  <input type="text" id="phoneNumber" name="constructor"><br>
        Web-site  <input type="text" id="website" name="constructor"><br>
    </form>
    Goal* 
    <form>
        <textarea rows="10" cols="50" maxlength="500" id="goal" name="constructor"></textarea>
    </form>
    About yourself* 
    <form>
        <textarea rows="10" cols="50" maxlength="500" id="aboutYS" name="constructor"></textarea>
    </form>
    Education* 
    <form>
        <textarea rows="10" cols="50" maxlength="500" id="education" name="constructor"></textarea>
    </form>
    Skills* 
    <form>
        <textarea rows="10" cols="50" maxlength="500" id="skills" name="constructor"></textarea>
    </form>
    Languages* 
    <form>
        <textarea rows="10" cols="50" maxlength="500" id="languages" name="constructor"></textarea>
    </form>
    Professional experience(if u have) :
    <form>
        Company <input type="text" id="company" name="constructor"><br>
        About company <input type="text" id="aboutCO" name="constructor"><br>
        Post <input type="text" id="post" name="constructor"><br>
        Web-site <input type="text" id="websiteCO" name="constructor"><br>
        Date <br>
        <input type="checkbox" id="isWork"> I am working here right now <br>
        Description <br>


        <textarea rows="10" cols="50" maxlength="500" id="description" name="constructor"></textarea>
    </form>
    <form>
        <input type="button" id="execute" onClick="constrCV();" value="Construct">
    </form>
    <h1 id="footer"></h1>

Please help. Thanks.

var imgData = docXData[«image.png»]; // imgData undefined

var imgData = docXData[«image.png»];
console.log(imgData);
///undefined printed
output.docx generated
what’s the problem ? thank you .
@edi9999 Is something wrong ? image in current dir .

Looping over images

I would like to add a variable amount of images to the docx file. Is this something that can be done at the moment that I’ve overlooked? Or something that could be added relatively simply?

I understand the imgManager.addImageRels function adds a new image. So the next step would just add to the document.xml file with the xml linking to that new image, right?

Any help would be much appreciated.

js error in complex table template

Hi edi9999,

This is a really useful stuff… but I’m not be able to make it works with templates a little bit more complex.

Check this example: http://94.46.241.129/docxtemplater/examples/demo.html

As you can see, in this case I’m using a for-loop and «stand-alone» values for the totals of the table. However, It seems that does not work fine, not all the tags are replaced and as a result there is a javascript error.

Thanks in advance

Invalid doc file if string contains ‘&’,

I tried to generate doc file with the string which has ‘&’ character. It generates the doc file and but when i tried to open the file, it says invalid file.

DocXTemplater + Express.js

Hi,
I’d like to use DocXTemplater with Express.js.
I create a route in this way:

var mime = require(‘mime’),
Docx = require(‘docxtemplater’);

exports.index = function(req, res) {
var file = __dirname + ‘/../assets/tagExample.docx’,
mimetype = mime.lookup( file );

res.setHeader(‘Content-disposition’, ‘attachment; filename=test.docx’);
res.setHeader(‘Content-type’, mimetype);

new Docx().loadFromFile( file, { async: true } ).success(function(docx) {
docx.setTags({
«first_name»:»Hipp»,
«last_name»:»Edgar»,
«phone» : «0652455478»,
«description» : «New Website»
});

res.write( docx.output(false) );
res.end();

});
};

but when I try to open the just downloaded file, it comply saying it’s a corrupted file. I can’t either open the file with WinZip.

Any ideas?
Thanks

ability to get a jsonschema from a template for auto creating forms.

Hi,

It would be very interesting if it would be possible to create json schema based on the tags in the template. This would mean you could input them into something like alpaca and have a instant form that can be used to fill the tags with.

An added advancement would then be to add optional types (boolean for instance) to the tags ( {{name:type}} ?) so the schema could be more specific.

Just put in a docx template and you’d end up with a form and a means to download a filled docx document.

Image from original document is lost when qrCode is true

Hello!

I have tried one of my templates in new version of docxtemplater and something strange happened: what perfectly worked in 0.2.0 is now broken.

You may download original report file and the processed one.

Now supply any first_name and be sure to set qrCode to true like this:

{
    "config.docxFile": "report1.docx",
    "config.outputFile": "output.docx",
    "config.qrcode": true,
    "first_name": "Edgar"
}

As you can see, image gets broken. However, if qrCode === false, everything works Ok.
And the most interesting part: this image + qrCode === true flawlessly worked in 0.2.0.

Docx-templates Build Status Coverage Status npm version

Template-based docx report creation for both Node and the browser. (See the blog post).

Why?

  • Write documents naturally using Word, just adding some commands where needed for dynamic contents
  • Express your data needs (queries) in the template itself (QUERY command), in whatever query language you want (e.g. in GraphQL). This is similar to the Relay way™: in Relay, data requirements are declared alongside the React components that need the data
  • Execute JavaScript snippets (EXEC command, or ! for short)
  • Insert the result of JavaScript snippets in your document (INS, = or just nothing)
  • Embed images, hyperlinks and even HTML dynamically (IMAGE, LINK, HTML). Dynamic images can be great for on-the-fly QR codes, downloading photos straight to your reports, charts… even maps!
  • Add loops with FOR/END-FOR commands, with support for table rows, nested loops, and JavaScript processing of elements (filter, sort, etc)
  • Include contents conditionally, IF a certain JavaScript expression is truthy
  • Define custom aliases for some commands (ALIAS) — useful for writing table templates!
  • Run all JavaScript in a separate Node VM or a user-provided sandbox.
  • Include literal XML
  • Written in TypeScript, so ships with type definitions.
  • Plenty of examples in this repo (with Node, Webpack and Browserify)
  • Supports .docm templates in addition to regular .docx files.

Contributions are welcome!

Installation

$ npm install docx-templates

…or using yarn:

$ yarn add docx-templates

Node usage

Here is a simple example, with report data injected directly as an object:

import createReport from 'docx-templates';
import fs from 'fs';

const template = fs.readFileSync('myTemplate.docx');

const buffer = await createReport({
  template,
  data: {
    name: 'John',
    surname: 'Appleseed',
  },
});

fs.writeFileSync('report.docx', buffer)

You can also provide a sync or Promise-returning callback function (query resolver) instead of a data object:

const report = await createReport({
  template,
  data: query => graphqlServer.execute(query),
});

Your resolver callback will receive the query embedded in the template (in a QUERY command) as an argument.

Other options (with defaults):

const report = await createReport({
  // ...
  additionalJsContext: {
    // all of these will be available to JS snippets in your template commands (see below)
    foo: 'bar',
    qrCode: async url => {
      /* build QR and return image data */
    },
  },
  cmdDelimiter: '+++',
    /* default for backwards compatibility; but even better: ['{', '}'] */
  literalXmlDelimiter: '||',
  processLineBreaks: true,
  noSandbox: false,

  /**
   * Whether to fail on the first error encountered in the template. Defaults to true. Can be used to collect all errors in a template (e.g. misspelled commands) before failing.
   */
  failFast: true,

  /**
   * When set to `true`, this setting ensures createReport throws an error when the result of an INS, HTML, IMAGE, or LINK command turns out to be null or undefined,
   * as this usually indicates a mistake in the template or the invoking code (defaults to `false`).
   */
  rejectNullish: false,

  /**
   * MS Word usually autocorrects JS string literal quotes with unicode 'smart' quotes ('curly' quotes). E.g. 'aubergine' -> ‘aubergine’.
   * This causes an error when evaluating commands containing these smart quotes, as they are not valid JavaScript.
   * If you set fixSmartQuotes to 'true', these smart quotes will automatically get replaced with straight quotes (') before command evaluation.
   * Defaults to false.
   */
  fixSmartQuotes: false;
});

Check out the Node examples folder.

Deno usage

You can use docx-templates in Deno! Just follow the Browser guide and import the polyfilled docx-templates bundle, for example from unpkg:

// @deno-types="https://unpkg.com/docx-templates/lib/bundled.d.ts"
import { createReport } from 'https://unpkg.com/docx-templates/lib/browser.js';

Browser usage

You can use docx-templates in the browser (yay!). Just as when using docx-templates in Node, you need to provide the template contents as a Buffer-like object.

For example when the template is on your server you can get it with something like:

const template = await fetch('./template.docx').then(res => res.arrayBuffer())

Or if the user provides the template you can get a File object with:

<input type="file">

Then read this file in an ArrayBuffer, feed it to docx-templates, and download the result:

import createReport from 'docx-templates';

const onTemplateChosen = async () => {
  const template = await readFileIntoArrayBuffer(myFile);
  const report = await createReport({
    template,
    data: { name: 'John', surname: 'Appleseed' },
  });
  saveDataToFile(
    report,
    'report.docx',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  );
};

// Load the user-provided file into an ArrayBuffer
const readFileIntoArrayBuffer = fd =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = reject;
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.readAsArrayBuffer(fd);
  });

You can find an example implementation of saveDataToFile() in the Webpack example.

Check out the examples using Webpack and using Browserify or you can use the browserified bundle directly as discussed below.

Polyfilled browser-ready bundle

As this library depends on the internal NodeJS modules vm, stream, util, events and the Buffer global, your build tools have to polyfill these modules when using the library in the browser. We provide a browser build wich includes the required polyfills. Its file size is about 300K uncompressed or 85K / 70K with gzip / brotli compression).

You can import the library directly as a module using e.g. the unpkg.com CDN, like below, or you can host the /lib/browser.js bundle yourself.

import { createReport } from 'https://unpkg.com/docx-templates/lib/browser.js';

this is good for testing or prototyping but you should keep in mind that the browser.js is es2017 code wich is supported by only 95% of users. If you have to support IE or old browser versions, you are better off compiling it to your target. Also see the support table for es2017 here.

Browser template compatibility caveat

Note that the JavaScript code in your .docx template will be run as-is by the browser. Transpilers like Babel can’t see this code, and won’t be able to polyfill it. This means that the JS code in your template needs to be compatible with the browsers you are targeting. In other words: don’t use fancy modern syntax and functions in your template if you want older browsers, like IE11, to be able to render it.

Writing templates

You can find several template examples in this repo:

  • SWAPI, a good example of what you can achieve embedding a template (GraphQL in this case) in your report, including a simple script for report generation. Uses the freak-ish online Star Wars GraphQL API.
  • Dynamic images: with examples of images that are dynamically downloaded or created. Check out the images-many-tiles example for a taste of this powerful feature.
  • Browser-based examples using Webpack and using Browserify.

Custom command delimiters

You can use different left/right command delimiters by passing an array to cmdDelimiter:

const report = await createReport({
  // ...
  cmdDelimiter: ['{', '}'],
})

This allows much cleaner-looking templates!

Then you can add commands and JS snippets in your template like this: {foo}, {project.name} {QUERY ...}, {FOR ...}.

When choosing a delimiter, take care not to introduce conflicts with JS syntax, especially if you are planning to use larger JS code snippets in your templates. For example, with ['{', '}'] you may run into conflicts as the brackets in your JS code may be mistaken for command delimiters. As an alternative, consider using multi-character delimiters, like {# and #} (see issue #102).

Supported commands

Currently supported commands are defined below.

QUERY

You can use GraphQL, SQL, whatever you want: the query will be passed unchanged to your data query resolver.

+++QUERY
query getData($projectId: Int!) {
  project(id: $projectId) {
    name
    details { year }
    people(sortedBy: "name") { name }
  }
}
+++

For the following sections (except where noted), we assume the following dataset:

const data = {
  project: {
    name: 'docx-templates',
    details: { year: '2016' },
    people: [{ name: 'John', since: 2015 }, { name: 'Robert', since: 2010 }],
  },
};

INS (=, or nothing at all)

Inserts the result of a given JavaScript snippet:

+++INS project.name+++ (+++INS project.details.year+++)
or...
+++INS `${project.name} (${$details.year})`+++

Note that the last evaluated expression is inserted into the document, so you can include more complex code if you wish:

+++INS
const a = Math.random();
const b = Math.round((a - 0.5) * 20);
`A number between -10 and 10: ${b}.`
+++

You can also use this shorthand notation:

+++= project.name+++ (+++= project.details.year+++)
+++= `${project.name} (${$details.year})`+++

Even shorter (and with custom cmdDelimiter: ['{', '}']):

{project.name} ({project.details.year})

You can also access functions in the additionalJsContext parameter to createReport(), which may even return a Promise. The resolved value of the Promise will be inserted in the document.

Use JavaScript’s ternary operator to implement an if-else structure:

+++= $details.year != null ? `(${$details.year})` : ''+++

EXEC (!)

Executes a given JavaScript snippet, just like INS or =, but doesn’t insert anything in the document. You can use EXEC, for example, to define functions or constants before using them elsewhere in your template.

+++EXEC
myFun = () => Math.random();
MY_CONSTANT = 3;
+++

+++! ANOTHER_CONSTANT = 5; +++

Usage elsewhere will then look like

+++= MY_CONSTANT +++
+++= ANOTHER_CONSTANT +++
+++= myFun() +++

A note on scoping:

When disabling sandbox mode (noSandbox: true), the scoping behaviour is slightly different. Disabling the sandbox will execute each EXEC snippet’s code in a with(this){...} context, where this is the ‘context’ object. This ‘context’ object is re-used between the code snippets of your template. The critical difference outside of sandbox mode is that you are not declaring functions and variables in the global scope by default. The only way to assign to the global scope is to assign declarations as properties of the context object. This is simplified by the with(context){} wrapper: all global declarations are actually added as properties to this context object. Locally scoped declarations are not. _The above examples should work in both noSandbox: true and noSandbox: false.

This example declares the test function in the context object, making it callable from another snippet.

test = () => {};

While the below example only declares test in the local scope of the snippet, meaning it gets garbage collected after the snippet has executed.

function test() {};

IMAGE

Includes an image with the data resulting from evaluating a JavaScript snippet:

+++IMAGE qrCode(project.url)+++

In this case, we use a function from additionalJsContext object passed to createReport() that looks like this:

  additionalJsContext: {
    qrCode: url => {
      const dataUrl = createQrImage(url, { size: 500 });
      const data = dataUrl.slice('data:image/gif;base64,'.length);
      return { width: 6, height: 6, data, extension: '.gif' };
    },
  }

The JS snippet must return an image object or a Promise of an image object, containing:

  • width: desired width of the image on the page in cm. Note that the aspect ratio should match that of the input image to avoid stretching.
  • height desired height of the image on the page in cm.
  • data: either an ArrayBuffer or a base64 string with the image data
  • extension: one of '.png', '.gif', '.jpg', '.jpeg', '.svg'.
  • thumbnail [optional]: when injecting an SVG image, a fallback non-SVG (png/jpg/gif, etc.) image can be provided. This thumbnail is used when SVG images are not supported (e.g. older versions of Word) or when the document is previewed by e.g. Windows Explorer. See usage example below.
  • alt [optional]: optional alt text.
  • rotation [optional]: optional rotation in degrees, with positive angles moving clockwise.

In the .docx template:

+++IMAGE injectSvg()+++

In the createReport call:

additionalJsContext: {
  injectSvg: () => {
      const svg_data = Buffer.from(`<svg  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                                  <rect x="10" y="10" height="100" width="100" style="stroke:#ff0000; fill: #0000ff"/>
                                </svg>`, 'utf-8');

      // Providing a thumbnail is technically optional, as newer versions of Word will just ignore it.
      const thumbnail = {
        data: fs.readFileSync('sample.png'),
        extension: '.png',
      };
      return { width: 6, height: 6, data: svg_data, extension: '.svg', thumbnail };                    
    }
  }

LINK

Includes a hyperlink with the data resulting from evaluating a JavaScript snippet:

+++LINK ({ url: project.url, label: project.name })+++

If the label is not specified, the URL is used as a label.

HTML

Takes the HTML resulting from evaluating a JavaScript snippet and converts it to Word contents (using altchunk):

+++HTML `
<meta charset="UTF-8">
<body>
  <h1>${$film.title}</h1>
  <h3>${$film.releaseDate.slice(0, 4)}</h3>
  <p>
    <strong style="color: red;">This paragraph should be red and strong</strong>
  </p>
</body>
`+++

FOR and END-FOR

Loop over a group of elements (resulting from the evaluation of a JavaScript expression):

+++FOR person IN project.people+++
+++INS $person.name+++ (since +++INS $person.since+++)
+++END-FOR person+++

Since JavaScript expressions are supported, you can for example filter the loop domain:

+++FOR person IN project.people.filter(person => person.since > 2013)+++
...

FOR loops also work over table rows:

----------------------------------------------------------
| Name                         | Since                   |
----------------------------------------------------------
| +++FOR person IN             |                         |
| project.people+++            |                         |
----------------------------------------------------------
| +++INS $person.name+++       | +++INS $person.since+++ |
----------------------------------------------------------
| +++END-FOR person+++         |                         |
----------------------------------------------------------

Finally, you can nest loops (this example assumes a different data set):

+++FOR company IN companies+++
+++INS $company.name+++
+++FOR person IN $company.people+++
* +++INS $person.firstName+++
+++FOR project IN $person.projects+++
    - +++INS $project.name+++
+++END-FOR project+++
+++END-FOR person+++

+++END-FOR company+++

IF and END-IF

Include contents conditionally (depending on the evaluation of a JavaScript expression):

+++IF person.name === 'Guillermo'+++
+++= person.fullName +++
+++END-IF+++

Similarly to the FOR command, it also works over table rows. You can also nest IF commands
and mix & match IF and FOR commands. In fact, for the technically inclined: the IF command
is implemented as a FOR command with 1 or 0 iterations, depending on the expression value.

ALIAS (and alias resolution with *)

Define a name for a complete command (especially useful for formatting tables):

+++ALIAS name INS $person.name+++
+++ALIAS since INS $person.since+++

----------------------------------------------------------
| Name                         | Since                   |
----------------------------------------------------------
| +++FOR person IN             |                         |
| project.people+++            |                         |
----------------------------------------------------------
| +++*name+++                  | +++*since+++            |
----------------------------------------------------------
| +++END-FOR person+++         |                         |
----------------------------------------------------------

Error handling

By default, the Promise returned by createReport will reject with an error immediately when a problem is encountered in the template, such as a bad command (i.e. it ‘fails fast’). In some cases, however, you may want to collect all errors that may exist in the template before failing. For example, this is useful when you are letting your users create templates interactively. You can disable fast-failing by providing the failFast: false parameter as shown below. This will make createReport reject with an array of errors instead of a single error so you can get a more complete picture of what is wrong with the template.

try {
  const report = await createReport({
    template,
    data: {
      name: 'John',
      surname: 'Appleseed',
    },
    failFast: false,
  });
} catch (errors) {
  if (Array.isArray(errors)) {
    // An array of errors likely caused by bad commands in the template.
    console.log(errors);
  } else {
    // Not an array of template errors, indicating something more serious.
    throw errors;
  }
}

Error types

The library exposes the following error types. See the errors.ts module for details.

NullishCommandResultError // thrown when rejectNullish is set to true and a command returns null or undefined
ObjectCommandResultError // thrown when the result of an `INS` command is an Object. This ensures you don't accidentally put `'[object Object]'` in your report.
CommandSyntaxError
InvalidCommandError
CommandExecutionError
ImageError
InternalError
TemplateParseError

Custom error handler

A custom error handler callback can be provided to handle any errors that may occur when executing commands from a template. The value returned by this callback will be inserted into the rendered document instead. The callback is provided with two arguments: the error that was caught and the raw code of the command.

  const report = await createReport({
    template,
    data: {
      name: 'John',
      surname: 'Appleseed',
    },
    errorHandler: (err, command_code) => {
      return 'command failed!';
    },
  });

Using a custom errorHandler in combination with rejectNullish = true allows users to intelligently replace the result of commands that returned null or undefined (make sure to check for NullishCommandResultError).

Inspecting templates

The listCommands function lets you list all the commands in a docx template using the same parser as createReport.

import { listCommands } from 'docx-templates';
const template_buffer = fs.readFileSync('template.docx');
const commands = await listCommands(template_buffer, ['{', '}']);

// `commands` will contain something like:
[
  { raw: 'INS some_variable', code: 'some_variable', type: 'INS' },
  { raw: 'IMAGE svgImgFile()', code: 'svgImgFile()', type: 'IMAGE' },
]

The getMetadata function lets you extract the metadata fields from a document, such as the number of pages or words. Note that this feature has a few limitations:

  • Not all fields may be available, depending on the document.
  • These metadata fields, including the number of pages, are only updated by MS Word (or LibreOffice) when saving the document. Docx-templates does not alter these metadata fields, so the number of pages may not reflect the actual size of your rendered document (see issue #240). Docx-templates can not reliably determine the number of pages in a document, as this requires a full-fledged docx renderer (e.g. MS Word).
    import { getMetadata } from 'docx-templates';
    const template = fs.readFileSync('template.docx');
    await getMetadata(template)
    // result:
      Object {
        "category": undefined,
        "characters": 24,
        "company": undefined,
        "created": "2015-08-16T18:55:00Z",
        "creator": "Someone Else",
        "description": undefined,
        "lastModifiedBy": "Grau Panea, Guillermo",
        "lastPrinted": undefined,
        "lines": 1,
        "modified": "2016-12-15T11:21:00Z",
        "pages": 1,
        "paragraphs": 1,
        "revision": "32",
        "subject": undefined,
        "template": "Normal.dotm",
        "title": undefined,
        "words": 4,
      }

Performance & security

Templates can contain arbitrary javascript code. Beware of code injection risks!

Obviously, this is less of an issue when running docx-templates in a browser environment.

Regardless of whether you are using sandboxing or not (noSandbox: true), be aware that allowing users to upload arbitrary templates to be executed on your server poses a significant security threat. Use at your own risk.

The library uses require('vm') as its default sandboxing environment. Note that this sandbox is explicitly not meant to be used as a security mechanism. You can provide your own sandboxing environment if you want, as shown in this example project.

Note that turning off the sandbox (noSandbox: true) is known to give significant performance improvements when working with large templates or datasets. However, before you do this, make sure you are aware of the security implications.

Changelog

Similar projects

  • docxtemplater (believe it or not, I just discovered this very similarly-named project after brushing up my old CS code for docx-templates and publishing it for the first time!). It provides lots of goodies, but doesn’t allow (AFAIK) embedding queries or JS snippets.

  • docx and similar ones — generate docx files from scratch, programmatically. Drawbacks of this approach: they typically do not support all Word features, and producing a complex document can be challenging.

License (MIT)

Copyright (c) Guillermo Grau Panea 2016-now

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the «Software»), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Понравилась статья? Поделить с друзьями:
  • Node js import syntaxerror unexpected reserved word
  • Node js excel parser
  • No word for fun in russian
  • Node js excel import
  • Node js excel export