├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── coffee ├── chartMaker.coffee ├── chartManager.coffee ├── docUtils.coffee ├── index.coffee └── test.coffee ├── examples ├── chartExample.docx ├── dateExample.docx ├── loopChartExample.docx └── multipleChartsExample.docx ├── gulpfile.js ├── package.json └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /*.docx 2 | test/ 3 | js/ 4 | node_modules 5 | .idea/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /*.docx 2 | test/ 3 | examples/ 4 | coffee/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.12" 5 | - "0.11" 6 | - "0.10" 7 | - "iojs" 8 | 9 | before_script: 10 | - "npm install -g npm" 11 | - "npm install -g gulp" 12 | - "npm install -g mocha" 13 | - "gulp allCoffee" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Danila Dergachev (dderg) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /coffee/chartMaker.coffee: -------------------------------------------------------------------------------- 1 | DocUtils = require('./docUtils') 2 | module.exports = class ChartMaker 3 | getTemplateTop: () -> 4 | return """ 5 | 6 | 7 | 8 | 9 | #{if @options.title then "" else ""} 10 | 11 | 12 | 13 | 14 | """ 15 | 16 | getFormatCode: () -> 17 | if @options.axis.x.type == 'date' 18 | return "m/d/yyyy" 19 | else 20 | return "" 21 | getLineTemplate: (line, i) -> 22 | result = """ 23 | 24 | 25 | 26 | 27 | #{line.name} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | #{@getFormatCode()} 37 | 38 | 39 | """ 40 | for elem, i in line.data 41 | result += """ 42 | 43 | #{elem.x} 44 | 45 | """ 46 | result += """ 47 | 48 | 49 | 50 | 51 | 52 | 53 | General 54 | 55 | """ 56 | for elem, i in line.data 57 | result += """ 58 | 59 | #{elem.y} 60 | 61 | """ 62 | result += """ 63 | 64 | 65 | 66 | 67 | """ 68 | return result 69 | id1: 142309248, 70 | id2: 142310784 71 | getScaling: (opts) -> 72 | """ 73 | 74 | 75 | #{if opts.max != undefined then "" else ""} 76 | #{if opts.min != undefined then "" else ""} 77 | 78 | """ 79 | getAxOpts: () -> 80 | return """ 81 | 82 | #{@getScaling(@options.axis.x)} 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | """ 100 | getCatAx: () -> 101 | return """ 102 | 103 | #{@getAxOpts()} 104 | 105 | 106 | """ 107 | getDateAx: () -> 108 | return """ 109 | 110 | #{@getAxOpts()} 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | """ 120 | getBorder: () -> 121 | unless @options.border 122 | return """ 123 | 124 | 125 | 126 | 127 | 128 | """ 129 | else 130 | return '' 131 | getTemplateBottom: () -> 132 | result = """ 133 | 134 | 135 | 136 | 137 | """ 138 | switch @options.axis.x.type 139 | when 'date' 140 | result += @getDateAx() 141 | else 142 | result += @getCatAx() 143 | result += """ 144 | 145 | 146 | #{@getScaling(@options.axis.y)} 147 | 148 | #{if @options.grid then "" else ""} 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | #{@getBorder()} 173 | 174 | """ 175 | return result 176 | constructor: (@zip, @options) -> 177 | if (@options.axis.x.type == 'date') 178 | @ref = "numRef" 179 | @cache = "numCache" 180 | else 181 | @ref = "strRef" 182 | @cache = "strCache" 183 | 184 | 185 | makeChartFile: (lines) -> 186 | result = @getTemplateTop() 187 | for line, i in lines 188 | result += @getLineTemplate(line, i) 189 | result += @getTemplateBottom() 190 | @chartContent = result 191 | return @chartContent 192 | 193 | writeFile: (path) -> 194 | @zip.file("word/charts/#{path}.xml", @chartContent, {}) 195 | return -------------------------------------------------------------------------------- /coffee/chartManager.coffee: -------------------------------------------------------------------------------- 1 | DocUtils = require('./docUtils') 2 | 3 | module.exports = class ChartManager 4 | constructor: (@zip, @fileName) -> 5 | @endFileName = @fileName.replace(/^.*?([a-z0-9]+)\.xml$/, "$1") 6 | @relsLoaded = false 7 | ###* 8 | * load relationships 9 | * @return {ChartManager} for chaining 10 | ### 11 | loadChartRels: () -> 12 | # console.log('loadChartRels') 13 | ###* 14 | * load file, save path 15 | * @param {String} filePath path to current file 16 | * @return {Object} file 17 | ### 18 | loadFile = (filePath) => 19 | @filePath = filePath 20 | # console.log('loading file: ' + @filePath) 21 | return @zip.files[@filePath] 22 | 23 | file = loadFile("word/_rels/#{@endFileName}.xml.rels") || loadFile("word/_rels/document.xml.rels") #duct tape hack, doesn't work otherwise 24 | return if file == undefined 25 | content = DocUtils.decodeUtf8(file.asText()) 26 | @xmlDoc = DocUtils.Str2xml(content) 27 | RidArray = ((parseInt tag.getAttribute("Id").substr(3)) for tag in @xmlDoc.getElementsByTagName('Relationship')) #Get all Rids 28 | @maxRid = DocUtils.maxArray(RidArray) 29 | # console.log @xmlDoc 30 | @chartRels = [] 31 | @relsLoaded = true 32 | return this 33 | 34 | 35 | addChartRels: (chartName) -> 36 | # console.log('addChartRels') 37 | # console.log('name: ' + chartName) 38 | return unless @relsLoaded 39 | @maxRid++ 40 | @_addChartRelationship(@maxRid, chartName); 41 | @_addChartContentType(chartName); 42 | 43 | @zip.file(@filePath, DocUtils.encodeUtf8(DocUtils.xml2Str(@xmlDoc)), {}) 44 | return @maxRid 45 | 46 | ###* 47 | * add relationship tag to relationships 48 | * @param {Number} id relationship ID 49 | * @param {String} name target file name 50 | ### 51 | _addChartRelationship: (id, name) -> 52 | relationships = @xmlDoc.getElementsByTagName("Relationships")[0] 53 | newTag = @xmlDoc.createElement('Relationship') 54 | newTag.namespaceURI = null 55 | newTag.setAttribute('Id', "rId#{id}") 56 | newTag.setAttribute('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart') 57 | newTag.setAttribute('Target', "charts/#{name}.xml") 58 | relationships.appendChild(newTag) 59 | 60 | ###* 61 | * add override to [Content_Types].xml 62 | * @param {String} name filename 63 | ### 64 | _addChartContentType: (name) -> 65 | path = '[Content_Types].xml' 66 | file = @zip.files[path] 67 | content = DocUtils.decodeUtf8(file.asText()) 68 | xmlDoc = DocUtils.Str2xml(content) 69 | types = xmlDoc.getElementsByTagName("Types")[0] 70 | newTag = xmlDoc.createElement('Override') 71 | newTag.namespaceURI = 'http://schemas.openxmlformats.org/package/2006/content-types' 72 | newTag.setAttribute('ContentType', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml') 73 | newTag.setAttribute('PartName', "/word/charts/#{name}.xml") 74 | types.appendChild(newTag) 75 | @zip.file(path, DocUtils.encodeUtf8(DocUtils.xml2Str(xmlDoc)), {}) 76 | -------------------------------------------------------------------------------- /coffee/docUtils.coffee: -------------------------------------------------------------------------------- 1 | DOMParser = require('xmldom').DOMParser 2 | XMLSerializer= require('xmldom').XMLSerializer 3 | 4 | DocUtils = {} 5 | 6 | DocUtils.xml2Str = (xmlNode) -> 7 | a = new XMLSerializer() 8 | a.serializeToString(xmlNode) 9 | 10 | DocUtils.Str2xml= (str,errorHandler) -> 11 | parser = new DOMParser({errorHandler}) 12 | xmlDoc=parser.parseFromString(str,"text/xml") 13 | 14 | DocUtils.maxArray = (a) -> Math.max.apply(null, a) 15 | 16 | DocUtils.decodeUtf8 = (s) -> 17 | try 18 | if s==undefined then return undefined 19 | return decodeURIComponent(escape(DocUtils.convertSpaces(s))) #replace Ascii 160 space by the normal space, Ascii 32 20 | catch e 21 | console.error s 22 | console.error 'could not decode' 23 | throw new Error('end') 24 | 25 | DocUtils.encodeUtf8 = (s)-> 26 | unescape(encodeURIComponent(s)) 27 | 28 | DocUtils.convertSpaces = (s) -> 29 | s.replace(new RegExp(String.fromCharCode(160),"g")," ") 30 | 31 | DocUtils.pregMatchAll = (regex, content) -> 32 | ###regex is a string, content is the content. It returns an array of all matches with their offset, for example: 33 | regex=la 34 | content=lolalolilala 35 | returns: [{0:'la',offset:2},{0:'la',offset:8},{0:'la',offset:10}] 36 | ### 37 | regex= (new RegExp(regex,'g')) unless (typeof regex=='object') 38 | matchArray= [] 39 | replacer = (match,pn ..., offset, string)-> 40 | pn.unshift match #add match so that pn[0] = whole match, pn[1]= first parenthesis,... 41 | pn.offset= offset 42 | matchArray.push pn 43 | content.replace regex,replacer 44 | matchArray 45 | 46 | module.exports=DocUtils 47 | -------------------------------------------------------------------------------- /coffee/index.coffee: -------------------------------------------------------------------------------- 1 | SubContent = require('docxtemplater').SubContent 2 | ChartManager = require('./chartManager') 3 | ChartMaker = require('./chartMaker') 4 | 5 | fs = require('fs') 6 | 7 | class ChartModule 8 | ###* 9 | * self name for self-identification, variable for fast changing; 10 | * @type {String} 11 | ### 12 | name: 'chart' 13 | 14 | ###* 15 | * initialize options with empty object if not recived 16 | * @manager = ModuleManager instance 17 | * @param {Object} @options params for the module 18 | ### 19 | constructor: (@options = {}) -> 20 | 21 | handleEvent: (event, eventData) -> 22 | if (event == 'rendering-file') 23 | @renderingFileName = eventData; 24 | # console.log(renderingFileName) 25 | gen = @manager.getInstance('gen'); 26 | @chartManager = new ChartManager(gen.zip, @renderingFileName) 27 | @chartManager.loadChartRels(); 28 | 29 | else if (event == 'rendered') 30 | @finished() 31 | 32 | get: (data) -> 33 | # console.log('get data: ' + data); 34 | if data == 'loopType' 35 | templaterState = @manager.getInstance('templaterState') 36 | # console.log(templaterState.textInsideTag) 37 | if templaterState.textInsideTag[0] == '$' 38 | return @name 39 | return null 40 | 41 | handle: (type, data) -> 42 | if (type == 'replaceTag' and data == @name) 43 | # console.log('handle') 44 | @replaceTag() 45 | return null 46 | 47 | finished: () -> 48 | 49 | on: (event, data) -> 50 | if event == 'error' 51 | throw data 52 | 53 | replaceBy: (text, outsideElement) -> 54 | xmlTemplater = @manager.getInstance('xmlTemplater') 55 | templaterState = @manager.getInstance('templaterState') 56 | subContent = new SubContent(xmlTemplater.content) 57 | .getInnerTag(templaterState) 58 | .getOuterXml(outsideElement) 59 | xmlTemplater.replaceXml(subContent,text) 60 | 61 | convertPixelsToEmus: (pixel) -> 62 | Math.round(pixel * 9525) 63 | 64 | extendDefaults: (options) -> 65 | deepMerge = (target, source) -> 66 | for key of source 67 | original = target[key] 68 | next = source[key] 69 | if original and next and typeof next == 'object' 70 | deepMerge original, next 71 | else 72 | target[key] = next 73 | return target 74 | defaultOptions = { 75 | width: 5486400 / 9525, 76 | height: 3200400 / 9525, 77 | grid: true, 78 | border: true, 79 | title: false, # works only for single-line charts 80 | legend: { 81 | position: 'r', # 'l', 'r', 'b', 't' 82 | }, 83 | axis: { 84 | x: { 85 | orientation: 'minMax', # 'maxMin' 86 | min: undefined, # number 87 | max: undefined, 88 | type: undefined, # 'date' 89 | date: { 90 | format: 'unix', 91 | code: 'mm/yy', # "m/yy;@" 92 | unit: 'months', # "days" 93 | step: '1' 94 | } 95 | }, 96 | y: { 97 | orientation: 'minMax', 98 | mix: undefined, 99 | max: undefined 100 | } 101 | } 102 | } 103 | result = deepMerge({}, defaultOptions); 104 | result = deepMerge(result, options); 105 | return result; 106 | 107 | 108 | convertUnixTo1900: (chartData, axName) -> 109 | unixTo1900 = (value) -> 110 | return Math.round(value / 86400 + 25569) 111 | convertOption = (name) -> 112 | if (chartData.options.axis[axName][name]) 113 | chartData.options.axis[axName][name] = unixTo1900(chartData.options.axis[axName][name]) 114 | convertOption('min'); 115 | convertOption('max'); 116 | for line in chartData.lines 117 | for data in line.data 118 | data[axName] = unixTo1900(data[axName]) 119 | return chartData 120 | 121 | replaceTag: () -> 122 | scopeManager = @manager.getInstance('scopeManager') 123 | templaterState = @manager.getInstance('templaterState') 124 | gen = @manager.getInstance('gen'); 125 | 126 | tag = templaterState.textInsideTag.substr(1) # tag to be replaced 127 | chartData = scopeManager.getValueFromScope(tag) # data to build chart from 128 | 129 | # exit gracefully if no chartData, required when outer loop data doesn't exist 130 | return if (!chartData?) 131 | 132 | # create a unique filename so we can have multiple charts from one tag, via the loop functionality in docxtemplater 133 | # Note the +1 isn't really required, it just makes the number the same as the associated rel, handy for debugging the resulting docx 134 | filename = tag + (this.chartManager.maxRid + 1); 135 | 136 | 137 | imageRels = @chartManager.loadChartRels() 138 | 139 | return unless imageRels # break if no Relationships loaded 140 | chartId = @chartManager.addChartRels(filename) 141 | 142 | options = @extendDefaults(chartData.options) 143 | 144 | for name of options.axis 145 | ax = options.axis[name] 146 | if ax.type == 'date' and ax[ax.type].format == 'unix' 147 | chartData = @convertUnixTo1900(chartData, name) 148 | 149 | chart = new ChartMaker(gen.zip, options) 150 | chart.makeChartFile(chartData.lines) 151 | chart.writeFile(filename) 152 | 153 | 154 | tagXml = @manager.getInstance('xmlTemplater').fileTypeConfig.tagsXmlArray[0] 155 | 156 | newText = @getChartXml({ 157 | chartID: chartId, 158 | width: @convertPixelsToEmus(options.width), 159 | height: @convertPixelsToEmus(options.height) 160 | }) 161 | @replaceBy(newText, tagXml) 162 | 163 | getChartXml: ({chartID, width, height}) -> 164 | return """ 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | """ 179 | 180 | 181 | 182 | module.exports = ChartModule 183 | -------------------------------------------------------------------------------- /coffee/test.coffee: -------------------------------------------------------------------------------- 1 | fs = require('fs') 2 | DocxGen = require('docxtemplater') 3 | expect = require('chai').expect 4 | 5 | fileNames = [ 6 | 'chartExample.docx', 7 | 'loopChartExample.docx', 8 | 'dateExample.docx', 9 | 'multipleChartsExample.docx' 10 | ] 11 | 12 | ChartModule = require('../js/index.js') 13 | 14 | docX = {} 15 | 16 | loadFile = (name) -> 17 | if fs.readFileSync? then return fs.readFileSync(__dirname + "/../examples/" + name, "binary") 18 | xhrDoc = new XMLHttpRequest() 19 | xhrDoc.open('GET', "../examples/" + name, false) 20 | if (xhrDoc.overrideMimeType) 21 | xhrDoc.overrideMimeType('text/plain; charset=x-user-defined') 22 | xhrDoc.send() 23 | xhrDoc.response 24 | 25 | for name in fileNames 26 | content=loadFile(name) 27 | docX[name]=new DocxGen() 28 | docX[name].loadedContent=content 29 | 30 | describe 'adding with {$ chart} syntax', () -> 31 | name = 'chartExample.docx' 32 | chartModule = new ChartModule() 33 | docX[name].attachModule(chartModule) 34 | out = docX[name] 35 | .load(docX[name].loadedContent) 36 | .setData({ 37 | chart: { 38 | options: { 39 | width: 300, 40 | height: 200, 41 | border: false, 42 | legend: { 43 | position: 'l' # can be 'r' 44 | } 45 | }, 46 | lines: [ 47 | { 48 | name: 'line 1', 49 | data: [ 50 | { 51 | x: 'day 1', 52 | y: '4.3' 53 | }, 54 | { 55 | x: 'day 2', 56 | y: '2.5' 57 | }, 58 | { 59 | x: 'day 3', 60 | y: '3.5' 61 | }, 62 | { 63 | x: 'day 4', 64 | y: '4.5' 65 | } 66 | ] 67 | }, 68 | { 69 | name: 'line 2', 70 | data: [ 71 | { 72 | x: 'day 1', 73 | y: '2.4' 74 | }, 75 | { 76 | x: 'day 2', 77 | y: '4.4000000000000004' 78 | }, 79 | { 80 | x: 'day 3', 81 | y: '1.8' 82 | }, 83 | { 84 | x: 'day 4', 85 | y: '2.8' 86 | } 87 | ] 88 | }, 89 | { 90 | name: 'line 3', 91 | data: [ 92 | { 93 | x: 'day 1', 94 | y: '2' 95 | }, 96 | { 97 | x: 'day 2', 98 | y: '2' 99 | }, 100 | { 101 | x: 'day 3', 102 | y: '3' 103 | }, 104 | { 105 | x: 'day 4', 106 | y: '5' 107 | } 108 | ] 109 | } 110 | ] 111 | } 112 | }) 113 | .render() 114 | zip = out.getZip() 115 | 116 | it 'should create relationship in rels file', () -> 117 | relsFile = zip.files['word/_rels/document.xml.rels'] 118 | expect(relsFile?).to.equal(true) 119 | relsFileContent = relsFile.asText() 120 | expect(relsFileContent).to.equal(""" 121 | 122 | """) 123 | 124 | it 'should create chart file without min and max in scaling', () -> 125 | chartFile = zip.files['word/charts/chart6.xml'] 126 | expect(chartFile?).to.equal(true) 127 | chartFileContent = chartFile.asText() 128 | expect(chartFileContent).to.contain("c:orientation") 129 | expect(chartFileContent).to.not.contain("c:max") 130 | 131 | 132 | it 'should add content type', () -> 133 | typeFile = zip.files['[Content_Types].xml'] 134 | expect(typeFile?).to.equal(true) 135 | typeFileContent = typeFile.asText() 136 | expect(typeFileContent).to.equal(""" 137 | 138 | """) 139 | 140 | 141 | 142 | 143 | 144 | fs.writeFile('test.docx', zip.generate({type:"nodebuffer"})); 145 | 146 | describe 'adding with {$ chart} syntax inside a loop', () -> 147 | name = 'loopChartExample.docx' 148 | chartModule = new ChartModule() 149 | docX[name].attachModule(chartModule) 150 | out = docX[name] 151 | .load(docX[name].loadedContent) 152 | .setData({ 153 | subsidiaries: [ 154 | { 155 | title: "Euro Giant Ltd", 156 | chart: { 157 | options: { 158 | width: 300, 159 | height: 200, 160 | border: false, 161 | legend: { 162 | position: 'l' # can be 'r' 163 | } 164 | }, 165 | lines: [ 166 | { 167 | name: 'Product A', 168 | data: [ 169 | { 170 | x: 'Jan', 171 | y: '14.5' 172 | }, 173 | { 174 | x: 'Feb', 175 | y: '12.5' 176 | }, 177 | { 178 | x: 'Mar', 179 | y: '13.5' 180 | } 181 | ] 182 | }, 183 | { 184 | name: 'Product B', 185 | data: [ 186 | { 187 | x: 'Jan', 188 | y: '32.4' 189 | }, 190 | { 191 | x: 'Feb', 192 | y: '34.4000000000000004' 193 | }, 194 | { 195 | x: 'Mar', 196 | y: '31.8' 197 | } 198 | ] 199 | }, 200 | { 201 | name: 'Product C', 202 | data: [ 203 | { 204 | x: 'Jan', 205 | y: '32' 206 | }, 207 | { 208 | x: 'Feb', 209 | y: '22' 210 | }, 211 | { 212 | x: 'Mar', 213 | y: '35' 214 | } 215 | ] 216 | } 217 | ] 218 | } 219 | }, 220 | { 221 | title: "USA Giant Inc", 222 | chart: { 223 | options: { 224 | width: 300, 225 | height: 200, 226 | border: false, 227 | legend: { 228 | position: 'l' # can be 'r' 229 | } 230 | }, 231 | lines: [ 232 | { 233 | name: 'Product A', 234 | data: [ 235 | { 236 | x: 'Jan', 237 | y: '15' 238 | }, 239 | { 240 | x: 'Feb', 241 | y: '25' 242 | }, 243 | { 244 | x: 'Mar', 245 | y: '12' 246 | } 247 | ] 248 | }, 249 | { 250 | name: 'Product B', 251 | data: [ 252 | { 253 | x: 'Jan', 254 | y: '7' 255 | }, 256 | { 257 | x: 'Feb', 258 | y: '8.5' 259 | }, 260 | { 261 | x: 'Mar', 262 | y: '27' 263 | } 264 | ] 265 | }, 266 | { 267 | name: 'Product C', 268 | data: [ 269 | { 270 | x: 'Jan', 271 | y: '20' 272 | }, 273 | { 274 | x: 'Feb', 275 | y: '18' 276 | }, 277 | { 278 | x: 'Mar', 279 | y: '22' 280 | } 281 | ] 282 | } 283 | ] 284 | } 285 | } 286 | ] 287 | 288 | }) 289 | .render() 290 | zip = out.getZip() 291 | 292 | it 'should create two relationships in rels file', () -> 293 | relsFile = zip.files['word/_rels/document.xml.rels'] 294 | expect(relsFile?).to.equal(true) 295 | relsFileContent = relsFile.asText() 296 | expect(relsFileContent).to.equal(""" 297 | 298 | """) 299 | 300 | it 'should create two separate chart files', () -> 301 | chartFile1 = zip.files['word/charts/chart7.xml'] 302 | expect(chartFile1?).to.equal(true) 303 | chartFile2 = zip.files['word/charts/chart8.xml'] 304 | expect(chartFile2?).to.equal(true) 305 | 306 | it 'should add content type', () -> 307 | typeFile = zip.files['[Content_Types].xml'] 308 | expect(typeFile?).to.equal(true) 309 | typeFileContent = typeFile.asText() 310 | expect(typeFileContent).to.equal(""" 311 | 312 | """) 313 | 314 | 315 | 316 | 317 | 318 | fs.writeFile('looptest.docx', zip.generate({type:"nodebuffer"})); 319 | 320 | describe 'multiple charts adding', () -> 321 | name = 'multipleChartsExample.docx' 322 | chartModule = new ChartModule() 323 | docX[name].attachModule(chartModule) 324 | out = docX[name] 325 | .load(docX[name].loadedContent) 326 | .setData({ 327 | chart: { 328 | lines: [ 329 | { 330 | name: 'line 1', 331 | data: [ 332 | { 333 | x: 'day 1', 334 | y: '4.3' 335 | }, 336 | { 337 | x: 'day 2', 338 | y: '2.5' 339 | }, 340 | { 341 | x: 'day 3', 342 | y: '3.5' 343 | }, 344 | { 345 | x: 'day 4', 346 | y: '4.5' 347 | } 348 | ] 349 | }, 350 | { 351 | name: 'line 2', 352 | data: [ 353 | { 354 | x: 'day 1', 355 | y: '2.4' 356 | }, 357 | { 358 | x: 'day 2', 359 | y: '4' 360 | }, 361 | { 362 | x: 'day 3', 363 | y: '1.8' 364 | }, 365 | { 366 | x: 'day 4', 367 | y: '2.8' 368 | } 369 | ] 370 | }, 371 | { 372 | name: 'line 3', 373 | data: [ 374 | { 375 | x: 'day 1', 376 | y: '2' 377 | }, 378 | { 379 | x: 'day 2', 380 | y: '2' 381 | }, 382 | { 383 | x: 'day 3', 384 | y: '3' 385 | }, 386 | { 387 | x: 'day 4', 388 | y: '5' 389 | } 390 | ] 391 | } 392 | ] 393 | }, 394 | chart2: { 395 | options: { 396 | legend: { 397 | position: 'b' 398 | } 399 | axis: { 400 | y: { 401 | min: 500, 402 | max: 5000 403 | } 404 | } 405 | } 406 | lines: [ 407 | { 408 | name: 'line 1', 409 | data: [ 410 | { 411 | x: 'day 1', 412 | y: '1000' 413 | }, 414 | { 415 | x: 'day 2', 416 | y: '1000' 417 | }, 418 | { 419 | x: 'day 3', 420 | y: '3000' 421 | }, 422 | { 423 | x: 'day 4', 424 | y: '4500' 425 | }, 426 | { 427 | x: 'day 5', 428 | y: '1000' 429 | }, 430 | { 431 | x: 'day 6', 432 | y: '3000' 433 | }, 434 | { 435 | x: 'day 7', 436 | y: '1000' 437 | }, 438 | { 439 | x: 'day 8', 440 | y: '3000' 441 | }, 442 | { 443 | x: 'day 9', 444 | y: '1000' 445 | }, 446 | { 447 | x: 'day 10', 448 | y: '3000' 449 | }, 450 | { 451 | x: 'day 11', 452 | y: '1000' 453 | }, 454 | { 455 | x: 'day 12', 456 | y: '3000' 457 | }, 458 | { 459 | x: 'day 13', 460 | y: '1000' 461 | }, 462 | { 463 | x: 'day 14', 464 | y: '3000' 465 | }, 466 | { 467 | x: 'day 15', 468 | y: '1000' 469 | }, 470 | { 471 | x: 'day 16', 472 | y: '3000' 473 | }, 474 | { 475 | x: 'day 17', 476 | y: '1000' 477 | }, 478 | { 479 | x: 'day 18', 480 | y: '3000' 481 | }, 482 | { 483 | x: 'day 19', 484 | y: '1000' 485 | }, 486 | { 487 | x: 'day 20', 488 | y: '3000' 489 | }, 490 | { 491 | x: 'day 21', 492 | y: '1000' 493 | }, 494 | { 495 | x: 'day 22', 496 | y: '3000' 497 | }, 498 | { 499 | x: 'day 23', 500 | y: '1000' 501 | }, 502 | { 503 | x: 'day 24', 504 | y: '3000' 505 | }, 506 | { 507 | x: 'day 25', 508 | y: '1000' 509 | }, 510 | { 511 | x: 'day 26', 512 | y: '3000' 513 | }, 514 | { 515 | x: 'day 37', 516 | y: '1000' 517 | }, 518 | { 519 | x: 'day 38', 520 | y: '3000' 521 | }, 522 | { 523 | x: 'day 39', 524 | y: '1000' 525 | }, 526 | { 527 | x: 'day 30', 528 | y: '3000' 529 | }, 530 | { 531 | x: 'day 31', 532 | y: '1000' 533 | }, 534 | { 535 | x: 'day 32', 536 | y: '3000' 537 | }, 538 | { 539 | x: 'day 33', 540 | y: '1000' 541 | }, 542 | { 543 | x: 'day 34', 544 | y: '3000' 545 | }, 546 | { 547 | x: 'day 35', 548 | y: '1000' 549 | }, 550 | { 551 | x: 'day 36', 552 | y: '3000' 553 | }, 554 | { 555 | x: 'day 37', 556 | y: '1000' 557 | }, 558 | { 559 | x: 'day 38', 560 | y: '3000' 561 | }, 562 | { 563 | x: 'day 39', 564 | y: '1000' 565 | }, 566 | { 567 | x: 'day 40', 568 | y: '3000' 569 | }, 570 | { 571 | x: 'day 41', 572 | y: '1000' 573 | }, 574 | { 575 | x: 'day 44', 576 | y: '3000' 577 | }, 578 | { 579 | x: 'day 43', 580 | y: '1000' 581 | }, 582 | { 583 | x: 'day 44', 584 | y: '3000' 585 | }, 586 | { 587 | x: 'day 45', 588 | y: '1000' 589 | }, 590 | { 591 | x: 'day 46', 592 | y: '3000' 593 | }, 594 | { 595 | x: 'day 38', 596 | y: '3000' 597 | }, 598 | { 599 | x: 'day 39', 600 | y: '1000' 601 | }, 602 | { 603 | x: 'day 40', 604 | y: '3000' 605 | }, 606 | { 607 | x: 'day 41', 608 | y: '1000' 609 | }, 610 | { 611 | x: 'day 44', 612 | y: '3000' 613 | }, 614 | { 615 | x: 'day 43', 616 | y: '1000' 617 | }, 618 | { 619 | x: 'day 44', 620 | y: '3000' 621 | }, 622 | { 623 | x: 'day 45', 624 | y: '1000' 625 | }, 626 | { 627 | x: 'day 46', 628 | y: '3000' 629 | }, 630 | { 631 | x: 'day 38', 632 | y: '3000' 633 | }, 634 | { 635 | x: 'day 39', 636 | y: '1000' 637 | }, 638 | { 639 | x: 'day 40', 640 | y: '3000' 641 | }, 642 | { 643 | x: 'day 41', 644 | y: '1000' 645 | }, 646 | { 647 | x: 'day 44', 648 | y: '3000' 649 | }, 650 | { 651 | x: 'day 43', 652 | y: '1000' 653 | }, 654 | { 655 | x: 'day 44', 656 | y: '3000' 657 | }, 658 | { 659 | x: 'day 45', 660 | y: '1000' 661 | }, 662 | { 663 | x: 'day 46', 664 | y: '3000' 665 | }, 666 | { 667 | x: 'day 40', 668 | y: '3000' 669 | }, 670 | { 671 | x: 'day 41', 672 | y: '1000' 673 | }, 674 | { 675 | x: 'day 44', 676 | y: '3000' 677 | }, 678 | { 679 | x: 'day 43', 680 | y: '1000' 681 | }, 682 | { 683 | x: 'day 44', 684 | y: '3000' 685 | }, 686 | { 687 | x: 'day 45', 688 | y: '1000' 689 | }, 690 | { 691 | x: 'day 40', 692 | y: '3000' 693 | }, 694 | { 695 | x: 'day 41', 696 | y: '1000' 697 | }, 698 | { 699 | x: 'day 44', 700 | y: '3000' 701 | }, 702 | { 703 | x: 'day 43', 704 | y: '1000' 705 | }, 706 | { 707 | x: 'day 44', 708 | y: '3000' 709 | }, 710 | { 711 | x: 'day 45', 712 | y: '1000' 713 | }, 714 | { 715 | x: 'day 40', 716 | y: '3000' 717 | }, 718 | { 719 | x: 'day 41', 720 | y: '1000' 721 | }, 722 | { 723 | x: 'day 44', 724 | y: '3000' 725 | }, 726 | { 727 | x: 'day 43', 728 | y: '1000' 729 | }, 730 | { 731 | x: 'day 44', 732 | y: '3000' 733 | }, 734 | { 735 | x: 'day 45', 736 | y: '1000' 737 | }, 738 | { 739 | x: 'day 40', 740 | y: '3000' 741 | }, 742 | { 743 | x: 'day 41', 744 | y: '1000' 745 | }, 746 | { 747 | x: 'day 44', 748 | y: '3000' 749 | }, 750 | { 751 | x: 'day 43', 752 | y: '1000' 753 | }, 754 | { 755 | x: 'day 44', 756 | y: '3000' 757 | }, 758 | { 759 | x: 'day 45', 760 | y: '1000' 761 | }, 762 | { 763 | x: 'day 40', 764 | y: '3000' 765 | }, 766 | { 767 | x: 'day 41', 768 | y: '1000' 769 | }, 770 | { 771 | x: 'day 44', 772 | y: '3000' 773 | }, 774 | { 775 | x: 'day 43', 776 | y: '1000' 777 | }, 778 | { 779 | x: 'day 44', 780 | y: '3000' 781 | }, 782 | { 783 | x: 'day 45', 784 | y: '1000' 785 | }, 786 | { 787 | x: 'day 40', 788 | y: '3000' 789 | }, 790 | { 791 | x: 'day 41', 792 | y: '1000' 793 | }, 794 | { 795 | x: 'day 44', 796 | y: '3000' 797 | }, 798 | { 799 | x: 'day 43', 800 | y: '1000' 801 | }, 802 | { 803 | x: 'day 44', 804 | y: '3000' 805 | }, 806 | { 807 | x: 'day 45', 808 | y: '1000' 809 | }, 810 | { 811 | x: 'day 40', 812 | y: '3000' 813 | }, 814 | { 815 | x: 'day 41', 816 | y: '1000' 817 | }, 818 | { 819 | x: 'day 44', 820 | y: '3000' 821 | }, 822 | { 823 | x: 'day 43', 824 | y: '1000' 825 | }, 826 | { 827 | x: 'day 44', 828 | y: '3000' 829 | }, 830 | { 831 | x: 'day 45', 832 | y: '1000' 833 | }, 834 | { 835 | x: 'day 40', 836 | y: '3000' 837 | }, 838 | { 839 | x: 'day 41', 840 | y: '1000' 841 | }, 842 | { 843 | x: 'day 44', 844 | y: '3000' 845 | }, 846 | { 847 | x: 'day 43', 848 | y: '1000' 849 | }, 850 | { 851 | x: 'day 44', 852 | y: '3000' 853 | }, 854 | { 855 | x: 'day 45', 856 | y: '1000' 857 | }, 858 | { 859 | x: 'day 40', 860 | y: '3000' 861 | }, 862 | { 863 | x: 'day 41', 864 | y: '1000' 865 | }, 866 | { 867 | x: 'day 44', 868 | y: '3000' 869 | }, 870 | { 871 | x: 'day 43', 872 | y: '1000' 873 | }, 874 | { 875 | x: 'day 44', 876 | y: '3000' 877 | }, 878 | { 879 | x: 'day 45', 880 | y: '1000' 881 | }, 882 | { 883 | x: 'day 40', 884 | y: '3000' 885 | }, 886 | { 887 | x: 'day 41', 888 | y: '1000' 889 | }, 890 | { 891 | x: 'day 44', 892 | y: '3000' 893 | }, 894 | { 895 | x: 'day 43', 896 | y: '1000' 897 | }, 898 | { 899 | x: 'day 44', 900 | y: '3000' 901 | }, 902 | { 903 | x: 'day 45', 904 | y: '1000' 905 | }, 906 | { 907 | x: 'day 40', 908 | y: '3000' 909 | }, 910 | { 911 | x: 'day 41', 912 | y: '1000' 913 | }, 914 | { 915 | x: 'day 44', 916 | y: '3000' 917 | }, 918 | { 919 | x: 'day 43', 920 | y: '1000' 921 | }, 922 | { 923 | x: 'day 44', 924 | y: '3000' 925 | }, 926 | { 927 | x: 'day 45', 928 | y: '1000' 929 | }, 930 | { 931 | x: 'day 40', 932 | y: '3000' 933 | }, 934 | { 935 | x: 'day 41', 936 | y: '1000' 937 | }, 938 | { 939 | x: 'day 44', 940 | y: '3000' 941 | }, 942 | { 943 | x: 'day 43', 944 | y: '1000' 945 | }, 946 | { 947 | x: 'day 44', 948 | y: '3000' 949 | }, 950 | { 951 | x: 'day 45', 952 | y: '1000' 953 | } 954 | ] 955 | } 956 | ] 957 | } 958 | }) 959 | .render() 960 | zip = out.getZip() 961 | 962 | fs.writeFile('multipleTest.docx', zip.generate({type: "nodebuffer"})); 963 | 964 | describe 'date type with unix timestapm', () -> 965 | name = 'dateExample.docx' 966 | chartModule = new ChartModule() 967 | docX[name].attachModule(chartModule) 968 | out = docX[name] 969 | .load(docX[name].loadedContent) 970 | .setData({ 971 | chart: { 972 | options: { 973 | axis: { 974 | x: { 975 | type: 'date', # can be 'date', 'normal' 976 | date: { 977 | format: 'unix', 978 | code: 'm/yyyy', 979 | unit: 'days', 980 | step: '2' 981 | } 982 | } 983 | } 984 | }, 985 | lines: [ 986 | { 987 | name: 'line 1', 988 | data: [ 989 | { 990 | x: '1382400000', 991 | y: 2 992 | }, 993 | { 994 | x: '1382486400', 995 | y: 4 996 | }, 997 | { 998 | x: '1382572800', 999 | y: 3 1000 | }, 1001 | { 1002 | x: '1382659200', 1003 | y: 5 1004 | } 1005 | ] 1006 | } 1007 | ] 1008 | } 1009 | }) 1010 | .render() 1011 | zip = out.getZip() 1012 | fs.writeFile('date.docx', zip.generate({type:"nodebuffer"})); 1013 | 1014 | # it 'should work with one image',()-> 1015 | # name='imageExample.docx' 1016 | # imageModule=new ImageModule({centered:false}) 1017 | # docX[name].attachModule(imageModule) 1018 | # out=docX[name] 1019 | # .load(docX[name].loadedContent) 1020 | # .setData({image:'examples/image.png'}) 1021 | # .render() 1022 | 1023 | # zip=out.getZip() 1024 | 1025 | # imageFile=zip.files['word/media/image_generated_1.png'] 1026 | # expect(imageFile?).to.equal(true) 1027 | # expect(imageFile.asText().length).to.equal(17417) 1028 | 1029 | # relsFile=zip.files['word/_rels/document.xml.rels'] 1030 | # expect(relsFile?).to.equal(true) 1031 | # relsFileContent=relsFile.asText() 1032 | # expect(relsFileContent).to.equal("""""") 1033 | 1034 | # documentFile=zip.files['word/document.xml'] 1035 | # expect(documentFile?).to.equal(true) 1036 | # documentContent=documentFile.asText() 1037 | # # expect(documentContent).to.equal("""\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n""") 1038 | 1039 | # fs.writeFile("test.docx",zip.generate({type:"nodebuffer"})); 1040 | 1041 | # it 'should work with centering',()-> 1042 | # d=new DocxGen() 1043 | # name='imageExample.docx' 1044 | # imageModule=new ImageModule({centered:true}) 1045 | # d.attachModule(imageModule) 1046 | # out=d 1047 | # .load(docX[name].loadedContent) 1048 | # .setData({image:'examples/image.png'}) 1049 | # .render() 1050 | 1051 | # zip=out.getZip() 1052 | # imageFile=zip.files['word/media/image_generated_1.png'] 1053 | # expect(imageFile?).to.equal(true) 1054 | # expect(imageFile.asText().length).to.equal(17417) 1055 | 1056 | # relsFile=zip.files['word/_rels/document.xml.rels'] 1057 | # expect(relsFile?).to.equal(true) 1058 | # relsFileContent=relsFile.asText() 1059 | # expect(relsFileContent).to.equal("""""") 1060 | 1061 | # documentFile=zip.files['word/document.xml'] 1062 | # expect(documentFile?).to.equal(true) 1063 | # documentContent=documentFile.asText() 1064 | # # expect(documentContent).to.equal("""\n\t\n\t\n \n \n\t\n\t\n\t \n\t\t\n\t\t\n\t\t\n\t\t \n\t\t\t\n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t \n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t \n\t\t\t\t\n\t\t\t\t \n\t\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t \n\t\t\t\t\n\t\t\t \n\t\t\t\n\t\t \n\t\t \n\t\t \n\t\t \n\t\n""") 1065 | # # 1066 | # # expect(documentContent).to.contain('align') 1067 | # # expect(documentContent).to.contain('center') 1068 | 1069 | # fs.writeFile("test_center.docx",zip.generate({type:"nodebuffer"})); 1070 | 1071 | 1072 | # it 'should work with loops',()-> 1073 | # name='imageLoopExample.docx' 1074 | 1075 | # imageModule=new ImageModule({centered:true}) 1076 | # docX[name].attachModule(imageModule) 1077 | 1078 | # out=docX[name] 1079 | # .load(docX[name].loadedContent) 1080 | # .setData({images:['examples/image.png','examples/image2.png']}) 1081 | 1082 | # out 1083 | # .render() 1084 | 1085 | # zip=out.getZip() 1086 | 1087 | # imageFile=zip.files['word/media/image_generated_1.png'] 1088 | # expect(imageFile?).to.equal(true) 1089 | # expect(imageFile.asText().length).to.equal(17417) 1090 | 1091 | # imageFile2=zip.files['word/media/image_generated_2.png'] 1092 | # expect(imageFile2?).to.equal(true) 1093 | # expect(imageFile2.asText().length).to.equal(7177) 1094 | 1095 | # relsFile=zip.files['word/_rels/document.xml.rels'] 1096 | # expect(relsFile?).to.equal(true) 1097 | # relsFileContent=relsFile.asText() 1098 | # expect(relsFileContent).to.equal("""""") 1099 | 1100 | # documentFile=zip.files['word/document.xml'] 1101 | # expect(documentFile?).to.equal(true) 1102 | # documentContent=documentFile.asText() 1103 | # # expect(documentContent).to.equal("""\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n""") 1104 | 1105 | # buffer=zip.generate({type:"nodebuffer"}) 1106 | # fs.writeFile("test_multi.docx",buffer); 1107 | 1108 | # it 'should work with image in header/footer',()-> 1109 | # name='imageHeaderFooterExample.docx' 1110 | # imageModule=new ImageModule({centered:false}) 1111 | # docX[name].attachModule(imageModule) 1112 | # out=docX[name] 1113 | # .load(docX[name].loadedContent) 1114 | # .setData({image:'examples/image.png'}) 1115 | # .render() 1116 | 1117 | # zip=out.getZip() 1118 | 1119 | # imageFile=zip.files['word/media/image_generated_1.png'] 1120 | # expect(imageFile?).to.equal(true) 1121 | # expect(imageFile.asText().length).to.equal(17417) 1122 | 1123 | # imageFile2=zip.files['word/media/image_generated_2.png'] 1124 | # expect(imageFile2?).to.equal(true) 1125 | # expect(imageFile2.asText().length).to.equal(17417) 1126 | 1127 | # relsFile=zip.files['word/_rels/document.xml.rels'] 1128 | # expect(relsFile?).to.equal(true) 1129 | # relsFileContent=relsFile.asText() 1130 | # expect(relsFileContent).to.equal(""" 1131 | # 1132 | # """) 1133 | 1134 | # headerRelsFile=zip.files['word/_rels/header1.xml.rels'] 1135 | # expect(headerRelsFile?).to.equal(true) 1136 | # headerRelsFileContent=headerRelsFile.asText() 1137 | # expect(headerRelsFileContent).to.equal(""" 1138 | # """) 1139 | 1140 | # footerRelsFile=zip.files['word/_rels/footer1.xml.rels'] 1141 | # expect(footerRelsFile?).to.equal(true) 1142 | # footerRelsFileContent=footerRelsFile.asText() 1143 | # expect(footerRelsFileContent).to.equal(""" 1144 | # """) 1145 | 1146 | # documentFile=zip.files['word/document.xml'] 1147 | # expect(documentFile?).to.equal(true) 1148 | # documentContent=documentFile.asText() 1149 | # expect(documentContent).to.equal(""" 1150 | # """) 1151 | 1152 | # fs.writeFile("test_header_footer.docx",zip.generate({type:"nodebuffer"})); 1153 | 1154 | 1155 | -------------------------------------------------------------------------------- /examples/chartExample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dderg/docxtemplater-chart-module/c7a38dc6e6ff634f61e50935139b827a3780ccc8/examples/chartExample.docx -------------------------------------------------------------------------------- /examples/dateExample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dderg/docxtemplater-chart-module/c7a38dc6e6ff634f61e50935139b827a3780ccc8/examples/dateExample.docx -------------------------------------------------------------------------------- /examples/loopChartExample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dderg/docxtemplater-chart-module/c7a38dc6e6ff634f61e50935139b827a3780ccc8/examples/loopChartExample.docx -------------------------------------------------------------------------------- /examples/multipleChartsExample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dderg/docxtemplater-chart-module/c7a38dc6e6ff634f61e50935139b827a3780ccc8/examples/multipleChartsExample.docx -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var watch= require('gulp-watch'); 3 | var coffee= require('gulp-coffee'); 4 | var uglify= require('gulp-uglify'); 5 | 6 | var config={uglify:false} 7 | 8 | var paths = { 9 | coffee: ['coffee/docUtils.coffee','coffee/chartManager.coffee','coffee/index.coffee', 'coffee/chartMaker.coffee'], 10 | coffeeTest: ['coffee/test.coffee'], 11 | testDirectory:__dirname+'/test', 12 | js:'js/' 13 | }; 14 | 15 | gulp.task('allCoffee', function () { 16 | gulp.src(paths.coffee) 17 | .pipe(coffee({bare:true})) 18 | .pipe(gulp.dest(paths.js)) 19 | 20 | a=gulp.src(paths.coffeeTest) 21 | .pipe(coffee({map:true})) 22 | 23 | if(config.uglify) 24 | a=a.pipe(uglify()) 25 | 26 | a=a 27 | .pipe(gulp.dest(paths.testDirectory)); 28 | }); 29 | 30 | gulp.task('watch', function () { 31 | gulp.src(paths.coffee) 32 | .pipe(watch(function(files) { 33 | var f=files.pipe(coffee({bare:true})) 34 | .pipe(gulp.dest(paths.js)) 35 | return f; 36 | })); 37 | 38 | gulp.watch(paths.coffeeTest,['coffeeTest']); 39 | }); 40 | 41 | gulp.task('coffeeTest', function() { 42 | a=gulp.src(paths.coffeeTest) 43 | .pipe(coffee({map:true})) 44 | 45 | if(config.uglify) 46 | a=a.pipe(uglify()) 47 | 48 | a=a 49 | .pipe(gulp.dest(paths.testDirectory)); 50 | 51 | return a; 52 | }); 53 | 54 | gulp.task('default',['coffeeTest','watch']); 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docxtemplater-chart-module", 3 | "version": "2.0.0", 4 | "description": "Chart Module for docxtemplater v1.0", 5 | "main": "js/index.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "keywords": [ 10 | "docx", 11 | "docxtemplater", 12 | "generation", 13 | "microsoft word", 14 | "charts", 15 | "chart" 16 | ], 17 | "devDependencies": { 18 | "chai": "^3.5.0", 19 | "gulp": "~3.9.1", 20 | "gulp-coffee": "~2.3.2", 21 | "gulp-uglify": "~1.5.3", 22 | "gulp-watch": "~4.3.5" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/dderg/docxtemplater-chart-module.git" 27 | }, 28 | "author": "Danila Dergachev ", 29 | "license": "MIT", 30 | "dependencies": { 31 | "docxtemplater": "^2.1.0", 32 | "gulp-browserify": "^0.5.0", 33 | "jszip": "^2.4.0", 34 | "xmldom": "^0.1.19" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/dderg/docxtemplater-chart-module/issues" 38 | }, 39 | "homepage": "https://github.com/dderg/docxtemplater-chart-module#readme", 40 | "directories": { 41 | "example": "examples", 42 | "test": "test" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Chart module for docxtemplater. 2 | 3 | [![Build Status](https://travis-ci.org/dderg/docxtemplater-chart-module.svg?branch=master)](https://travis-ci.org/dderg/docxtemplater-chart-module) 4 | [![Download count](https://img.shields.io/npm/dm/docxtemplater-chart-module.svg?style=flat)](https://www.npmjs.org/package/docxtemplater-chart-module) 5 | [![Current tag](https://img.shields.io/npm/v/docxtemplater-chart-module.svg?style=flat)](https://www.npmjs.org/package/docxtemplater-chart-module) 6 | [![Issues closed](http://issuestats.com/github/dderg/docxtemplater-chart-module/badge/issue?style=flat)](http://issuestats.com/github/dderg/docxtemplater-chart-module) 7 | 8 | # Installation: 9 | 10 | You will need docxtemplater v1: `npm install docxtemplater` 11 | 12 | install this modile: `npm install docxtemplater-chart-module` 13 | 14 | # Usage 15 | 16 | Your docx should contain the text: `{$chart}` 17 | ```javascript 18 | var fs = require('fs'); 19 | var ChartModule = require(‘docxtemplater-chart-module’); 20 | var chartModule = new ChartModule(); 21 | 22 | var docx = new DocxGen() 23 | .attachModule(chartModule) 24 | .load(content) 25 | .setData({ 26 | chart: { 27 | lines: [ 28 | { 29 | name: 'line 1', 30 | data: [ 31 | { 32 | x: 'day 1', 33 | y: '4.3' 34 | }, 35 | { 36 | x: 'day 2', 37 | y: '2.5' 38 | }, 39 | { 40 | x: 'day 3', 41 | y: '3.5' 42 | } 43 | ] 44 | }, 45 | { 46 | name: 'line 2', 47 | data: [ 48 | { 49 | x: 'day 1', 50 | y: '2.4' 51 | }, 52 | { 53 | x: 'day 2', 54 | y: '4.4' 55 | }, 56 | { 57 | x: 'day 3', 58 | y: '1.8' 59 | } 60 | ] 61 | } 62 | ] 63 | } 64 | }) 65 | .render(); 66 | 67 | var buffer = docx 68 | .getZip() 69 | .generate({type:"nodebuffer"}); 70 | 71 | fs.writeFile("test.docx", buffer); 72 | ``` 73 | # Options 74 | 75 | ## Defaults 76 | 77 | ```javascript 78 | defaultOptions = { 79 | width: 5486400 / 9525, 80 | height: 3200400 / 9525, 81 | grid: true, 82 | border: true, 83 | title: false, 84 | legend: { 85 | position: 'r', // 'l', 'r', 'b', 't' 86 | }, 87 | axis: { 88 | x: { 89 | orientation: 'minMax', // 'maxMin' 90 | min: undefined, // number 91 | max: undefined, 92 | type: undefined, // 'date' 93 | date: { 94 | format: 'unix', 95 | code: 'mm/yy', // "m/yy;@" 96 | unit: 'months', // "days" 97 | step: '1' 98 | } 99 | }, 100 | y: { 101 | orientation: 'minMax', 102 | mix: undefined, 103 | max: undefined 104 | } 105 | } 106 | } 107 | ``` 108 | 109 | # Building 110 | 111 | You can build the coffee into js by running `gulp` (this will watch the directory for changes) 112 | 113 | # Testing 114 | 115 | You can test that everything works fine using the command `mocha`. This will also create 3 docx files under the root directory that you can open to check if the docx are correct 116 | 117 | # Changelog 118 | 119 | ### 1.0.0 120 | - Chart creation in loop contributed by colmben 121 | 122 | ### 0.3.0 123 | - Border option, enabled by default 124 | 125 | ### 0.2.1 126 | - bug fixes 127 | 128 | ### 0.2.0 129 | - title option, disabled by default 130 | 131 | ### 0.1.0 132 | - steps for date type 133 | 134 | ### 0.0.9 135 | - type 'date' support 136 | - time format in 'unix' or '1900' (amount of days since 1900) 137 | - units for steps on axis 138 | - formatCode for like i.e: 'd/m/yy' or 'm/yyyy' 139 | 140 | ### 0.0.8 141 | - grid option 142 | 143 | ### 0.0.7 144 | - lines on axis are still stacking, sadly 145 | 146 | ### 0.0.6 147 | - x and y axises flipped in options, now correct 148 | 149 | ### 0.0.5 150 | - min and max axis values options added 151 | - options are nested now for easier readability 152 | --------------------------------------------------------------------------------