Commit 77664b68 by MaxRcd

Merged upstream master

parents f9614e62 bcf05498
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
"parser": "babel-eslint", "parser": "babel-eslint",
"globals":{ "globals":{
"saveAs": true, "saveAs": true,
"Uint8Array": true,
"JSZipUtils": true "JSZipUtils": true
}, },
"env": { "env": {
......
...@@ -11,7 +11,6 @@ script: ...@@ -11,7 +11,6 @@ script:
node_js: node_js:
- "0.12" - "0.12"
- "0.11"
- "0.10" - "0.10"
- "iojs" - "iojs"
- "4.0" - "4.0"
......
### unreleased (master branch)
- 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.
...@@ -28,7 +28,7 @@ var ImageModule=require('docxtemplater-image-module') ...@@ -28,7 +28,7 @@ var ImageModule=require('docxtemplater-image-module')
var opts = {} var opts = {}
opts.centered = false; opts.centered = false;
opts.getImage=function(tagValue, tagName) { opts.getImage=function(tagValue, tagName) {
return fs.readFileSync(tagValue,'binary'); return fs.readFileSync(tagValue);
} }
opts.getSize=function(img,tagValue, tagName) { opts.getSize=function(img,tagValue, tagName) {
...@@ -131,10 +131,9 @@ Building in the browser ...@@ -131,10 +131,9 @@ Building in the browser
You can build a release for the browser with the following commands You can build a release for the browser with the following commands
``` ```
npm install -g gulp jasmine-node uglify-js browserify
npm install npm install
npm run preversion
npm run compile npm run compile
mkdir build -p
browserify -r ./js/index.js -s ImageModule > build/imagemodule.js
uglifyjs build/imagemodule.js > build/imagemodule.min.js # Optional
``` ```
You will have build/imagemodule.js.
**This module has been deprecated. No new releases will be made to it.**
There is an up to date paid version of the module which you can find on https://docxtemplater.com/modules/image/
If you still wish to use this outdated module which might have unsolved bugs, please have a look at the [OLD README](OLD_README.md)
"use strict"; "use strict";
const DocUtils = require("docxtemplater").DocUtils; const DocUtils = require("docxtemplater").DocUtils;
DocUtils.convertPixelsToEmus = function (pixel) {
return Math.round(pixel * 9525);
};
module.exports = DocUtils; module.exports = DocUtils;
...@@ -3,43 +3,47 @@ ...@@ -3,43 +3,47 @@
const DocUtils = require("./docUtils"); const DocUtils = require("./docUtils");
const extensionRegex = /[^.]+\.([^.]+)/; 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 { module.exports = class ImgManager {
constructor(zip, fileName, xmlDocuments) { constructor(zip, fileName, xmlDocuments, fileType) {
this.fileType = this.getFileType(fileName); this.fileName = fileName;
this.fileTypeName = this.getFileTypeName(fileName); this.prefix = rels.getPrefix(fileType);
this.relsFilePath = this.getRelsFile(fileName);
this.zip = zip; this.zip = zip;
this.xmlDocuments = xmlDocuments; this.xmlDocuments = xmlDocuments;
this.relsDoc = xmlDocuments[this.relsFilePath] || this.createEmptyRelsDoc(xmlDocuments, this.relsFilePath); this.fileTypeName = rels.getFileTypeName(fileType);
} this.mediaPrefix = fileType === "pptx" ? "../media" : "media";
getRelsFile(fileName) { const relsFilePath = rels.getRelsFilePath(fileName, fileType);
let relsFilePath; this.relsDoc = xmlDocuments[relsFilePath] || this.createEmptyRelsDoc(xmlDocuments, 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";
} }
createEmptyRelsDoc(xmlDocuments, relsFileName) { createEmptyRelsDoc(xmlDocuments, relsFileName) {
const file = this.zip.files[relsFileName] || this.zip.files[this.fileType + "/_rels/" + this.fileTypeName + ".xml.rels"]; const mainRels = this.prefix + "/_rels/" + (this.fileTypeName) + ".xml.rels";
if (!file) { const doc = xmlDocuments[mainRels];
return; 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(DocUtils.xml2str(doc));
const relsDoc = DocUtils.str2xml(content);
const relationships = relsDoc.getElementsByTagName("Relationships")[0]; const relationships = relsDoc.getElementsByTagName("Relationships")[0];
const relationshipChilds = relationships.getElementsByTagName("Relationship"); const relationshipChilds = relationships.getElementsByTagName("Relationship");
for (let i = 0, l = relationshipChilds.length; i < l; i++) { for (let i = 0, l = relationshipChilds.length; i < l; i++) {
...@@ -81,11 +85,12 @@ module.exports = class ImgManager { ...@@ -81,11 +85,12 @@ module.exports = class ImgManager {
i = 0; i = 0;
} }
const realImageName = i === 0 ? imageName : imageName + `(${i})`; 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); return this.addImageRels(imageName, imageData, i + 1);
} }
const image = { const image = {
name: `${this.fileType}/media/${realImageName}`, name: imagePath,
data: imageData, data: imageData,
options: { options: {
binary: true, binary: true,
...@@ -100,12 +105,7 @@ module.exports = class ImgManager { ...@@ -100,12 +105,7 @@ module.exports = class ImgManager {
const maxRid = this.loadImageRels() + 1; const maxRid = this.loadImageRels() + 1;
newTag.setAttribute("Id", `rId${maxRid}`); newTag.setAttribute("Id", `rId${maxRid}`);
newTag.setAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"); newTag.setAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");
if (this.fileType === "ppt") { newTag.setAttribute("Target", `${this.mediaPrefix}/${realImageName}`);
newTag.setAttribute("Target", `../media/${realImageName}`);
}
else {
newTag.setAttribute("Target", `media/${realImageName}`);
}
relationships.appendChild(newTag); relationships.appendChild(newTag);
return maxRid; return maxRid;
} }
......
"use strict"; "use strict";
const templates = require("./templates");
const DocUtils = require("docxtemplater").DocUtils; const DocUtils = require("docxtemplater").DocUtils;
const DOMParser = require("xmldom").DOMParser; const DOMParser = require("xmldom").DOMParser;
...@@ -10,33 +11,36 @@ function isNaN(number) { ...@@ -10,33 +11,36 @@ function isNaN(number) {
const ImgManager = require("./imgManager"); const ImgManager = require("./imgManager");
const moduleName = "open-xml-templating/docxtemplater-image-module"; 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) { const xmlString = postparsed.slice(left + 1, right).reduce(function (concat, item) {
return concat + item.value; return concat + item.value;
}, ""); }, "");
part.off = {x: 0, y: 0};
part.ext = {cx: 0, cy: 0};
const xmlDoc = new DOMParser().parseFromString("<xml>" + xmlString + "</xml>"); const xmlDoc = new DOMParser().parseFromString("<xml>" + xmlString + "</xml>");
const off = xmlDoc.getElementsByTagName("a:off"); const offset = xmlDoc.getElementsByTagName("a:off");
if (off.length > 0) {
part.off.x = off[0].getAttribute("x");
part.off.y = off[0].getAttribute("y");
}
const ext = xmlDoc.getElementsByTagName("a:ext"); const ext = xmlDoc.getElementsByTagName("a:ext");
if (ext.length > 0) { part.ext = {
part.ext.cx = ext[0].getAttribute("cx"); cx: parseInt(ext[0].getAttribute("cx"), 10),
part.ext.cy = ext[0].getAttribute("cy"); 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; return part;
} }
class ImageModule { class ImageModule {
constructor(options) { constructor(options) {
this.name = "ImageModule";
this.options = options || {}; this.options = options || {};
this.imgManagers = {};
if (this.options.centered == null) { this.options.centered = false; } if (this.options.centered == null) { this.options.centered = false; }
if (this.options.getImage == null) { throw new Error("You should pass getImage"); } if (this.options.getImage == null) { throw new Error("You should pass getImage"); }
if (this.options.getSize == null) { throw new Error("You should pass getSize"); } if (this.options.getSize == null) { throw new Error("You should pass getSize"); }
this.qrQueue = [];
this.imageNumber = 1; this.imageNumber = 1;
} }
optionsTransformer(options, docxtemplater) { optionsTransformer(options, docxtemplater) {
...@@ -44,6 +48,7 @@ class ImageModule { ...@@ -44,6 +48,7 @@ class ImageModule {
.concat(docxtemplater.zip.file(/\[Content_Types\].xml/)) .concat(docxtemplater.zip.file(/\[Content_Types\].xml/))
.map((file) => file.name); .map((file) => file.name);
this.fileTypeConfig = docxtemplater.fileTypeConfig; this.fileTypeConfig = docxtemplater.fileTypeConfig;
this.fileType = docxtemplater.fileType;
this.zip = docxtemplater.zip; this.zip = docxtemplater.zip;
options.xmlFileNames = options.xmlFileNames.concat(relsFiles); options.xmlFileNames = options.xmlFileNames.concat(relsFiles);
return options; return options;
...@@ -69,248 +74,72 @@ class ImageModule { ...@@ -69,248 +74,72 @@ class ImageModule {
} }
postparse(parsed) { postparse(parsed) {
let expandTo; let expandTo;
if (this.options.fileType === "pptx") { let getInner;
if (this.fileType === "pptx") {
expandTo = "p:sp"; expandTo = "p:sp";
getInner = getInnerPptx;
} }
else { else {
expandTo = this.options.centered ? "w:p" : "w:t"; expandTo = this.options.centered ? "w:p" : "w:t";
getInner = getInnerDocx;
} }
return DocUtils.traits.expandToOne(parsed, {moduleName, getInner, expandTo}); return DocUtils.traits.expandToOne(parsed, {moduleName, getInner, expandTo});
} }
render(part, options) { 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) { if (!part.type === "placeholder" || part.module !== moduleName) {
return null; return null;
} }
try {
const tagValue = options.scopeManager.getValue(part.value); const tagValue = options.scopeManager.getValue(part.value);
if (!tagValue) { if (!tagValue) {
throw new Error("tagValue is empty"); return {value: this.fileTypeConfig.tagTextXml};
} }
const imgBuffer = this.options.getImage(tagValue, part.value); const imgBuffer = this.options.getImage(tagValue, part.value);
const rId = this.imgManager.addImageRels(this.getNextImageName(), imgBuffer); if (!imgBuffer) {
const sizePixel = this.options.getSize(imgBuffer, tagValue, part.value);
return this.getRenderedPart(part, rId, sizePixel);
}
catch (e) {
return {value: this.fileTypeConfig.tagTextXml}; return {value: this.fileTypeConfig.tagTextXml};
} }
const rId = imgManager.addImageRels(this.getNextImageName(), imgBuffer);
const sizePixel = this.options.getSize(imgBuffer, tagValue, part.value);
return this.getRenderedPart(part, rId, sizePixel);
} }
getRenderedPart(part, rId, sizePixel) { 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); const centered = (this.options.centered || part.centered);
let newText; let newText;
if (this.options.fileType === "pptx") { if (this.fileType === "pptx") {
newText = this.getPptRender(part, rId, size, centered); newText = this.getRenderedPartPptx(part, rId, size, centered);
} }
else { else {
newText = this.getDocxRender(part, rId, size, centered); newText = this.getRenderedPartDocx(rId, size, centered);
} }
return {value: newText}; return {value: newText};
} }
getPptRender(part, rId, size, centered) { getRenderedPartPptx(part, rId, size, centered) {
const offset = {x: parseInt(part.off.x, 10), y: parseInt(part.off.y, 10)}; const offset = {x: part.offset.x, y: part.offset.y};
const cellCX = parseInt(part.ext.cx, 10) || 1; const cellCX = part.ext.cx;
const cellCY = parseInt(part.ext.cy, 10) || 1; const cellCY = part.ext.cy;
const imgW = parseInt(size[0], 10) || 1; const imgW = size[0];
const imgH = parseInt(size[1], 10) || 1; const imgH = size[1];
if (centered) { if (centered) {
offset.x = offset.x + (cellCX / 2) - (imgW / 2); offset.x += cellCX / 2 - imgW / 2;
offset.y = offset.y + (cellCY / 2) - (imgH / 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) { getRenderedPartDocx(rId, size, centered) {
return (centered) ? this.getImageXmlCentered(rId, size) : this.getImageXml(rId, size); return centered ? templates.getImageXmlCentered(rId, size) : templates.getImageXml(rId, size);
} }
getNextImageName() { getNextImageName() {
const name = `image_generated_${this.imageNumber}.png`; const name = `image_generated_${this.imageNumber}.png`;
this.imageNumber++; this.imageNumber++;
return name; return name;
} }
convertPixelsToEmus(pixel) {
return Math.round(pixel * 9525);
}
getImageXml(rId, size) {
if (isNaN(rId)) {
throw new Error("rId is NaN, aborting");
}
return `<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="${size[0]}" cy="${size[1]}"/>
<wp:effectExtent l="0" t="0" r="0" b="0"/>
<wp:docPr id="2" name="Image 2" descr="image"/>
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/>
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="Picture 1" descr="image"/>
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId${rId}">
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>
</a:ext>
</a:extLst>
</a:blip>
<a:srcRect/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="${size[0]}" cy="${size[1]}"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln>
<a:noFill/>
</a:ln>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
`.replace(/\t|\n/g, "");
}
getImageXmlCentered(rId, size) {
if (isNaN(rId)) {
throw new Error("rId is NaN, aborting");
}
return `<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr/>
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="${size[0]}" cy="${size[1]}"/>
<wp:docPr id="0" name="Picture" descr=""/>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="Picture" descr=""/>
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId${rId}"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="${size[0]}" cy="${size[1]}"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln w="9525">
<a:noFill/>
<a:miter lim="800000"/>
<a:headEnd/>
<a:tailEnd/>
</a:ln>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
</w:p>
`.replace(/\t|\n/g, "");
}
getPptImageXml(rId, size, off) {
if (isNaN(rId)) {
throw new Error("rId is NaN, aborting");
}
return `<p:pic>
<p:nvPicPr>
<p:cNvPr id="6" name="Picture 2"/>
<p:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
</p:cNvPicPr>
<p:nvPr/>
</p:nvPicPr>
<p:blipFill>
<a:blip r:embed="rId${rId}" cstate="print">
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>
</a:ext>
</a:extLst>
</a:blip>
<a:srcRect/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</p:blipFill>
<p:spPr bwMode="auto">
<a:xfrm>
<a:off x="${off.x}" y="${off.y}"/>
<a:ext cx="${size[0]}" cy="${size[1]}"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln>
<a:noFill/>
</a:ln>
<a:effectLst/>
<a:extLst>
<a:ext uri="{909E8E84-426E-40DD-AFC4-6F175D3DCCD1}">
<a14:hiddenFill xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main">
<a:solidFill>
<a:schemeClr val="accent1"/>
</a:solidFill>
</a14:hiddenFill>
</a:ext>
<a:ext uri="{91240B29-F687-4F45-9708-019B960494DF}">
<a14:hiddenLine xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" w="9525">
<a:solidFill>
<a:schemeClr val="tx1"/>
</a:solidFill>
<a:miter lim="800000"/>
<a:headEnd/>
<a:tailEnd/>
</a14:hiddenLine>
</a:ext>
<a:ext uri="{AF507438-7753-43E0-B8FC-AC1667EBCBE1}">
<a14:hiddenEffects xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main">
<a:effectLst>
<a:outerShdw dist="35921" dir="2700000" algn="ctr" rotWithShape="0">
<a:schemeClr val="bg2"/>
</a:outerShdw>
</a:effectLst>
</a14:hiddenEffects>
</a:ext>
</a:extLst>
</p:spPr>
</p:pic>
`.replace(/\t|\n/g, "");
}
} }
module.exports = ImageModule; module.exports = ImageModule;
module.exports = {
getImageXml(rId, size) {
return `<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="${size[0]}" cy="${size[1]}"/>
<wp:effectExtent l="0" t="0" r="0" b="0"/>
<wp:docPr id="2" name="Image 2" descr="image"/>
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/>
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="Picture 1" descr="image"/>
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId${rId}">
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>
</a:ext>
</a:extLst>
</a:blip>
<a:srcRect/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="${size[0]}" cy="${size[1]}"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln>
<a:noFill/>
</a:ln>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
`.replace(/\t|\n/g, "");
},
getImageXmlCentered(rId, size) {
return `<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:rPr/>
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="${size[0]}" cy="${size[1]}"/>
<wp:docPr id="0" name="Picture" descr=""/>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="Picture" descr=""/>
<pic:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
</pic:cNvPicPr>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId${rId}"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr bwMode="auto">
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="${size[0]}" cy="${size[1]}"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln w="9525">
<a:noFill/>
<a:miter lim="800000"/>
<a:headEnd/>
<a:tailEnd/>
</a:ln>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
</w:p>
`.replace(/\t|\n/g, "");
},
getPptxImageXml(rId, size, offset) {
return `<p:pic>
<p:nvPicPr>
<p:cNvPr id="6" name="Picture 2"/>
<p:cNvPicPr>
<a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
</p:cNvPicPr>
<p:nvPr/>
</p:nvPicPr>
<p:blipFill>
<a:blip r:embed="rId${rId}" cstate="print">
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>
</a:ext>
</a:extLst>
</a:blip>
<a:srcRect/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</p:blipFill>
<p:spPr bwMode="auto">
<a:xfrm>
<a:off x="${offset.x}" y="${offset.y}"/>
<a:ext cx="${size[0]}" cy="${size[1]}"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
<a:noFill/>
<a:ln>
<a:noFill/>
</a:ln>
<a:effectLst/>
<a:extLst>
<a:ext uri="{909E8E84-426E-40DD-AFC4-6F175D3DCCD1}">
<a14:hiddenFill xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main">
<a:solidFill>
<a:schemeClr val="accent1"/>
</a:solidFill>
</a14:hiddenFill>
</a:ext>
<a:ext uri="{91240B29-F687-4F45-9708-019B960494DF}">
<a14:hiddenLine xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" w="9525">
<a:solidFill>
<a:schemeClr val="tx1"/>
</a:solidFill>
<a:miter lim="800000"/>
<a:headEnd/>
<a:tailEnd/>
</a14:hiddenLine>
</a:ext>
<a:ext uri="{AF507438-7753-43E0-B8FC-AC1667EBCBE1}">
<a14:hiddenEffects xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main">
<a:effectLst>
<a:outerShdw dist="35921" dir="2700000" algn="ctr" rotWithShape="0">
<a:schemeClr val="bg2"/>
</a:outerShdw>
</a:effectLst>
</a14:hiddenEffects>
</a:ext>
</a:extLst>
</p:spPr>
</p:pic>
`.replace(/\t|\n/g, "");
},
};
...@@ -8,11 +8,14 @@ const JSZip = require("jszip"); ...@@ -8,11 +8,14 @@ const JSZip = require("jszip");
const ImageModule = require("./index.js"); const ImageModule = require("./index.js");
const testutils = require("docxtemplater/js/tests/utils"); const testutils = require("docxtemplater/js/tests/utils");
const shouldBeSame = testutils.shouldBeSame; const shouldBeSame = testutils.shouldBeSame;
const sizeOf = require("image-size");
const fileNames = [ const fileNames = [
"imageExample.docx", "imageExample.docx",
"imageHeaderFooterExample.docx", "imageHeaderFooterExample.docx",
"imageLoopExample.docx", "imageLoopExample.docx",
"imageInlineExample.docx",
"expectedInline.docx",
"expectedNoImage.docx", "expectedNoImage.docx",
"expectedHeaderFooter.docx", "expectedHeaderFooter.docx",
"expectedOneImage.docx", "expectedOneImage.docx",
...@@ -20,14 +23,18 @@ const fileNames = [ ...@@ -20,14 +23,18 @@ const fileNames = [
"expectedLoopCentered.docx", "expectedLoopCentered.docx",
"withoutRels.docx", "withoutRels.docx",
"expectedWithoutRels.docx", "expectedWithoutRels.docx",
"expectedBase64.docx",
"tagImage.pptx", "tagImage.pptx",
"expectedTagImage.pptx", "expectedTagImage.pptx",
"tagImageCentered.pptx",
"expectedTagImageCentered.pptx",
"expectedInlineResize.docx",
]; ];
beforeEach(function () { beforeEach(function () {
this.opts = { this.opts = {
getImage: function (tagValue) { getImage: function (tagValue) {
return fs.readFileSync(tagValue, "binary"); return fs.readFileSync(tagValue);
}, },
getSize: function () { getSize: function () {
return [150, 150]; return [150, 150];
...@@ -36,11 +43,8 @@ beforeEach(function () { ...@@ -36,11 +43,8 @@ beforeEach(function () {
}; };
this.loadAndRender = function () { this.loadAndRender = function () {
const fileType = (testutils.pptX[this.name]) ? "pptx" : "docx"; const file = testutils.createDoc(this.name);
const file = (fileType === "pptx") ? testutils.pptX[this.name] : testutils.docX[this.name];
this.doc = new Docxtemplater(); this.doc = new Docxtemplater();
this.doc.setOptions({fileType});
this.opts.fileType = fileType;
const inputZip = new JSZip(file.loadedContent); const inputZip = new JSZip(file.loadedContent);
this.doc.loadZip(inputZip).setData(this.data); this.doc.loadZip(inputZip).setData(this.data);
const imageModule = new ImageModule(this.opts); const imageModule = new ImageModule(this.opts);
...@@ -57,7 +61,6 @@ function testStart() { ...@@ -57,7 +61,6 @@ function testStart() {
this.name = "imageExample.docx"; this.name = "imageExample.docx";
this.expectedName = "expectedOneImage.docx"; this.expectedName = "expectedOneImage.docx";
this.data = {image: "examples/image.png"}; this.data = {image: "examples/image.png"};
this.fileType = "docx";
this.loadAndRender(); this.loadAndRender();
}); });
...@@ -75,6 +78,13 @@ function testStart() { ...@@ -75,6 +78,13 @@ function testStart() {
this.loadAndRender(); this.loadAndRender();
}); });
it("should work with inline", function () {
this.name = "imageInlineExample.docx";
this.expectedName = "expectedInline.docx";
this.data = {firefox: "examples/image.png"};
this.loadAndRender();
});
it("should work with centering", function () { it("should work with centering", function () {
this.name = "imageExample.docx"; this.name = "imageExample.docx";
this.expectedName = "expectedCentered.docx"; this.expectedName = "expectedCentered.docx";
...@@ -104,17 +114,58 @@ function testStart() { ...@@ -104,17 +114,58 @@ function testStart() {
this.data = {image: "examples/image.png"}; this.data = {image: "examples/image.png"};
this.loadAndRender(); 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();
});
it("should work with auto resize", function () {
this.name = "imageInlineExample.docx";
this.expectedName = "expectedInlineResize.docx";
this.opts.getSize = function (img) {
const sizeObj = sizeOf(img);
return [sizeObj.width, sizeObj.height];
};
this.data = {firefox: "examples/image.png"};
this.loadAndRender();
});
it("should work with base64 data", function () {
const base64Image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QIJBywfp3IOswAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAkUlEQVQY052PMQqDQBREZ1f/d1kUm3SxkeAF/FdIjpOcw2vpKcRWCwsRPMFPsaIQSIoMr5pXDGNUFd9j8TOn7kRW71fvO5HTq6qqtnWtzh20IqE3YXtL0zyKwAROQLQ5l/c9gHjfKK6wMZjADE6s49Dver4/smEAc2CuqgwAYI5jU9NcxhHEy60sni986H9+vwG1yDHfK1jitgAAAABJRU5ErkJggg==";
this.name = "imageExample.docx";
function base64DataURLToArrayBuffer(dataURL) {
const stringBase64 = dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
let binaryString;
if (typeof window !== "undefined") {
binaryString = window.atob(stringBase64);
}
else {
binaryString = new Buffer(stringBase64, "base64").toString("binary");
}
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;
}
this.opts.getImage = function (image) {
return image;
};
this.expectedName = "expectedBase64.docx";
this.data = {image: base64DataURLToArrayBuffer(base64Image)};
this.loadAndRender();
});
}); });
} }
testutils.setExamplesDirectory(path.resolve(__dirname, "..", "examples")); testutils.setExamplesDirectory(path.resolve(__dirname, "..", "examples"));
testutils.setStartFunction(testStart); testutils.setStartFunction(testStart);
fileNames.forEach(function (filename) { fileNames.forEach(function (filename) {
if (filename.indexOf(".pptx") === filename.length - 5) { testutils.loadFile(filename, testutils.loadDocument);
testutils.loadFile(filename, testutils.loadPptx);
}
else {
testutils.loadFile(filename, testutils.loadDocx);
}
}); });
testutils.start(); testutils.start();
{ {
"name": "docxtemplater-image-module", "name": "docxtemplater-image-module",
"version": "3.0.0", "version": "3.1.0",
"description": "Image Module for docxtemplater", "description": "Image Module for docxtemplater",
"main": "js/index.js", "main": "js/index.js",
"scripts": { "scripts": {
"test:coverage": "istanbul cover _mocha -- es6/test.js",
"compile": "rimraf js && mkdirp js && babel es6 --out-dir js", "compile": "rimraf js && mkdirp js && babel es6 --out-dir js",
"preversion": "npm test", "preversion": "npm test && npm run browserify && npm run uglify",
"test:compiled": "mocha js/test.js", "test:compiled": "mocha js/test.js",
"test:es6": "mocha es6/test.js", "test:es6": "mocha es6/test.js",
"lint": "eslint .", "lint": "eslint .",
"test": "npm run compile && npm run test:compiled", "test": "npm run compile && npm run test:compiled",
"browserify": "browserify --insert-global-vars __filename,__dirname -r ./js/index.js -s ImageModule > build/imagemodule.js", "browserify": "mkdir build -p && browserify --insert-global-vars __filename,__dirname -r ./js/index.js -s ImageModule > build/imagemodule.js",
"uglify": "uglifyjs build/imagemodule.js > build/imagemodule.min.js" "uglify": "uglifyjs build/imagemodule.js > build/imagemodule.min.js"
}, },
"devDependencies": { "devDependencies": {
"babel-cli": "^6.11.4", "babel-cli": "^6.11.4",
"babel-eslint": "^6.0.2", "babel-eslint": "^7.1.1",
"babel-preset-es2015": "^6.3.13", "babel-preset-es2015": "^6.3.13",
"browserify": "^13.1.0", "browserify": "^14.0.0",
"chai": "^3.4.1", "chai": "^3.4.1",
"docxtemplater": "^3.0.0", "docxtemplater": "^3.0.0",
"eslint": "^2.7.0", "eslint": "^3.15.0",
"jszip": "^2.6.0", "image-size": "^0.5.1",
"mocha": "^2.4.5", "istanbul": "^0.4.5",
"jszip": "^2.6.1",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^3.2.0",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"uglifyjs": "^2.4.10" "uglifyjs": "^2.4.10"
}, },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment