You are currently offline, serving cached version

Usage

It is possible to retrieve data from any data source (synchronously or asynchronously).

For example :

  • from base64 data
  • from the filesystem
  • from a url like : https://docxtemplater.com/xt-pro-white.png
  • from an Amazon S3 stored image

The tag should be prefixed by % to be parsed as an image. So for example, you could use :{%image} which will take the data from the image key, or {%secondImage} to take the data from the secondImage key.

Usage (nodejs)

The below example is in nodejs, to read the image data from the filesystem :

const ImageModule = require("docxtemplater-image-module");
const fs = require("fs");

const imageOptions = {
    centered: false,
    getImage(tagValue, tagName, meta) {
        console.log({ tagValue, tagName, meta });
        return fs.readFileSync(tagValue);
    },
    getSize() {
        // it also is possible to return a size in centimeters, like this : return [ "2cm", "3cm" ];
        return [150, 150];
    },
};

const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render({ image: "examples/image.png" });

const buffer = doc.getZip().generate({
    type: "nodebuffer",
    compression: "DEFLATE",
});

fs.writeFile("test.docx", buffer);

Usage (browser)

In the browser, this shows how to get the image from a URL :

<html>
    <script src="node_modules/docxtemplater/build/docxtemplater.js"></script>
    <script src="node_modules/pizzip/dist/pizzip.js"></script>
    <script src="node_modules/pizzip/vendor/FileSaver.js"></script>
    <script src="node_modules/pizzip/dist/pizzip-utils.js"></script>
    <script src="build/imagemodule.js"></script>
    <script>
        // To use the image module, you have to be using docxtemplater async
        // (you should use doc.renderAsync(data), not doc.render(data)).
        const imageOptions = {
            centered: false,
            getImage(url) {
                return new Promise(function (resolve, reject) {
                    PizZipUtils.getBinaryContent(
                        url,
                        function (error, content) {
                            if (error) {
                                return reject(error);
                            }
                            return resolve(content);
                        }
                    );
                });
            },
            getSize(img, url, tagName) {
                return new Promise(function (resolve, reject) {
                    const image = new Image();
                    image.src = url;
                    image.onload = function () {
                        resolve([image.width, image.height]);
                    };
                    image.onerror = function (e) {
                        console.log(
                            "img, url, tagName : ",
                            img,
                            url,
                            tagName
                        );
                        alert(
                            "An error occured while loading " +
                                url
                        );
                        reject(e);
                    };
                });
            },
        };
        const docxType =
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        PizZipUtils.getBinaryContent(
            "examples/image-example.docx",
            function (error, content) {
                if (error) {
                    console.error(error);
                    return;
                }

                const zip = new PizZip(content);
                const doc = new docxtemplater(zip, {
                    modules: [new ImageModule(imageOptions)],
                });

                doc.renderAsync({
                    image: "examples/image.png",
                }).then(function () {
                    const out = doc.getZip().generate({
                        type: "blob",
                        mimeType: docxType,
                    });
                    saveAs(out, "generated.docx");
                });
            }
        );
    </script>
</html>

After installing the module, you can use a working demo by running node sample.js.

Configuring getImage

Here is some explanation about how to configure your "getImage" function.

This function determines which picture to show for a given tag.

If your template is :

{%myImage}

and your data:

{
    "myImage": "sampleImage.png"
}

The arguments passed to the getImage function are the following :

  • tagValue will be equal to "sampleImage.png"
  • tagName will be equal to "myImage"
  • meta is an object containing part and options keys. In the part key, you'll find the containerWidth attribute telling you the width of the container element. In the options key, you'll find options.filePath which will be "word/document.xml" if that image is present in the main document, or "word/header1.xml" if the image is in the first header of the document.

In the getSize function, the first argument will be : img

  • img will be what ever the getImage function returned

One of the most useful cases of this is to set the images to be the size of that image.

For this, you will need to install the npm package 'image-size' then, write:

const sizeOf = require("image-size");
const fs = require("fs");

const imageOptions = {
    getImage(tagValue) {
        return fs.readFileSync(tagValue, "binary");
    },
    getSize(img) {
        const sizeObj = sizeOf(img);
        console.log(sizeObj);
        return [sizeObj.width, sizeObj.height];
    },
    centered: false,
};
const doc = new Docxtemplater(zip, {
    modules: new ImageModule(imageOptions),
});
doc.render({
    image: "image1.png",
});

Replace an existing image

It is possible to replace an existing image in the document instead of adding a new image. This is possible since version 3.9.0 of the image module.

To replace an image, you can write {%image1} in the alt-text attribute, or the name of the image, and the image will be replaced. Note that the width will be updated depending on the getSize function.

To replace images in header or replace them in footers, you can use the "alt-text" property.

Inside Microsoft Word on Windows, you can update the alt-text by right clicking on the image :

Alt text demo1
Step 1: Click Edit Alt Text

Alt text demo2
Step 2: Change the alt text

It is possible to keep the size of the existing image by using the following function for getSize :

const sizeOf = require("image-size");

const imageOptions = {
    // getImage, // DON'T FORGET TO DECLARE IT
    getSize(image, data, tagValue, options) {
        const part = options.part;
        if (
            part.module ===
            "open-xml-templating/docxtemplater-replace-image-module"
        ) {
            return [part.width, part.height];
        }

        // return usual value, using image-size or other method.
        const buffer = Buffer.from(image, "binary");
        const sizeObj = sizeOf(buffer);
        return [sizeObj.width, sizeObj.height];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

Base64 include

You can use base64 images with the following code

const base64Regex =
    /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
const validBase64 =
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;

function base64Parser(dataURL) {
    if (
        typeof dataURL !== "string" ||
        !base64Regex.test(dataURL)
    ) {
        return false;
    }

    const stringBase64 = dataURL.replace(base64Regex, "");

    if (!validBase64.test(stringBase64)) {
        throw new Error(
            "Error parsing base64 data, your data contains invalid characters"
        );
    }

    // For nodejs, return a Buffer
    if (typeof Buffer !== "undefined" && Buffer.from) {
        return Buffer.from(stringBase64, "base64");
    }

    // For browsers, return a string (of binary content) :
    const binaryString = window.atob(stringBase64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        const ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    }
    return bytes.buffer;
}
const imageOptions = {
    getImage(tag) {
        return base64Parser(tag);
    },
    getSize() {
        return [100, 100];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render({
    image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QIJBywfp3IOswAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAkUlEQVQY052PMQqDQBREZ1f/d1kUm3SxkeAF/FdIjpOcw2vpKcRWCwsRPMFPsaIQSIoMr5pXDGNUFd9j8TOn7kRW71fvO5HTq6qqtnWtzh20IqE3YXtL0zyKwAROQLQ5l/c9gHjfKK6wMZjADE6s49Dver4/smEAc2CuqgwAYI5jU9NcxhHEy60sni986H9+vwG1yDHfK1jitgAAAABJRU5ErkJggg==",
});

Fetch image from url

It is possible to get images asynchronously by returning a Promise in the getImage function and use the docxtemplater async api.

Here is an example in node that allows you to retrieve values from an URL and use a fixed width, and keep the aspect ratio.

const fs = require("fs");
const Docxtemplater = require("docxtemplater");
const https = require("https");
const http = require("http");
const Stream = require("stream").Transform;
const ImageModule = require("docxtemplater-image-module");
const PizZip = require("pizzip");

const content = fs.readFileSync("demo_template.docx");

const base64Regex =
    /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
const validBase64 =
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;

function base64Parser(dataURL) {
    if (
        typeof dataURL !== "string" ||
        !base64Regex.test(dataURL)
    ) {
        return false;
    }

    const stringBase64 = dataURL.replace(base64Regex, "");

    if (!validBase64.test(stringBase64)) {
        throw new Error(
            "Error parsing base64 data, your data contains invalid characters"
        );
    }

    // For nodejs, return a Buffer
    if (typeof Buffer !== "undefined" && Buffer.from) {
        return Buffer.from(stringBase64, "base64");
    }

    // For browsers, return a string (of binary content) :
    const binaryString = window.atob(stringBase64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        const ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    }
    return bytes.buffer;
}

const data = { image: "https://docxtemplater.com/xt-pro.png" };

const imageOptions = {
    getImage(tagValue, tagName) {
        console.log(tagValue, tagName);
        const base64Value = base64Parser(tagValue);
        if (base64Value) {
            return base64Value;
        }
        // tagValue is "https://docxtemplater.com/xt-pro-white.png"
        // tagName is "image"
        return new Promise(function (resolve, reject) {
            getHttpData(tagValue, function (err, data) {
                if (err) {
                    return reject(err);
                }
                resolve(data);
            });
        });
    },
    getSize(img, tagValue, tagName) {
        console.log(tagValue, tagName);
        // img is the value that was returned by getImage
        // This is to force the width to 600px, but keep the same aspect ratio
        const sizeOf = require("image-size");
        const sizeObj = sizeOf(img);
        console.log(sizeObj);
        const forceWidth = 600;
        const ratio = forceWidth / sizeObj.width;
        return [
            forceWidth,
            // calculate height taking into account aspect ratio
            Math.round(sizeObj.height * ratio),
        ];
    },
};

const zip = new PizZip(content);
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});

doc.renderAsync(data)
    .then(function () {
        const buffer = doc.getZip().generate({
            type: "nodebuffer",
            compression: "DEFLATE",
        });

        fs.writeFileSync("test.docx", buffer);
        console.log("rendered");
    })
    .catch(function (error) {
        console.log("An error occured", error);
    });

// Returns a Promise<Buffer> of the image
function getHttpData(url, callback) {
    (url.substr(0, 5) === "https" ? https : http)
        .request(url, function (response) {
            if (response.statusCode !== 200) {
                return callback(
                    new Error(
                        `Request to ${url} failed, status code: ${response.statusCode}`
                    )
                );
            }

            const data = new Stream();
            response.on("data", function (chunk) {
                data.push(chunk);
            });
            response.on("end", function () {
                callback(null, data.read());
            });
            response.on("error", function (e) {
                callback(e);
            });
        })
        .end();
}

Pixel to Inch conversions

Pixels and Inches are two very different units, conceptually.

Pixels represent not a physical length, but a logical length. Screens can be the same physical size (13 inches for example) and have different amount of pixels (640px or 1920px for example).

The images in the image-module are specified in pixels, and Word is a format that is made for print, Word stores all dimensions in inches (or fractions of inches). The unit used for images is usually "DXA", where 1 inch is equal to 1440 DXAS. This unit is also called a "twip".

To translate pixels to inches, the image-module uses the concept of "dpi", or "dots per inches", which is a way to convert the image sizes returned by the "getSize" function which are in pixel unit, and convert them to inches.

This is done with the formula :

sizeInInch = sizeInPixel / dpi

By default, the dpi is set to 96, which means 96px will be translated to one inch.

You can change the dpi by setting it in the options, like this :

const imageOptions = {
    // getImage(tagValue) {
    //     ...
    // },
    // getSize(img) {
    //     ...
    // },
    dpi: 120,
};

const doc = new Docxtemplater(zip, {
    modules: new ImageModule(imageOptions),
});
doc.render(data);

To have the image module scale the images in exactly the same way as Microsoft word does, you should use a dpi of 224.23.

Centering images

You can center the images using opts.centered = true or by using {%%image} instead of {%image} in your documents

Avoid images bigger than their container

If you include some images inside tables, it is possible that you get some images that are not shown completely because they are bigger than their container.

To fix this issue, you can do the following to never overflow the containerWidth and keep the aspect ratio

const sizeOf = require("image-size");
const imageOptions = {
    // getImage: as usual
    getSize(img, value, tagName, context) {
        const sizeObj = sizeOf(img);
        const maxWidth = context.part.containerWidth;
        const maxHeight =
            context.part.containerHeight ||
            context.part.containerWidth;

        const widthRatio = sizeObj.width / maxWidth;
        const heightRatio = sizeObj.height / maxHeight;
        if (widthRatio < 1 && heightRatio < 1) {
            /*
             * Do not scale up images that are
             * smaller than maxWidth,maxHeight
             */
            return [sizeObj.width, sizeObj.height];
        }
        let finalWidth, finalHeight;
        if (widthRatio > heightRatio) {
            /*
             * Width will be equal to maxWidth
             * because width is the most "limiting"
             */
            finalWidth = maxWidth;
            finalHeight = sizeObj.height / widthRatio;
        } else {
            /*
             * Height will be equal to maxHeight
             * because height is the most "limiting"
             */
            finalHeight = maxHeight;
            finalWidth = sizeObj.width / heightRatio;
        }

        return [Math.round(finalWidth), Math.round(finalHeight)];
    },
};

const doc = new Docxtemplater(zip, {
    modules: new ImageModule(imageOptions),
});
doc.render(data);

Size and path based on placeholder

You can customize the image loader using the template's placeholder name.

const fs = require("fs");
const path = require("path");

const imageOptions = {
    getImage(tagValue, tagName) {
        if (tagName === "logo") {
            return fs.readFileSync(
                path.resolve(__dirname, "logos", tagValue)
            );
        }

        return fs.readFileSync(
            path.resolve(__dirname, "images", tagValue)
        );
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

The same thing can be used to customize image size.

const imageOptions = {
    getSize(img, tagValue, tagName) {
        if (tagName === "logo") {
            return [100, 100];
        }

        return [300, 300];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

Size with angular-parser

You can also use angular expressions to set image sizes :

In your template, write :

(This will force the aspect ratio to 2/1)
{%image | size:200:100}
or, to keep the aspect ratio by specifying a max size
{%image | maxSize:200:100}

First argument is the width, second argument is the height.

In your code, you would write the following :

const expressionParser = require("docxtemplater/expressions.js");
expressionParser.filters.size = function (input, width, height) {
    return {
        data: input,
        size: [width, height],
    };
};
// This one sets a max size, allowing to keep the aspect ratio of the image,
// but ensuring that the width and height do not exceed the limits specified
expressionParser.filters.maxSize = function (
    input,
    width,
    height
) {
    return {
        data: input,
        maxSize: [width, height],
    };
};

const base64Regex =
    /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
const validBase64 =
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(dataURL) {
    if (
        typeof dataURL !== "string" ||
        !base64Regex.test(dataURL)
    ) {
        return false;
    }

    const stringBase64 = dataURL.replace(base64Regex, "");

    if (!validBase64.test(stringBase64)) {
        throw new Error(
            "Error parsing base64 data, your data contains invalid characters"
        );
    }

    // For nodejs, return a Buffer
    if (typeof Buffer !== "undefined" && Buffer.from) {
        return Buffer.from(stringBase64, "base64");
    }

    // For browsers, return a string (of binary content) :
    const binaryString = window.atob(stringBase64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        const ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    }
    return bytes.buffer;
}

const imageOptions = {
    getImage(tagValue) {
        if (tagValue.size && tagValue.data) {
            return base64Parser(tagValue.data);
        }
        if (tagValue.maxSize && tagValue.data) {
            return base64Parser(tagValue.data);
        }
        return base64Parser(tagValue);
    },
    getSize(img, tagValue) {
        if (tagValue.size && tagValue.data) {
            return tagValue.size;
        }
        if (!tagValue.maxSize) {
            return [150, 150];
        }

        const maxWidth = tagValue.maxSize[0];
        const maxHeight = tagValue.maxSize[1];
        const sizeOf = require("image-size");
        const sizeObj = sizeOf(img);

        const widthRatio = sizeObj.width / maxWidth;
        const heightRatio = sizeObj.height / maxHeight;
        if (widthRatio < 1 && heightRatio < 1) {
            /*
             * Do not scale up images that are
             * smaller than maxWidth,maxHeight
             */
            return [sizeObj.width, sizeOf.height];
        }
        let finalWidth, finalHeight;
        if (widthRatio > heightRatio) {
            /*
             * Width will be equal to maxWidth
             * because width is the most "limiting"
             */
            finalWidth = maxWidth;
            finalHeight = sizeObj.height / widthRatio;
        } else {
            /*
             * Height will be equal to maxHeight
             * because height is the most "limiting"
             */
            finalHeight = maxHeight;
            finalWidth = sizeObj.width / heightRatio;
        }

        return [Math.round(finalWidth), Math.round(finalHeight)];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
    parser: expressionParser,
});
doc.render(data);

Max width and max height

It is quite likely that you would want to limit the width and the height of your images, to do this, you could use the following code in your getSize function

const imageOptions = {
    getSize(img) {
        // img is the value that was returned by getImage
        // This is to limit the width and height of the resulting image
        const maxWidth = 1000;
        const maxHeight = 800;
        const sizeOf = require("image-size");
        const sizeObj = sizeOf(img);
        const widthRatio = sizeObj.width / maxWidth;
        const heightRatio = sizeObj.height / maxHeight;
        if (widthRatio < 1 && heightRatio < 1) {
            /*
             * Do not scale up images that are
             * smaller than maxWidth,maxHeight
             */
            return [sizeObj.width, sizeObj.height];
        }
        let finalWidth, finalHeight;
        if (widthRatio > heightRatio) {
            /*
             * Width will be equal to maxWidth
             * because width is the most "limiting"
             */
            finalWidth = maxWidth;
            finalHeight = sizeObj.height / widthRatio;
        } else {
            /*
             * Height will be equal to maxHeight
             * because height is the most "limiting"
             */
            finalHeight = maxHeight;
            finalWidth = sizeObj.width / heightRatio;
        }
        return [Math.round(finalWidth), Math.round(finalHeight)];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

Retrieve image from <canvas>

It is possible to retrieve image from a canvas value, by customizing the getImage option.

const base64Regex =
    /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
const validBase64 =
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(dataURL) {
    if (
        typeof dataURL !== "string" ||
        !base64Regex.test(dataURL)
    ) {
        return false;
    }

    const stringBase64 = dataURL.replace(base64Regex, "");

    if (!validBase64.test(stringBase64)) {
        throw new Error(
            "Error parsing base64 data, your data contains invalid characters"
        );
    }

    // For nodejs, return a Buffer
    if (typeof Buffer !== "undefined" && Buffer.from) {
        return Buffer.from(stringBase64, "base64");
    }

    // For browsers, return a string (of binary content) :
    const binaryString = window.atob(stringBase64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        const ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    }
    return bytes.buffer;
}
const imageOptions = {
    getImage(tagValue, tagName) {
        console.log({ tagValue, tagName });
        const canvas = document.getElementById("myCanvas");
        const ctx = canvas.getContext("2d");
        ctx.beginPath();
        ctx.arc(100, 75, 50, 0, 2 * Math.PI);
        ctx.stroke();
        return base64Parser(canvas.toDataURL());
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

Note that you can change that function so that depending on the tagvalue, either the image is loaded from canvas or from a URL.

SVG support

SVGs can be added, but this format is only readable on newer Word version : Microsoft Word, PowerPoint, Outlook, and Excel on the Office 365 subscription.

See this article for details about this feature.

When SVG is not supported in your reader (libreoffice, google-docs, Word 2013), you will see the image "SVG unsupported" instead.

You can configure the image that is shown as a fallback if SVG is not supported, by defining the getSVGFallback method. You can use this method to convert the SVG to png so that all docx readers are able to see the SVG. In the below example, I use graphicsmagick to do the conversion : sudo apt-get install -y graphicsmagick

const imageOptions = {
    getSVGFallback(svgFile, sizePixel) {
        const result = require("child_process").spawnSync(
            "gm",
            [
                "convert",
                "SVG:-",
                "-resize",
                sizePixel.join("x"),
                "PNG:-",
            ],
            {
                input: new Buffer(svgFile),
            }
        );
        if (result.status !== 0) {
            console.error(
                JSON.stringify({
                    "result.stderr": result.stderr.toString(),
                })
            );
            throw new Error(
                "Error while executing graphicsmagick"
            );
        }
        return new Buffer(result.stdout);
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

In the browser, you can use the following to transform the SVG's into PNG :

const base64Regex =
    /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
const validBase64 =
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(dataURL) {
    if (
        typeof dataURL !== "string" ||
        !base64Regex.test(dataURL)
    ) {
        return false;
    }

    const stringBase64 = dataURL.replace(base64Regex, "");

    if (!validBase64.test(stringBase64)) {
        throw new Error(
            "Error parsing base64 data, your data contains invalid characters"
        );
    }

    // For nodejs, return a Buffer
    if (typeof Buffer !== "undefined" && Buffer.from) {
        return Buffer.from(stringBase64, "base64");
    }

    // For browsers, return a string (of binary content) :
    const binaryString = window.atob(stringBase64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        const ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    }
    return bytes.buffer;
}
const imageOptions = {
    getSVGFallback(svgFile, sizePixel) {
        function arrayBufferToString(buffer) {
            let binary = "";
            const bytes = new Uint8Array(buffer);
            const len = bytes.byteLength;
            for (let i = 0; i < len; i++) {
                binary += String.fromCharCode(bytes[i]);
            }
            return binary;
        }

        return new Promise(function (resolve, reject) {
            function svgUrlToPng(svgUrl) {
                const svgImage = document.createElement("img");
                svgImage.style.position = "absolute";
                svgImage.style.top = "-9999px";
                document.body.appendChild(svgImage);
                const width = sizePixel[0];
                const height = sizePixel[1];
                svgImage.width = width;
                svgImage.height = height;
                svgImage.onload = function () {
                    const canvas =
                        document.createElement("canvas");
                    canvas.width = width;
                    canvas.height = height;
                    const canvasCtx = canvas.getContext("2d");
                    canvasCtx.drawImage(
                        svgImage,
                        0,
                        0,
                        width,
                        height
                    );
                    const imgData =
                        canvas.toDataURL("image/png");
                    resolve(base64Parser(imgData));
                };
                svgImage.onerror = function () {
                    reject(
                        new Error(
                            "Could not transform svg to png"
                        )
                    );
                };
                svgImage.src =
                    "data:image/svg+xml;utf8," + svgUrl;
            }
            svgUrlToPng(
                arrayBufferToString(svgFile).replace(
                    "<svg",
                    "<svg xmlns='http://www.w3.org/2000/svg'"
                )
            );
        });
    },
};

Adding Captions

For docx, it is possible to add captions with the following code :

const fs = require("fs");
const imageOptions = {
    getProps(img, tagValue, tagName) {
        console.log("getProps", {
            img: img.length,
            tagValue,
            tagName,
        });
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            caption: {
                text: "My custom caption",
            },
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

Note that there are two types of tags, as noted in the beginning of this documentation page : inline tags {%image} and block tags {%%image}. When using block tags and adding captions, the image will be inserted into a block drawing followed by a normal paragraph. When using an inline block tag with captions, a box will be created that will contain both the image and another textbox that will contain the caption.

It is possible to add following options :

  • pStyle: The paragraph style that should be used for the caption
  • align: The align property for the caption ("left", "right", "center")
  • prefix: By default, the prefix will be Illustration 1, Illustration 2, and so on.
  • height: (This parameter is only used when the tag is an inline tag: see above note). By default, the height is 51px, but this can be changed to an other value

Here is an example with all options :

const fs = require("fs");
const imageOptions = {
    getProps(img, tagValue, tagName) {
        console.log("getProps", {
            img: img.length,
            tagValue,
            tagName,
        });
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            caption: {
                text: "My custom caption",
                prefix: [
                    "Figura ",
                    { seq: "SEQ Figure \\* ARABIC" },
                ],
                pStyle: "Caption",
                align: "right",
            },
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

For the prefix, it is possible to disable the prefix completely by using prefix: "".

One way of using this feature with all data coming from the render() call, would be to move the options into your data.

In your template, write :

{%photo1}
{%photo2}

In your code, write :

const fs = require("fs");
const imageOptions = {
    getImage(tagValue) {
        return fs.readFileSync(tagValue.img);
    },
    getSize() {
        return [150, 150];
    },
    getProps(img, tagValue, tagName) {
        console.log("getProps", {
            img: img.length,
            tagValue,
            tagName,
        });
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return tagValue.props;
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render({
    photo1: {
        img: "pictures/image1.png",
        props: {
            alt: "The first image",
            caption: {
                text: "My first caption",
                prefix: "",
            },
        },
    },

    photo2: {
        img: "pictures/image2.png",
        props: {
            alt: "The other image",
            caption: {
                text: "My first caption",
                prefix: "",
            },
        },
    },
});

Rotating / flipping image

The rotation attribute in return of the "getProps" function allows to rotate the image of a specified number of degrees (90 is quarter of a turn, clockwise)

It is possible to rotate or flip the image with the following code :

const fs = require("fs");

const imageOptions = {
    getProps(img, tagValue, tagName) {
        console.log("getProps", {
            img: img.length,
            tagValue,
            tagName,
        });
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            rotation: 90,
            // flipVertical: true,
            // flipHorizontal: true,
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};

It is possible to take into account exif data in order to to the correct flipping/rotation.

Use following code for this :

const ExifImage = require("exif").ExifImage;

function getOrientation(result, image) {
    return new Promise(function (resolve, reject) {
        if (typeof image === "string") {
            image = Buffer.from(image, "binary");
        }
        try {
            new ExifImage({ image }, function (error, exifData) {
                if (error) {
                    reject(null);
                    console.log("Error: " + error.message);
                } else {
                    const image = exifData.image;
                    const orientation = image.Orientation;
                    switch (orientation) {
                        case 2:
                            return resolve({
                                flipHorizontal: true,
                            });
                        case 3:
                            return resolve({ rotation: 180 });
                        case 4:
                            return resolve({
                                flipVertical: true,
                            });
                        case 5:
                            return resolve({
                                rotation: 270,
                                flipHorizontal: true,
                            });
                        case 7:
                            return resolve({
                                rotation: 90,
                                flipHorizontal: true,
                            });
                        case 8:
                            return resolve({ rotation: 90 });
                        default:
                            return resolve({});
                    }
                }
            });
        } catch (error) {
            resolve(null);
        }
    });
}
const imageOptions = {
    // getImage, // return usual value, using fs or any other method
    // getSize, // return usual value
    getProps(image) {
        return getOrientation(image);
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

Changing Alignment

For docx, it is possible to change the alignment of a block image with the following code :

const fs = require("fs");

const imageOptions = {
    getProps() {
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            align: "right",
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};

Adding border around image

It is possible since version 3.27.0 of the image module to add borders to an image, like this :

const fs = require("fs");
const imageOptions = {
    getProps() {
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            border: {
                color: "#713ffe",
                size: 12,
                type: "sng",
            },
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};

Changing name or alternative text

It is possible to customize the alternative text (the text shown on non-visual editors) using the getProps "alt" attribute. The "name" attribute is used to set the name of the image.

const fs = require("fs");
const imageOptions = {
    getProps() {
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            name: "myimage.png",
            alt: "A cat looking playfully at a mouse.",
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};

Adding a link to an image

It is possible to add a link to an image, by using the following

const fs = require("fs");
const imageOptions = {
    getProps() {
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            link: "https://duckduckgo.com/",
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};

Centered Images should be placed in empty paragraphs

For the imagereplacer to work, the image tag: {%image} needs to be in its own paragraph (<w:p> tag in Word), so that means that you have to put a new line after and before the tag.

The following template will throw that error :

Foo
Hello {%image}
Bar

And the following will work

Foo
Hello
{%image}
Bar

Powerpoint notice

If you put images in a box that also contains some other text (content, title, or even whitespace , you will get an error :

To fix this error, you have to create a box that contains the {%...} and only this text, no other whitespace nor content.

For example, the following template will fail, because the shape containing the text {%image} also contains "Some content" and "Other content".

Fail
Invalid template

For example, the following template will succeed, because there are 3 different shapes, and the shape containing the {%image} tag contains no other text. Note that the width/height of the generated image will be the same as the one from the shape.

Succeed
Correct template

Images inside loop for Powerpoint files

If you try to add a loop with an image inside it :

{#images}
{%src}
{/}

You will get following error message :

The image tag "src" should not be placed inside a loop, it should be the only text in a given shape

There are two ways to fix this issue, one uses table cells with a background image, the other uses the grid-table module

Adding multiple images to Powerpoint presentations using table cell background

When placing the tag : {%image} in a table cell in a pptx document, the module sets the background-image of that cell.

This makes it possible to create a loop with multiple images inside a table :

Pptx table with loop
Template pptx
Pptx table with loop
Generated pptx

Generating multiple images in one slide using the grid module

To include multiple images in a given slide, you need to have both the "Table" module, the "Slides" module and the "Image module".

You can generate multiple "slides" in a grid layout with following template :

grid-template-pptx
Grid template for multiple slides

companies is an array of objects representing each company.

It will generate output similar to this :

grid-template-pptx-rendered
Generated document

In your code, do the following

const TableModule = require("docxtemplater-table-module");
const SlidesModule = require("docxtemplater-slides-module");
const ImageModule = require("docxtemplater-image-module");
const fs = require("fs");

const imageOptions = {
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};
const doc = new Docxtemplater(zip, {
    modules: [
        new TableModule.GridPptx(),
        new SlidesModule(),
        new ImageModule(imageOptions),
    ],
});

doc.render({
    companies: [
        {
            name: "first company",
            id: "first id",
            logo: "2",
            region: "france",
        },
        {
            name: "second company",
            id: "second",
            region: "germany",
        },
        {
            name: "third",
            id: "third id",
            logo: "4",
            region: "russia",
        },
        {
            name: "fourth",
            id: "fourth id",
            region: "italy",
        },
    ],
});

Take into account XResolution (from the jpeg metadata)

JPEG images often have metadata about the XResolution, which means they can have a specific dpi as metadata to the image. When copy-pasting a JPEG image into Microsoft Word, Microsoft Word will read the XResolution of the image and size the image accordingly.

With the image-module, usually the getSize function only will read the size of the image in pixels, but not the metadata.

If you need docxtemplater to behave like Microsoft Word, you could use an Exif extractor that will give you the XResolution. Here is an example of how you can do that (you need to use async rendering)

const ExifImage = require("exif").ExifImage;

function scaleWithExif(result, image) {
    return new Promise(function (resolve) {
        if (typeof image === "string") {
            image = Buffer.from(image, "binary");
        }
        try {
            new ExifImage({ image }, function (error, exifData) {
                if (error) {
                    resolve(null);
                    console.log("Error: " + error.message);
                } else {
                    const image = exifData.image;
                    const unit = image.ResolutionUnit;
                    const res = image.XResolution;
                    let scaleFactor;
                    if (unit === 1) {
                        scaleFactor = 1;
                    } else if (unit === 2) {
                        scaleFactor = 96 / res;
                    } else if (unit === 3) {
                        // dots per centimeter => dots per inch conversion
                        scaleFactor = 96 / res / 2.54;
                    }
                    console.log(JSON.stringify({ scaleFactor }));
                    console.log(JSON.stringify({ result }));
                    resolve([
                        result[0] * scaleFactor,
                        result[1] * scaleFactor,
                    ]);
                }
            });
        } catch (error) {
            resolve(null);
        }
    });
}
const imageOptions = {
    // getImage, // return usual value, using fs or any other method
    getSize(image) {
        const sizeOf = require("image-size");
        const buffer = Buffer.from(image, "binary");
        const sizeObj = sizeOf(buffer);
        return scaleWithExif(
            [sizeObj.width, sizeObj.height],
            image
        );
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.renderAsync(data).then(function () {
    console.log("rendered");
});

Handling webp images

Webp is a format that Microsoft Word will not accept, so that is why docxtemplater doesn't accept it either.

const childProcess = require("child_process");
const fs = require("fs");

const imageOptions = {
    centered: false,
    getImage(tagValue) {
        const baseImg = fs.readFileSync(tagValue);
        // 82,73,70,70 is in hexformat : 52 49 46 46
        // which is the signature of webp files
        if (
            baseImg[0] === 82 &&
            baseImg[1] === 73 &&
            baseImg[2] === 70 &&
            baseImg[3] === 70
        ) {
            return new Promise(function (resolve, reject) {
                const result = childProcess.spawnSync(
                    "gm",
                    ["convert", "-", "-format", "png", "png:-"],
                    {
                        input: baseImg,
                    }
                );
                if (result.status !== 0) {
                    console.error(
                        JSON.stringify({
                            "result.stderr":
                                result.stderr.toString(),
                        })
                    );
                    reject(
                        new Error(
                            "Error while executing graphicsmagick"
                        )
                    );
                }
                return resolve(Buffer.from(result.stdout));
            });
        }
        return baseImg;
    },
    getSize() {
        return [150, 150];
    },
};

CHANGELOG

3.28.4

Show correct error message if resolving fails on tags with attributes inside alt-text

3.28.3

For powerpoint documents, add "slideWidth" and "slideHeight" attributes to image parts.

3.28.2

When using the following in your image description :

{=[[ ]]=}{%image}[[{ }]]

The result document will contain the following :

{%image}

Previously, the change delimiter tag was not working correctly in this context.

3.28.1

Make it possible to use prefix from the constructor

Add rootError when getProps or getImage fails

3.28.0

Allow to template image description text, alternate text.

3.27.1

Add typescript typings to be able to change the module prefix

3.27.0

Add support to add borders using getProps.

3.26.2

If the parser throws an error, in async mode, the error shown should be a multi error.

The error shown in previous versions was : "Cannot read properties of undefined (reading 'length')"

3.26.1

Make it possible to set align: "right" in getProps even on replaced images.

3.26.0

Add support for inserting SVG's into Powerpoint and Excel files.

In previous versions, this would silently do nothing (ie the template would have no image at all when inserting SVG's in both these file formats).

3.25.0

Add "containerType" key to each part, which contains one of the following string :

  • if the container is a textbox, "textbox"
  • if the container is a table cell, "table-cell"
  • if the container is a picture, "picture"
  • if the container is a page, "page"

Correctly calculate the width of each table cell, even when using colGrid and when having merged cells.

3.24.5

Add { part, options } as third argument to getImage function.

3.24.4

Update handling of nullGetter.

Now, the nullGetter function will be called only if the value returned by the parser is null or undefined.

For example, now, if the value returned by the parser is "", then, the nullGetter function will not be called and no image will be shown.

3.24.3

Upgrade module to use NodeNext moduleResolution setting. See explanation here

3.24.2

Bugfix issue when using a document that contains a <w:sectPrChange> (which is a tracked "section" change, ie a tracked "header" or "footer" change :

TypeError: Cannot read properties of null (reading 'map')
at collectSectionsWidth (get-dimensions.js)
at Object.collect (es6/get-dimensions.js)
at ImageModule.preparse (es6/index.js)
at docxtemplater/js/parser.js
at Array.forEach (<anonymous>)
at preparse (docxtemplater/js/parser.js)
at Object.preparse (docxtemplater/js/parser.js)
at XmlTemplater.preparse (docxtemplater/js/xml-templater.js)
at Docxtemplater.precompileFile (docxtemplater/js/docxtemplater.js)
at docxtemplater/js/docxtemplater.js
at Array.forEach (<anonymous>)
at Docxtemplater.compile (docxtemplater/js/docxtemplater.js)
at new Docxtemplater (docxtemplater/js/docxtemplater.js)
at createDocV4 (docxtemplater/js/tests/utils.js)
at Context.loadAndCompile (es6/test.js)
at Context.loadAndRender (es6/test.js)
at Context.<anonymous> (es6/test.js)
at process.processImmediate (node:internal/timers)

Bugfix issue when using a document that has a <v:shape> which has a multiline "alt" attribute.

TypeError: Cannot read properties of null (reading 'split')
at collectPicts (es6/get-dimensions.js)
at Object.collect (es6/get-dimensions.js)
at ImageModule.preparse (es6/index.js)
at docxtemplater/js/parser.js
at Array.forEach (<anonymous>)
at preparse (docxtemplater/js/parser.js)
at Object.preparse (docxtemplater/js/parser.js)
at XmlTemplater.preparse (docxtemplater/js/xml-templater.js)
at Docxtemplater.precompileFile (docxtemplater/js/docxtemplater.js)
at docxtemplater/js/docxtemplater.js
at Array.forEach (<anonymous>)
at Docxtemplater.compile (docxtemplater/js/docxtemplater.js)
at new Docxtemplater (docxtemplater/js/docxtemplater.js)
at createDocV4 (docxtemplater/js/tests/utils.js)
at Context.loadAndCompile (es6/test.js)
at Context.loadAndRender (es6/test.js)
at Context.<anonymous> (es6/test.js)
at process.processImmediate (node:internal/timers)

3.24.1

Use new resolvedId API => requires docxtemplater@3.43.0

3.24.0

Better handle errors thrown in getProps, getSize or getImage, in synchronous and async rendering.

3.23.11

Bugfix when used together with the xlsx module 3.15.0

It would produce a corrupt document when placing a tag in the worksheet name.

Now the document is valid again.

3.23.10

Allow to insert emf images (use correct content-type of image/x-emf)

Bugfix when used together with subtemplate module, images inside the footnotes/endnotes would sometimes not appear.

3.23.9

Fix bug when using renderAsync and parser compilation fails : the error should now be shown inside a multierror. To to this, we know use sm.getValueAsync in resolve instead of sm.getValue

3.23.8

Bugfix to set or get xml attributes correctly, even when using tabs instead of spaces in tags attributes

3.23.7

Set module.priority in order to have no more issues related to module ordering

3.23.6

With pptx documents, bugfix when having a title that contains a tag, such as {%image}

In this case, the title would also be stored in app.xml (inside a <vt:lpstr> tag), and the following error would be thrown :

Image tag should not be placed inside a loop
at new XTTemplateError (node_modules/docxtemplater/js/errors.js:21:16)
at throwExpandNotFound (node_modules/docxtemplater/js/errors.js:174:13)

Now, those tags do not cause any errors, and are simply replaced by "".

3.23.5

Bugfix caption height calculation when using :

  • async mode (with doc.renderAsync())
  • a caption

3.23.4

  • Correctly calculate pageHeight by removing marginTop and marginBottom
  • Add pageWidth

3.23.3

Correctly add part.section even when tag is inside a loop

This release needs version 3.40.1 of docxtemplater open-source.

3.23.2

Add correct typings for part.section and part.pageHeight

3.23.1

Make image module compatible with docxtemplater@3.40.0

3.23.0

Add support to retrieve the pageHeight of the section that contains the current tag.

(The value is in pixels)

const imageOptions = {
    // getImage: as usual
    getSize(img, value, tagName, context) {
        const pageHeight = context.part.pageHeight;
        console.log(pageHeight); // will log 1112
        return [100, 100];
        // or return anything you like
    },
};

It is also now possible to view the context.part.section for more information about the current section (number of columns, …).

3.22.2

Add support for setting "offset" property for image module in xlsx file types.

Previously, following code had no effect on the image module for xlsx files :

const imageOptions = {
    // getImage,
    // getSize,
    getProps() {
        return {
            offset: [200, 10],
        };
    },
};
const doc = new Docxtemplater(zip, {
    modules: [new ImageModule(imageOptions)],
});
doc.render(data);

Now, the code works correctly.

3.22.1

In typescript typings, allow to return false from getImage function.

3.22.0

Add support to replace the cover photo of a document

3.21.4

Throw specific error if trying to insert a webp image.

Previously, the error message was :

could not find extension for this image [82, 73, 70, 70]

Now the message is :

webp images are not supported

3.21.3

Add typescript typings for the following :

  • getProps return value :
{
    caption?: {
        text: string;
        pStyle?: string;
        align?: string;
        prefix?: string | Seq[];
        height?: integer;
    }
}

3.21.2

Add typescript typings for the following :

  • getProps return value : {imageFit?: string, flipHorizontal?: boolean, flipVertical?: boolean, rotation?: number}

3.21.1

Bugfix to allow getProps to return a Promise.

3.21.0

Add new feature for Powerpoint documents to add multiple images to one slide.

3.20.1

Add support to set the caption height for inline images (in pixels).

Only for inline images, it is now possible to set the height of a given image using following code :

const fs = require("fs");

const imageOptions = {
    getProps() {
        /*
         * If you don't want to change the props
         * for a given tagValue, you should write :
         *
         * return null;
         */
        return {
            caption: {
                text: "My custom caption",
                // The default height is 51 (in pixels)
                height: 80,
            },
        };
    },
    getImage(tagValue) {
        return fs.readFileSync(tagValue);
    },
    getSize() {
        return [150, 150];
    },
};

For block images, the caption is in a paragraph part of the document that will automatically expand the space depending on font-size, …

3.20.0

Multiple bugfixes/features for captions :

  • Now, the pStyle for the caption and the pStyle for the image will be applied correctly, even for inline images.

  • Add feature to change the prefix for captions. For example, it is possible to do this :

    const fs = require("fs");
    const imageOptions = {
        getProps() {
            /*
             * If you don't want to change the props
             * for a given tagValue, you should write :
             *
             * return null;
             */
            return {
                caption: {
                    text: "My custom caption",
                    pStyle: "Caption",
                    // This is to remove the prefix
                    prefix: "",
                },
            };
        },
        getImage(tagValue) {
            return fs.readFileSync(tagValue);
        },
        getSize() {
            return [150, 150];
        },
    };
    const doc = new Docxtemplater(zip, {
        modules: [new ImageModule(imageOptions)],
    });
    doc.render(data);
    

3.19.2

Add support for setting the pStyle of the generated paragraph using getProps.

3.19.1

Two bugfixes :

If an image has a Target="NULL" in its relationship, the image module will no more fail with an error.

When using the imagemodule together with the styling module, sometimes you could see the message on docx files :

The tag {:styleshape style} is not allowed in an image description. Place the {:styleshape stylre} tag in a separate visible textbox on the slide."

This won't happen anymore with the image module on docx files.

3.19.0

Bugfix when used together with the xlsx module :

If the image is an image that uses the "description" tag, and is present inside a loop, the image is now correctly replaced.

This module requires version 3.12.0 of the xlsx module or higher

3.18.2

Bugfix error "Buffer is not defined" when using in browser in version 3.18.1 or 3.18.0

3.18.1

Bugfix error "No Media with relM Rid (null) found" when having an image that uses a r:link instead of a r:embed

3.18.0

Since this version, if you include twice the same image (for example a green check mark that is used 10 times in your generated document), the resulting docx will not duplicate the image internally, and instead it will reuse the same image.

It does so by doing comparisons between Buffers, ArrayBuffers, or Strings.

3.17.2

Bugfix for working well with xlsx module in some particular case.

If internally, the tag was using atag but not atag, the image size could not be changed or calculated when using the "replace-image" feature.

3.17.1

Bugfix to correctly calculate containerWidth when having a sectPr defined at the beginning of a paragraph.

Specifically, in the following case :

<w:p>
    <w:sectPr>
        xxxx
    </w:sectPr>
    <w:r><w:t>Paragraph</w:t></w:r>
</w:p>

Normally, only text before the sectPr should be part of that section, but it actually applies to the "Paragraph" part as well.

3.17.0

Remove option to set a dpi per image. Instead, one should change the getSize function to do some scaling.

Update calculation of containerWidth to work well with w:cols which have w:space.

3.16.4

Using getProps, it is now possible to add a link to an image.

3.16.3

Using getProps, it is now possible to return a dpi per image.

3.16.2

Throw specific error if the data returned by getImage() is not a string, a Buffer, or an ArrayBuffer.

3.16.1

Throw error if placing a {:data} tag in the description of an image.

Only tag starting with a % are allowed in the description of an image, such as {%img}.

{:data} tags are tags of the slide module and need to be placed inside a textbox, as visible text. This text will be removed by the slides module.

3.16.0

Add support for keeping an existing image, when using the "replacement module".

When using the replacement module, if the value of the tag is "true", it will keep the existing image unchanged.

For example, if the image has the description {%img}, if you set the data like this :

doc.render({
    img: true,
});

It will keep the existing image.

3.15.6

Bugfix "Error: could not find extension for this image".

This happened for some jpeg files that didn't contain any metadata.

3.15.5

Avoid possible issue of "Maximum call stack size exceeded"

3.15.4

Throw error when using a Promise in getImage in sync mode.

3.15.3

Correctly store png files as ".png" extension, ".jpeg" as ".jpeg", …

Previously all files where stored as ".png", regardless of actual data.

3.15.2

Bugfix of a bug that removed some images present in the document (that use the type : "http://purl.oclc.org/ooxml/officeDocument/relationships/image")

Previously those images would be removed from the template by mistake.

Now those images are kept as intended.

3.15.1

Bugfix when using together with xlsx module on multiple slides.

Fixes Github Issue #672

In previous versions, when entering multiple {%image} tags on multiple slides, the images would not appear at all.

Now all images appear correctly.

3.15.0

Add support for rotating pictures with the "rotation" attribute in getProps

Add support for flipping pictures horizontally or vertically with the "flipVertical" or "flipHorizontal" attribute in getProps

3.14.6

Bugfix to avoid using lodash, in previous versions, the following message would appear :

Error: Cannot find module 'lodash'

3.14.5

Bugfix to avoid corruption when inserting SVG.

The corruption was caused by a <pic:cNvPr> tag which didn't have a name attribute

3.14.4

When using the replace image module with an SVG image, the replacement would not show up on Microsoft Word.

Now, the svg image is dropped, thus showing the replaced png image

3.14.3

Add support to be able to customize the name and alt text of replaced images using getProps.

3.14.2

Update to make module compatible with docxtemplater@3.30.0

Fixes following error :

TypeError: Cannot read properties of undefined (reading 'droppedTagsInsidePlaceholder')
at parse (js/parser.js:170:46)

3.14.1

Avoid corruption when using img { display: block; margin:auto; } with images inside <li> elements.

See : Github Issue #655

Now, images that are inside li elements will always be rendered as "inline-block" elements.

3.14.0

Add support for setting the name and alt attribute of the image programmatically using the getProps function.

3.13.6

Improve typescript typings (sizePixel, svgSize, getProps, getDxaWidth)

3.13.5

Add access to the "path" property of the image replacement module.

This allows to for example retrieve all placeholders and the linked images.

const InspectModule = require("docxtemplater/js/inspect-module.js");

const imageOptions = {
    /* getImage, getSize function should be defined */
};
const imageModule = new ImageModule(imageOptions);
const iModule = InspectModule();
const doc = new Docxtemplater(zip, {
    modules: [imageModule, iModule],
});
const tags = iModule.getStructuredTags();
// tags will be an array, you could filter for the module name :
// open-xml-templating/docxtemplater-replace-image-module
tags.forEach(function (part) {
    if (
        part.module ===
        "open-xml-templating/docxtemplater-replace-image-module"
    ) {
        console.log(
            "Tag",
            part.value,
            "which path is at ",
            part.path
        );
        const buffer = zip.file(part.path).asArrayBuffer();
        console.log(buffer.length);
    }
});
doc.render(data);

3.13.4

Bugfix on offset when replacing an image.

Previously, if the offset value was [0, 100] or [100, 0], the fact that one value was zero made the offset not apply at all.

Now, the offset works even when one value is zero.

3.13.3

On Powerpoint documents, when using the replace-image feature, allow to set an offset to move the image a certain amount of pixels. This allows for example to keep an image centered (see below example)

const doc = new Docxtemplater(zip, {
    modules: [
        new ImageModule({
            // getImage, // Usual getImage function using fs or other method
            // getSize, // Usual getSize function
            getProps(img, data, tagValue, options) {
                const { part, sizePixel } = options;
                if (
                    part.module ===
                        "open-xml-templating/docxtemplater-replace-image-module" &&
                    part.width &&
                    part.height
                ) {
                    return {
                        offset: [
                            part.width / 2 - sizePixel[0] / 2,
                            part.height / 2 - sizePixel[1] / 2,
                        ],
                    };
                }
            },
        }),
    ],
});
doc.render(data);

3.13.2

On Powerpoint documents, bugfix to correctly calculate size of images that are replaced using the "description" field

3.13.1

On Excel documents, bugfix to use correct width/height when replacing an image using the description field. Internally, we now convert the "twoCellAnchor" element into a "oneCellAnchor".

3.13.0

When used together with the xlsx module v3.9.0 or higher, Add possibility to replace images in Excel documents (using the description field).

3.12.5

Avoid removing hdphoto images from the template by mistake.

This was causing a corruption in the generated document

3.12.4

Bugfix slideMasters (and images in slideMasters) are now correctly templated

3.12.3

Avoid corrupting images that contain "instrText" and "INCLUDEPICTURE"

3.12.2

Add `to all images

3.12.1

Add support for returning containerWidth when having a <v:rect> element

3.12.0

Make module compatible with docxtemplater@3.28.0. Please make sure to update docxtemplater to 3.28.0 at the same time you update this module. The internal change made is the use of the new matchers API which fixes bugs that were triggered depending on the order of the modules that are attached to the instance. Now the order of the modules should not matter as expected.

3.11.0

Make module compatible with docxtemplater@3.27.0. Please make sure to update docxtemplater to 3.27.0 at the same time you update this module

3.10.0

Bugfix to correctly retrieve the width/height of the image when the image is in the header or footer when trying to replace an image.

Add support for other units for getSize (by default, the unit is in px when using integers).

You can now write :

const doc = new Docxtemplater(zip, {
    modules: [
        new ImageModule({
            getImage() {
                // Usual getImage function
            },
            getSize() {
                return ["1cm", "1in"];
            },
        }),
    ],
});
doc.render(data);

The list of allowed units is :

  • px => the default unit
  • in => an inch, which is 2.54cm
  • pt => a point, which is 1/72th of an inch
  • cm => a centimeter
  • mm => a millimeter
  • pc => a pica, which is 1/12th of a point
  • emu => english metric unit, which is 1/914400th of an inch, and is the unit used by Word to store most sizes internally.

3.9.3

Throw TemplateError when having a centered image tag surrounded by text. This makes it possible for the Error-location module to show the error correctly.

Also, throw TemplateError when having other text in a text shape for Powerpoint document

3.9.2

Allow to replace an existing image using the "title" property, which is the property accessible using "alt-text"

This allows to replace images in headers/footers, which do not have the name property.

3.9.1

Bugfix issue when using together with the subtemplate module.

The error message : "ParseError: Attribute name redefined" could appear

3.9.0

Add support to change an existing image by using {%image} in the "alt-text" name of the image.

3.8.14

Call nullGetter in async mode when the value returned by the parser is falsy.

3.8.13

Update typings for getImage to allow Uint8Array, ArrayBuffer, …

3.8.12

Add typings for async mode (getSize can also return a Promise, and getImage can return a Promise) Add typings for options.part.containerWidth

3.8.11

Add internal clone method (to use image tags with the Subtemplate.Segment module)

3.8.10

Use @xmldom/xmldom instead of xmldom, see this github issue

3.8.9

Generate files in built with correct filename In previous versions, the filename was always build/docxtemplater.js. Now the filename is build/imagemodule.js The .min.js file is also created now.

3.8.8

Add typescript definitions for public API

3.8.7

Improve support for columns width, so that images inside columns which are of unequal width will render correctly.

For example, if you have a document with two columns, one of width of 2/3 of the document, and an other one of 1/3 of the document width, the containerWidth property will be correctly calculated.

Previously, the width would be in this case always splitted equally, so you have a wrong containerWidth in this case

3.8.6

Make width and height calculation work when a textbox is inside a v:shape, and the width and height are set in the style attribute

3.8.5

When having hyperlinks that contain "/media" in the target, the generated document would be corrupt. Now the document is not made corrupt anymore.

3.8.4

  • Add support for floating point numbers in getSize.
  • This version is compatible with version 3.25.4 of the HTML module
  • Add currentSection argument to getDxaWidth function

3.8.3

Update to retrieve containerHeight and containerWidth for pptx textboxes and pptx tables.

For docx tables, containerHeight cannot be retrieved because the height depends on the rendering of the fonts and the content of the table, so it cannot be retrieved.

3.8.2

Don't fail when placing the {%image} tag inside a table element.

3.8.1

Bugfix to make Image replacement work in async mode (you need access to both modules image and xlsx)

3.8.0

Add support for ImageModule together with XLSXModule to insert images with {%image} (you need access to both modules)

3.7.9

Do not fail when using the tag {%image} on "generic" shapes, eg those defined in the slideLayout

3.7.8

  • Do not console.log errors, as they are now properly handled by docxtemplater in async mode with loops (with docxtemplater 3.17.7)

  • Avoid corruption on XLSX file when it contains images (the images would be removed by the document)

3.7.7

Use SEQ Figure instead of SEQ Illustration for Captions

3.7.6

Declare supportedFileTypes, which allows to use this module with the new docxtemplater constructor which was introduced in docxtemplater 3.17.

3.7.5

Better bugfix to remove unused images (works with other modules)

3.7.4

Bugfix No section found appeared :

  • when using document with tags inside properties (for Title, Subject, Keywords, …)
  • when using a document generated from onedrive (online office version)

3.7.3

Add getSVGFallback option to allow to convert the svg to png in JS

3.7.2

Bugfix when using image module together with pptx-subtemplate module, the images were removed

3.7.1

Bugfix to allow to have inline loop of inline images

Improve error message when putting block image inside inline loop

3.7.0

(docx) Add support to add captions with module.getProps function.

(docx) Add support for aligning block images to the left or the right with module.getProps function.

3.6.1

Fix issue "No section found" when having an image in a footer

3.6.0

Add possibility to retrieve the containerWidth to be able to use 100% of the containerWidth for example

Add possibility to change deviceWidth and getDxaWidth to change the dpi

3.5.4

  • Bugfix : do not remove images inside vfill
  • Remove usage of new Buffer

3.5.3

Expect to have error when using centered image in a paragraph which contains other text.

When using centered: true option, images containing %% now will show up as normal images (uncentered).

3.5.2

Add part.extPx and part.offsetPx to be able to know the size of the placeholder in pptx in pixel unit.

3.5.1

Fix corruption when having centered image inside table and return no img

3.5.0

Change the way this.opts.centered works.

Before, you would have to write : {%image} for inline images and {%%image} for centered images, and if you used the centered:true option, you would only be able to write centered images (with any of {%image} or {%%image}). Now when you specify opts.centered : true, the standard behavior is just swapped ({%image} for centered images and {%%image} for inline images).

However, in the future, opts.centered : true will be removed and this means that you should rather just change the prefix, like this :

const imageOptions = {
    /* getImage, getSize function should be defined */
};
function getImageModule() {
    const imageModule = new ImageModule(imageOptions);
    imageModule.prefix = {
        normal: "%%",
        centered: "%",
    };
    return imageModule;
}
const doc = new Docxtemplater(zip, {
    modules: [getImageModule()],
});
doc.render(data);

Also, this fixes a bug with SVG that were not shown when using "centered" images.

3.4.4

Bugfix for pptx. Before this release, if the document contained <a:extLst>, the error Images should be placed in new Text Boxes, that are not part of any slide layout was sometimes wrongfully thrown.

3.4.3

Bugfix in asyncmode, errors are now correctly thrown

3.4.2

Bugfix image module loops in async mode. Before this bugfix, images inside loops where sometimes always using the same image.

If you use the HTML module, you should update to 3.13.5 of the HTML module.

3.4.1

Bugfix SVG : async rendering should also work with base64 svgs

If you use the image module with the HTML module, please also update the HTML module to 3.14.4

3.4.0

Auto remove images that are no more needed when using slides module or when using a loop.

This makes the size of the generated docx smaller, and might make the rendering a little bit slower.

3.3.4

  • Update browser build to use XMLSerializer instead of xmldom

  • Use requiredAPIVersion

3.3.3

Make it possible to change prefix and normal prefix :

const imageOptions = {
    /* getImage, getSize function should be defined */
};
function getImageModule() {
    const imageModule = new ImageModule(imageOptions);
    imageModule.prefix = {
        normal: ":image ",
        centered: ":centered ",
    };
    return imageModule;
}
const doc = new Docxtemplater(zip, {
    modules: [getImageModule()],
});
doc.render(data);

3.3.2

  • Move docxtemplater from devDependencies to dependencies

Explanation : On some versions of npm (notably 5.8.0), when having a package containing docxtemplater-image-module, the installation will generate a tree of node_modules that puts the module on a level where it has no access to docxtemplater. By explicitly asking it as a dependency, this issue is avoided.

3.3.1

  • Make module compatible with docxtemplater version 3.5 and below.

Explanation : Recently, the scopemanager API (internal API) has changed, this new version of the image module makes the module work with both versions newer than 3.6 and older than 3.6 of docxtemplater.

3.3.0

Add SVG support (but this format is only readable on newer Word version : Microsoft Word, PowerPoint, Outlook, and Excel on the Office 365 subscription).

3.2.8

Add meta context argument to custom parser with information about the tag for each types of tags

3.2.7

  • Fix issue "size is not defined"

3.2.6

  • Add clearer error message if getSize returns float or not an array of two integers

3.2.5

  • Add fallback to calling nullGetter if getValue returns falsey value

3.2.4

  • Fix "Maximum call stack size exceeeded" when using huge arraybuffers

3.2.3

  • Fix corruption error because of duplicate docPr. The shown error is usually : HRESULT 0x80004005 Location: Part: /word/document.xml, Line: 0, Column: 0

  • Add async support (by returning a Promise in the getImage function).

3.2.2

  • Fix error in browser "Buffer is not defined"

3.2.1

  • Fix regression in 3.2.0 : It should work with Buffer type

3.2.0

  • Throw error if using unsupported filetype (SVG)

  • Throw error if using image in a default box ("Images should be placed in new Text Boxes, that are not part of any slide layout")

3.1.6

Mark package as private in package.json

3.1.5

Fix corruption when reusing multiple times the docPrId

Reuse Target when possible

3.1.4

Do not add relationship for files docProps/app.xml and docProps/core.xml

3.1.3

Fix unit tests

3.1.2

Files are no longer corrupted when using the option "center"

3.1.1

The image module now throws an error if the given size is invalid.

3.1.0

  • Breaking : The image module no longer swallows error thrown by options.getImage and options.getSize. It is the implementors responsibility to not throw any errors, or the error will be passed. You can return a falsy value in getImage to not render an image at all.

3.0.2

  • Fix issue with PPTX : Before, you had to add to your options : {fileType: "pptx"} in the module options passed as argument in the constructor of the module. Now, the fileType is retrieved from the main docxtemplater.

3.0.1

  • Add support for PPTX.
  • Add centering of images with {%%image} syntax

3.0.0

  • This version is compatible with docxtemplater 3.0.
Talk with sales Contact us