├── .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 | [](https://travis-ci.org/dderg/docxtemplater-chart-module)
4 | [](https://www.npmjs.org/package/docxtemplater-chart-module)
5 | [](https://www.npmjs.org/package/docxtemplater-chart-module)
6 | [](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 |
--------------------------------------------------------------------------------