diff --git a/es6/docUtils.js b/es6/docUtils.js
index 246db4d..0fc5bf0 100644
--- a/es6/docUtils.js
+++ b/es6/docUtils.js
@@ -1,3 +1,6 @@
"use strict";
const DocUtils = require("docxtemplater").DocUtils;
+DocUtils.convertPixelsToEmus = function (pixel) {
+ return Math.round(pixel * 9525);
+};
module.exports = DocUtils;
diff --git a/es6/imgManager.js b/es6/imgManager.js
index dae66c0..50358ee 100644
--- a/es6/imgManager.js
+++ b/es6/imgManager.js
@@ -3,43 +3,47 @@
const DocUtils = require("./docUtils");
const extensionRegex = /[^.]+\.([^.]+)/;
+const rels = {
+ getPrefix(fileType) {
+ return fileType === "docx" ? "word" : "ppt";
+ },
+ getFileTypeName(fileType) {
+ return fileType === "docx" ? "document" : "presentation";
+ },
+ getRelsFileName(fileName) {
+ return fileName.replace(/^.*?([a-zA-Z0-9]+)\.xml$/, "$1") + ".xml.rels";
+ },
+ getRelsFilePath(fileName, fileType) {
+ const relsFileName = rels.getRelsFileName(fileName);
+ const prefix = fileType === "pptx" ? "ppt/slides" : "word";
+ return `${prefix}/_rels/${relsFileName}`;
+ },
+};
+
module.exports = class ImgManager {
- constructor(zip, fileName, xmlDocuments) {
- this.fileType = this.getFileType(fileName);
- this.fileTypeName = this.getFileTypeName(fileName);
- this.relsFilePath = this.getRelsFile(fileName);
+ constructor(zip, fileName, xmlDocuments, fileType) {
+ this.fileName = fileName;
+ this.prefix = rels.getPrefix(fileType);
this.zip = zip;
this.xmlDocuments = xmlDocuments;
- this.relsDoc = xmlDocuments[this.relsFilePath] || this.createEmptyRelsDoc(xmlDocuments, this.relsFilePath);
- }
- getRelsFile(fileName) {
- let relsFilePath;
- const relsFileName = this.getRelsFileName(fileName);
- const fileType = this.getFileType(fileName);
- if (fileType === "ppt") {
- relsFilePath = "ppt/slides/_rels/" + relsFileName;
- }
- else {
- relsFilePath = "word/_rels/" + relsFileName;
- }
- return relsFilePath;
- }
- getRelsFileName(fileName) {
- return fileName.replace(/^.*?([a-z0-9]+)\.xml$/, "$1") + ".xml.rels";
- }
- getFileType(fileName) {
- return (fileName.indexOf("ppt/slides") === 0) ? "ppt" : "word";
- }
- getFileTypeName(fileName) {
- return (fileName.indexOf("ppt/slides") === 0) ? "presentation" : "document";
+ this.fileTypeName = rels.getFileTypeName(fileType);
+ this.mediaPrefix = fileType === "pptx" ? "../media" : "media";
+ const relsFilePath = rels.getRelsFilePath(fileName, fileType);
+ this.relsDoc = xmlDocuments[relsFilePath] || this.createEmptyRelsDoc(xmlDocuments, relsFilePath);
}
createEmptyRelsDoc(xmlDocuments, relsFileName) {
- const file = this.zip.files[relsFileName] || this.zip.files[this.fileType + "/_rels/" + this.fileTypeName + ".xml.rels"];
- if (!file) {
- return;
+ const mainRels = this.prefix + "/_rels/" + (this.fileTypeName) + ".xml.rels";
+ const doc = xmlDocuments[mainRels];
+ if (!doc) {
+ const err = new Error("Could not copy from empty relsdoc");
+ err.properties = {
+ mainRels,
+ relsFileName,
+ files: Object.keys(this.zip.files),
+ };
+ throw err;
}
- const content = DocUtils.decodeUtf8(file.asText());
- const relsDoc = DocUtils.str2xml(content);
+ const relsDoc = DocUtils.str2xml(DocUtils.xml2str(doc));
const relationships = relsDoc.getElementsByTagName("Relationships")[0];
const relationshipChilds = relationships.getElementsByTagName("Relationship");
for (let i = 0, l = relationshipChilds.length; i < l; i++) {
@@ -81,11 +85,12 @@ module.exports = class ImgManager {
i = 0;
}
const realImageName = i === 0 ? imageName : imageName + `(${i})`;
- if (this.zip.files[`${this.fileType}/media/${realImageName}`] != null) {
+ const imagePath = `${this.prefix}/media/${realImageName}`;
+ if (this.zip.files[imagePath] != null) {
return this.addImageRels(imageName, imageData, i + 1);
}
const image = {
- name: `${this.fileType}/media/${realImageName}`,
+ name: imagePath,
data: imageData,
options: {
binary: true,
@@ -100,12 +105,7 @@ module.exports = class ImgManager {
const maxRid = this.loadImageRels() + 1;
newTag.setAttribute("Id", `rId${maxRid}`);
newTag.setAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
- if (this.fileType === "ppt") {
- newTag.setAttribute("Target", `../media/${realImageName}`);
- }
- else {
- newTag.setAttribute("Target", `media/${realImageName}`);
- }
+ newTag.setAttribute("Target", `${this.mediaPrefix}/${realImageName}`);
relationships.appendChild(newTag);
return maxRid;
}
diff --git a/es6/index.js b/es6/index.js
index 60ea7c4..d1e0ec6 100644
--- a/es6/index.js
+++ b/es6/index.js
@@ -1,5 +1,6 @@
"use strict";
+const templates = require("./templates");
const DocUtils = require("docxtemplater").DocUtils;
const DOMParser = require("xmldom").DOMParser;
@@ -10,23 +11,25 @@ function isNaN(number) {
const ImgManager = require("./imgManager");
const moduleName = "open-xml-templating/docxtemplater-image-module";
-function getInner({part, left, right, postparsed}) {
+function getInnerDocx({part}) {
+ return part;
+}
+
+function getInnerPptx({part, left, right, postparsed}) {
const xmlString = postparsed.slice(left + 1, right).reduce(function (concat, item) {
return concat + item.value;
}, "");
- part.off = {x: 0, y: 0};
- part.ext = {cx: 0, cy: 0};
const xmlDoc = new DOMParser().parseFromString("" + xmlString + "");
- const off = xmlDoc.getElementsByTagName("a:off");
- if (off.length > 0) {
- part.off.x = off[0].getAttribute("x");
- part.off.y = off[0].getAttribute("y");
- }
+ const offset = xmlDoc.getElementsByTagName("a:off");
const ext = xmlDoc.getElementsByTagName("a:ext");
- if (ext.length > 0) {
- part.ext.cx = ext[0].getAttribute("cx");
- part.ext.cy = ext[0].getAttribute("cy");
- }
+ part.ext = {
+ cx: parseInt(ext[0].getAttribute("cx"), 10),
+ cy: parseInt(ext[0].getAttribute("cy"), 10),
+ };
+ part.offset = {
+ x: parseInt(offset[0].getAttribute("x"), 10),
+ y: parseInt(offset[0].getAttribute("y"), 10),
+ };
return part;
}
@@ -34,10 +37,10 @@ class ImageModule {
constructor(options) {
this.name = "ImageModule";
this.options = options || {};
+ this.imgManagers = {};
if (this.options.centered == null) { this.options.centered = false; }
if (this.options.getImage == null) { throw new Error("You should pass getImage"); }
if (this.options.getSize == null) { throw new Error("You should pass getSize"); }
- this.qrQueue = [];
this.imageNumber = 1;
}
optionsTransformer(options, docxtemplater) {
@@ -45,6 +48,7 @@ class ImageModule {
.concat(docxtemplater.zip.file(/\[Content_Types\].xml/))
.map((file) => file.name);
this.fileTypeConfig = docxtemplater.fileTypeConfig;
+ this.fileType = docxtemplater.fileType;
this.zip = docxtemplater.zip;
options.xmlFileNames = options.xmlFileNames.concat(relsFiles);
return options;
@@ -70,16 +74,20 @@ class ImageModule {
}
postparse(parsed) {
let expandTo;
+ let getInner;
if (this.options.fileType === "pptx") {
expandTo = "p:sp";
+ getInner = getInnerPptx;
}
else {
expandTo = this.options.centered ? "w:p" : "w:t";
+ getInner = getInnerDocx;
}
return DocUtils.traits.expandToOne(parsed, {moduleName, getInner, expandTo});
}
render(part, options) {
- this.imgManager = new ImgManager(this.zip, options.filePath, this.xmlDocuments);
+ this.imgManagers[options.filePath] = this.imgManagers[options.filePath] || new ImgManager(this.zip, options.filePath, this.xmlDocuments, this.fileType);
+ const imgManager = this.imgManagers[options.filePath];
if (!part.type === "placeholder" || part.module !== moduleName) {
return null;
}
@@ -89,7 +97,7 @@ class ImageModule {
throw new Error("tagValue is empty");
}
const imgBuffer = this.options.getImage(tagValue, part.value);
- const rId = this.imgManager.addImageRels(this.getNextImageName(), imgBuffer);
+ const rId = imgManager.addImageRels(this.getNextImageName(), imgBuffer);
const sizePixel = this.options.getSize(imgBuffer, tagValue, part.value);
return this.getRenderedPart(part, rId, sizePixel);
}
@@ -98,220 +106,42 @@ class ImageModule {
}
}
getRenderedPart(part, rId, sizePixel) {
- const size = [this.convertPixelsToEmus(sizePixel[0]), this.convertPixelsToEmus(sizePixel[1])];
+ if (isNaN(rId)) {
+ throw new Error("rId is NaN, aborting");
+ }
+ const size = [DocUtils.convertPixelsToEmus(sizePixel[0]), DocUtils.convertPixelsToEmus(sizePixel[1])];
const centered = (this.options.centered || part.centered);
let newText;
if (this.options.fileType === "pptx") {
- newText = this.getPptRender(part, rId, size, centered);
+ newText = this.getRenderedPartPptx(part, rId, size, centered);
}
else {
- newText = this.getDocxRender(part, rId, size, centered);
+ newText = this.getRenderedPartDocx(rId, size, centered);
}
return {value: newText};
}
- getPptRender(part, rId, size, centered) {
- const offset = {x: parseInt(part.off.x, 10), y: parseInt(part.off.y, 10)};
- const cellCX = parseInt(part.ext.cx, 10) || 1;
- const cellCY = parseInt(part.ext.cy, 10) || 1;
- const imgW = parseInt(size[0], 10) || 1;
- const imgH = parseInt(size[1], 10) || 1;
+ getRenderedPartPptx(part, rId, size, centered) {
+ const offset = {x: part.offset.x, y: part.offset.y};
+ const cellCX = part.ext.cx;
+ const cellCY = part.ext.cy;
+ const imgW = size[0];
+ const imgH = size[1];
if (centered) {
- offset.x = offset.x + (cellCX / 2) - (imgW / 2);
- offset.y = offset.y + (cellCY / 2) - (imgH / 2);
+ offset.x += cellCX / 2 - imgW / 2;
+ offset.y += cellCY / 2 - imgH / 2;
}
- return this.getPptImageXml(rId, [imgW, imgH], offset);
+ return templates.getPptxImageXml(rId, [imgW, imgH], offset);
}
- getDocxRender(part, rId, size, centered) {
- return (centered) ? this.getImageXmlCentered(rId, size) : this.getImageXml(rId, size);
+ getRenderedPartDocx(rId, size, centered) {
+ return centered ? templates.getImageXmlCentered(rId, size) : templates.getImageXml(rId, size);
}
getNextImageName() {
const name = `image_generated_${this.imageNumber}.png`;
this.imageNumber++;
return name;
}
- convertPixelsToEmus(pixel) {
- return Math.round(pixel * 9525);
- }
- getImageXml(rId, size) {
- if (isNaN(rId)) {
- throw new Error("rId is NaN, aborting");
- }
- return `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `.replace(/\t|\n/g, "");
- }
- getImageXmlCentered(rId, size) {
- if (isNaN(rId)) {
- throw new Error("rId is NaN, aborting");
- }
- return `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `.replace(/\t|\n/g, "");
- }
- getPptImageXml(rId, size, off) {
- if (isNaN(rId)) {
- throw new Error("rId is NaN, aborting");
- }
- return `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `.replace(/\t|\n/g, "");
- }
}
module.exports = ImageModule;
diff --git a/es6/templates.js b/es6/templates.js
new file mode 100644
index 0000000..d7b0fc8
--- /dev/null
+++ b/es6/templates.js
@@ -0,0 +1,172 @@
+module.exports = {
+ getImageXml(rId, size) {
+ return `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `.replace(/\t|\n/g, "");
+ },
+ getImageXmlCentered(rId, size) {
+ return `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `.replace(/\t|\n/g, "");
+ },
+ getPptxImageXml(rId, size, offset) {
+ return `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `.replace(/\t|\n/g, "");
+ },
+};
+
diff --git a/es6/test.js b/es6/test.js
index 43e3868..a91a3ac 100644
--- a/es6/test.js
+++ b/es6/test.js
@@ -22,6 +22,8 @@ const fileNames = [
"expectedWithoutRels.docx",
"tagImage.pptx",
"expectedTagImage.pptx",
+ "tagImageCentered.pptx",
+ "expectedTagImageCentered.pptx",
];
beforeEach(function () {
@@ -104,6 +106,13 @@ function testStart() {
this.data = {image: "examples/image.png"};
this.loadAndRender();
});
+
+ it("should work with PPTX documents centered", function () {
+ this.name = "tagImageCentered.pptx";
+ this.expectedName = "expectedTagImageCentered.pptx";
+ this.data = {image: "examples/image.png"};
+ this.loadAndRender();
+ });
});
}
diff --git a/examples/expectedTagImage.pptx b/examples/expectedTagImage.pptx
index 9e0bb99..e6277f1 100644
Binary files a/examples/expectedTagImage.pptx and b/examples/expectedTagImage.pptx differ
diff --git a/examples/expectedTagImageCentered.pptx b/examples/expectedTagImageCentered.pptx
new file mode 100644
index 0000000..c7d5cee
Binary files /dev/null and b/examples/expectedTagImageCentered.pptx differ
diff --git a/examples/tagImageCentered.pptx b/examples/tagImageCentered.pptx
new file mode 100644
index 0000000..eb8ad27
Binary files /dev/null and b/examples/tagImageCentered.pptx differ
diff --git a/package.json b/package.json
index 2eaf1b3..d4b01a2 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"description": "Image Module for docxtemplater",
"main": "js/index.js",
"scripts": {
+ "test:coverage": "istanbul cover _mocha -- es6/test.js",
"compile": "rimraf js && mkdirp js && babel es6 --out-dir js",
"preversion": "npm test && npm run compile && npm run browserify && npm run uglify",
"test:compiled": "mocha js/test.js",
@@ -24,6 +25,7 @@
"jszip": "^2.6.1",
"mkdirp": "^0.5.1",
"mocha": "^3.2.0",
+ "istanbul": "^0.4.5",
"rimraf": "^2.5.4",
"uglifyjs": "^2.4.10"
},