With the image module, you will be able to add a dynamic image to your document, by placing a single {%image}
tag in your document.
This module support Word, Powerpoint and Excel. (For excel you also need access to the xlsx module).
The data source to fetch the image can be synchronous (using doc.render(/* data */)
) or asynchronous (using doc.renderAsync(/* data */)
) :
https://docxtemplater.com/xt-pro-white.png
Your docx/pptx or xlsx template should contain the text: {%image}
or {%%image}
.
%
is used for inline images;{%image}
or {%logo}
which will use the "logo" data;%%
is used for block images;{%%image}
or{%%logo}
which will use the "logo" data;The Image block tag {%%logo}
works only with paragraphs, not with line breaks. See this tip in the FAQ to understand how to view and replace line breaks.
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 = {
getImage(tagValue, tagName, meta) {
console.log({ tagValue, tagName, meta });
return fs.readFileSync(tagValue);
},
getSize(img) {
// 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);
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 = {
getImage(tagValue) {
// In this case tagValue will be a URL tagValue = "https://docxtemplater.com/puffin.png"
return new Promise(function (resolve, reject) {
PizZipUtils.getBinaryContent(
tagValue,
function (error, content) {
if (error) {
return reject(error);
}
return resolve(content);
}
);
});
},
getSize(img, tagValue, tagName) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.src = tagValue;
image.onload = function () {
resolve([image.width, image.height]);
};
image.onerror = function (e) {
console.log(
"img, tagValue, tagName : ",
img,
tagValue,
tagName
);
alert(
"An error occured while loading " +
tagValue
);
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
.
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 :
and your data:
{
"myImage": "sampleImage.png"
}
The arguments passed to the getImage function are the following :
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
getImage
function returnedOne 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];
},
};
const doc = new Docxtemplater(zip, {
modules: new ImageModule(imageOptions),
});
doc.render({
image: "image1.png",
});
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 :
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(img, tagValue, tagName, context) {
const part = context.part;
if (
part.module ===
"open-xml-templating/docxtemplater-replace-image-module"
) {
return [part.width, part.height];
}
const buffer = Buffer.from(img, "binary");
const sizeObj = sizeOf(buffer);
return [sizeObj.width, sizeObj.height];
},
};
const doc = new Docxtemplater(zip, {
modules: [new ImageModule(imageOptions)],
});
doc.render(data);
You can use base64 images with the following code
const base64Regex =
/^(?:data:)?image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;
const validBase64 =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(tagValue) {
if (
typeof tagValue !== "string" ||
!base64Regex.test(tagValue)
) {
return false;
}
const stringBase64 = tagValue.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) {
return base64Parser(tagValue);
},
getSize(img, tagValue, tagName, context) {
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==",
});
It is possible to get images asynchronously by returning a Promise in the getImage
function and use the docxtemplater renderAsync 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|jpeg|svg|svg\+xml);base64,/;
const validBase64 =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(tagValue) {
if (
typeof tagValue !== "string" ||
!base64Regex.test(tagValue)
) {
return false;
}
const stringBase64 = tagValue.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, context) {
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();
}
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, // return usual value, using fs or any other method
// getSize, // return usual value
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.
You can center the images using {%%image}
instead of {%image}
in your documents.
If you prefer to have all your images be centered by default, you can also change your prefix configuration like this :
const imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
prefix: {
centered: "%",
normal: "%%",
},
};
const doc = new Docxtemplater(zip, {
modules: [new ImageModule(imageOptions)],
});
doc.render(data);
This means that all tags that look like : {%image} will be considered as centered images, and {%%image} as normal images.
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, // DON'T FORGET TO DECLARE IT
getSize(img, tagValue, 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);
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);
You can also use angular expressions to set image sizes :
In your template, write :
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|jpeg|svg|svg\+xml);base64,/;
const validBase64 =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(tagValue) {
if (
typeof tagValue !== "string" ||
!base64Regex.test(tagValue)
) {
return false;
}
const stringBase64 = tagValue.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) {
if (tagValue.size && tagValue.data) {
return base64Parser(tagValue.data);
}
if (tagValue.maxSize && tagValue.data) {
return base64Parser(tagValue.data);
}
return base64Parser(tagValue);
},
getSize(img, tagValue, tagName) {
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);
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);
It is possible to retrieve image from a canvas value, by customizing the getImage
option.
const base64Regex =
/^(?:data:)?image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;
const validBase64 =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(tagValue) {
if (
typeof tagValue !== "string" ||
!base64Regex.test(tagValue)
) {
return false;
}
const stringBase64 = tagValue.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());
},
// getSize, // return usual value, using image-size or other method
};
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.
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|jpeg|svg|svg\+xml);base64,/;
const validBase64 =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
function base64Parser(tagValue) {
if (
typeof tagValue !== "string" ||
!base64Regex.test(tagValue)
) {
return false;
}
const stringBase64 = tagValue.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'"
)
);
});
},
};
For docx, it is possible to add captions with the following code :
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, // return usual value, using fs or any other method
// getSize, // return usual value
};
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 :
Here is an example with all options :
const imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
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",
},
};
},
};
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 :
In your code, write :
const imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
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: "",
},
},
},
});
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 imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
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,
};
},
};
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 {
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);
For docx, it is possible to change the alignment of a block image with the following code :
const imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
getProps() {
/*
* If you don't want to change the props
* for a given tagValue, you should write :
*
* return null;
*/
return {
align: "right",
};
},
};
It is possible since version 3.27.0 of the image module to add borders to an image, like this :
const imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
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",
},
};
},
};
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 imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
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.",
};
},
};
It is possible to add a link to an image, by using the following
const imageOptions = {
// getImage, // return usual value, using fs or any other method
// getSize, // return usual value
getProps() {
/*
* If you don't want to change the props
* for a given tagValue, you should write :
*
* return null;
*/
return {
link: "https://duckduckgo.com/",
};
},
};
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 :
And the following will work
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".
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.
If you try to add a loop with an image inside it :
You will get following error message :
There are two ways to fix this issue, one uses table cells with a background image, the other uses the grid-table module
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 :
When using the "table cell background" feature, there is an issue that you can have if your text content is also dynamic and it increases the size of the table row containing the image, in this case, the image will not respect the aspect ratio because the size of the image is calculated depending on the size of the cell in the template, docxtemplater doesn't know what the final height of the row will be. A trick to handle this well is to create an additional table row just for the image, and then merge the two cells on the right where there is no image tag.
See following template : Download pptx file :
See also this trick to use Jimp to reuse the same background color as the background color of the cell in the template.
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 :
companies is an array of objects representing each company.
It will generate output similar to this :
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(img) {
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",
},
],
});
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 renderAsync)
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 {
resolve(null);
}
});
}
const imageOptions = {
// getImage, // return usual value, using fs or any other method
getSize(img) {
const sizeOf = require("image-size");
const buffer = Buffer.from(img, "binary");
const sizeObj = sizeOf(buffer);
return scaleWithExif(
[sizeObj.width, sizeObj.height],
img
);
},
};
const doc = new Docxtemplater(zip, {
modules: [new ImageModule(imageOptions)],
});
doc.renderAsync(data).then(function () {
console.log("rendered");
});
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 = {
// getSize, // return usual value
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;
},
};
Throw RenderingError instead of InternalError when having invalid images (webp, other unsupported formats).
Bugfix to not try to template `
Add missing lodash dependency in package.json
A bug was introduced in version 3.29.2, when having an image inside a cell, and that cell defines its color using a theme.
The following error was shown :
Now, the part.fillColor
is properly set in this case.
Add fillColor
property to part (the background color of the existing table) in powerpoint tables.
This allows to do the following with jimp, so that if you add an image as a background of a cell, the background color used for the image will match the background color of the initial cell :
const { Jimp } = require("jimp");
const fs = require("fs");
const pptxCellFill =
"open-xml-templating/docxtemplater-pptx-cell-fill-image-module";
async function getImageBuffer(tagValue) {
try {
// Replace this function content by the way you want to retrieve the image.
// For example fetching the image from a URL using the example from here :
// https://docxtemplater.com/modules/image/#fetch-image-from-url
return await fs.promises.readFile(tagValue);
} catch (error) {
throw new Error(`Error reading file: ${error.message}`);
}
}
const imageOptions = {
async getImage(tagValue, tagName, meta) {
if (meta.part.module !== pptxCellFill) {
return getImageBuffer(tagValue);
}
return getImageBuffer(tagValue).then(function (buffer) {
return Jimp.read(buffer).then(function (image) {
let baseWidth = image.width;
let baseHeight = image.height;
const { containerWidth, containerHeight } =
meta.part;
if (baseWidth > containerWidth) {
baseHeight =
(baseHeight * containerWidth) /
baseWidth;
baseWidth = containerWidth;
}
if (baseHeight > containerHeight) {
baseWidth =
(baseWidth * containerHeight) /
baseHeight;
baseHeight = containerHeight;
}
const fillColor =
meta.part && meta.part.fillColor
? meta.part.fillColor
: // use transparent for background if there is no fillColor from the template
0xffffff00;
const background = new Jimp({
width: containerWidth,
height: containerHeight,
color: fillColor,
});
const xOffset = (containerWidth - baseWidth) / 2;
const yOffset =
(containerHeight - baseHeight) / 2;
image.scaleToFit({
w: Math.floor(baseWidth),
h: Math.floor(baseHeight),
});
background.composite(image, xOffset, yOffset);
return background.getBuffer(image.mime);
});
});
},
getSize: function getImageSize(img, _, __, meta) {
if (meta.part.module === pptxCellFill) {
return [
meta.part.containerWidth,
meta.part.containerHeight,
];
}
const sizeOf = require("image-size");
if (typeof img === "string") {
img = Buffer.from(img, "binary");
}
const sizeObj = sizeOf(img);
return [sizeObj.width, sizeObj.height];
},
getProps(img, x, y, meta) {
if (meta.part.module === pptxCellFill) {
return {
imageFit: "fill",
};
}
},
};
const doc = new Docxtemplater(zip, {
paragraphLoop: true,
linebreaks: true,
modules: [new ImageModule(imageOptions)],
});
doc.renderAsync({
image: "image.png",
});
Add properties.explanation of the error to the root explanation when the image fails to load.
For xlsx files, improve centering of images, using the new official width calculation algorithm. This requires the XLSXModule version 3.23.0
Add support for centering of images when using {%%image}
in xlsx files.
(This needs version 3.21.1 of the xlsx module or higher).
Calculate "containerHeight" if placing the {%image} tag inside a Word table, and the given table row uses the <w:trHeight hRule="auto" w:val="11111">
tag.
This can be changed in the UI using the "exact" mode for the height of the row.
If the hRule is set to "atLeast", there will be no "containerHeight" that will be set because the row height will be automatically increased if the content is bigger.
Show correct error message if resolving fails on tags with attributes inside alt-text
For powerpoint documents, add "slideWidth" and "slideHeight" attributes to image parts.
When using the following in your image description :
The result document will contain the following :
Previously, the change delimiter tag was not working correctly in this context.
Make it possible to use prefix from the constructor
Add rootError when getProps or getImage fails
Allow to template image description text, alternate text.
Add typescript typings to be able to change the module prefix
Add support to add borders using getProps.
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')"
Make it possible to set align: "right"
in getProps
even on replaced images.
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).
Add "containerType" key to each part, which contains one of the following string :
Correctly calculate the width of each table cell, even when using colGrid and when having merged cells.
Add { part, options }
as third argument to getImage
function.
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.
Upgrade module to use NodeNext moduleResolution setting. See explanation here
Bugfix issue when using a document that contains a <w:sectPrChange>
(which is a tracked "section" change, ie a tracked "header" or "footer" change :
Bugfix issue when using a document that has a <v:shape>
which has a multiline "alt" attribute.
Use new resolvedId
API => requires docxtemplater@3.43.0
Better handle errors thrown in getProps, getSize or getImage, in synchronous and async rendering.
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.
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.
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
Bugfix to set or get xml attributes correctly, even when using tabs instead of spaces in tags attributes
Set module.priority in order to have no more issues related to module ordering
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 :
Now, those tags do not cause any errors, and are simply replaced by "".
Bugfix caption height calculation when using :
doc.renderAsync()
)Correctly add part.section
even when tag is inside a loop
This release needs version 3.40.1 of docxtemplater open-source.
Add correct typings for part.section
and part.pageHeight
Make image module compatible with docxtemplater@3.40.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, …).
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.
In typescript typings, allow to return false
from getImage function.
Add support to replace the cover photo of a document
Throw specific error if trying to insert a webp image.
Previously, the error message was :
Now the message is :
Add typescript typings for the following :
{
caption?: {
text: string;
pStyle?: string;
align?: string;
prefix?: string | Seq[];
height?: integer;
}
}
Add typescript typings for the following :
{imageFit?: string, flipHorizontal?: boolean, flipVertical?: boolean, rotation?: number}
Bugfix to allow getProps to return a Promise.
Add new feature for Powerpoint documents to add multiple images to one slide.
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(img) {
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, …
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(img) {
return [150, 150];
},
};
const doc = new Docxtemplater(zip, {
modules: [new ImageModule(imageOptions)],
});
doc.render(data);
Add support for setting the pStyle of the generated paragraph using getProps.
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.
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
Bugfix error "Buffer is not defined" when using in browser in version 3.18.1 or 3.18.0
Bugfix error "No Media with relM Rid (null) found" when having an image that uses a r:link
instead of a r:embed
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.
Bugfix for working well with xlsx module in some particular case.
If internally, the tag was using a
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.
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
.
Using getProps, it is now possible to add a link to an image.
Using getProps, it is now possible to return a dpi per image.
Throw specific error if the data returned by getImage()
is not a string, a Buffer, or an ArrayBuffer.
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.
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.
Bugfix "Error: could not find extension for this image".
This happened for some jpeg files that didn't contain any metadata.
Avoid possible issue of "Maximum call stack size exceeded"
Throw error when using a Promise in getImage in sync mode.
Correctly store png files as ".png" extension, ".jpeg" as ".jpeg", …
Previously all files where stored as ".png", regardless of actual data.
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.
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.
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
Bugfix to avoid using lodash, in previous versions, the following message would appear :
Error: Cannot find module 'lodash'
Bugfix to avoid corruption when inserting SVG.
The corruption was caused by a <pic:cNvPr>
tag which didn't have a name attribute
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
Add support to be able to customize the name and alt text of replaced images using getProps
.
Update to make module compatible with docxtemplater@3.30.0
Fixes following error :
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.
Add support for setting the name
and alt
attribute of the image programmatically using the getProps
function.
Improve typescript typings (sizePixel, svgSize, getProps, getDxaWidth)
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);
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.
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);
On Powerpoint documents, bugfix to correctly calculate size of images that are replaced using the "description" field
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".
When used together with the xlsx module v3.9.0 or higher, Add possibility to replace images in Excel documents (using the description field).
Avoid removing hdphoto images from the template by mistake.
This was causing a corruption in the generated document
Bugfix slideMasters (and images in slideMasters) are now correctly templated
Avoid corrupting images that contain "instrText" and "INCLUDEPICTURE"
Add `
Add support for returning containerWidth when having a <v:rect>
element
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.
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
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(tagValue) {
// Usual getImage function
},
getSize(img, tagValue) {
return ["1cm", "1in"];
},
}),
],
});
doc.render(data);
The list of allowed units is :
px
=> the default unitin
=> an inch, which is 2.54cmpt
=> a point, which is 1/72th of an inchcm
=> a centimetermm
=> a millimeterpc
=> a pica, which is 1/12th of a pointemu
=> english metric unit, which is 1/914400th of an inch, and is the unit used by Word to store most sizes internally.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
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.
Bugfix issue when using together with the subtemplate module.
The error message : "ParseError: Attribute name redefined" could appear
Add support to change an existing image by using {%image} in the "alt-text" name of the image.
Call nullGetter in async mode when the value returned by the parser is falsy.
Update typings for getImage to allow Uint8Array, ArrayBuffer, …
Add typings for async mode (getSize can also return a Promise
Add internal clone method (to use image tags with the Subtemplate.Segment module)
Use @xmldom/xmldom instead of xmldom, see this github issue
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.
Add typescript definitions for public API
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
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
When having hyperlinks that contain "/media" in the target, the generated document would be corrupt. Now the document is not made corrupt anymore.
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.
Don't fail when placing the {%image} tag inside a table element.
Bugfix to make Image replacement work in async mode (you need access to both modules image and xlsx)
Add support for ImageModule together with XLSXModule to insert images with {%image} (you need access to both modules)
Do not fail when using the tag {%image} on "generic" shapes, eg those defined in the slideLayout
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)
Use SEQ Figure instead of SEQ Illustration for Captions
Declare supportedFileTypes, which allows to use this module with the new docxtemplater constructor which was introduced in docxtemplater 3.17.
Better bugfix to remove unused images (works with other modules)
Bugfix No section found
appeared :
Add getSVGFallback
option to allow to convert the svg to png in JS
Bugfix when using image module together with pptx-subtemplate module, the images were removed
Bugfix to allow to have inline loop of inline images
Improve error message when putting block image inside inline loop
(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.
Fix issue "No section found" when having an image in a footer
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
vfill
new Buffer
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).
Add part.extPx and part.offsetPx to be able to know the size of the placeholder in pptx in pixel unit.
Fix corruption when having centered image inside table and return no img
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.
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.
Bugfix in asyncmode, errors are now correctly thrown
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.
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
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.
Update browser build to use XMLSerializer instead of xmldom
Use requiredAPIVersion
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);
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.
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.
Add SVG support (but this format is only readable on newer Word version : Microsoft Word, PowerPoint, Outlook, and Excel on the Office 365 subscription).
Add meta context argument to custom parser with information about the tag for each types of tags
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).
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")
Mark package as private in package.json
Fix corruption when reusing multiple times the docPrId
Reuse Target when possible
Do not add relationship for files docProps/app.xml and docProps/core.xml
Fix unit tests
Files are no longer corrupted when using the option "center"
The image module now throws an error if the given size is invalid.
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.