Commit 7fddd644 by Edgar Hipp

first commit

parents
/*.docx
test/
js/
node_modules
fs=require('fs')
DOMParser = require('xmldom').DOMParser
XMLSerializer= require('xmldom').XMLSerializer
JSZip=require('jszip')
url=require('url')
http=require('http')
https=require('https')
DocUtils= {}
DocUtils.env= if fs.readFile? then 'node' else 'browser'
DocUtils.docX=[]
DocUtils.docXData=[]
DocUtils.getPathConfig=()->
if !DocUtils.pathConfig? then return ""
if DocUtils.env=='node' then return DocUtils.pathConfig.node
DocUtils.pathConfig.browser
DocUtils.escapeRegExp= (str) ->
str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
DocUtils.charMap=
'&':"&"
"'":"'"
"<":"&lt;"
">":"&gt;"
DocUtils.wordToUtf8= (string) ->
for endChar,startChar of DocUtils.charMap
string=string.replace(new RegExp(DocUtils.escapeRegExp(startChar),"g"),endChar)
string
DocUtils.utf8ToWord= (string) ->
for startChar,endChar of DocUtils.charMap
string=string.replace(new RegExp(DocUtils.escapeRegExp(startChar),"g"),endChar)
string
DocUtils.defaultParser=(tag) ->
return {
'get':(scope) ->
if tag=='.' then return scope else return scope[tag]
}
DocUtils.loadDoc= (path,options={}) ->
noDocx= if options.docx? then !options.docx else false
async=if options.async? then options.async else false
intelligentTagging=if options.intelligentTagging? then options.intelligentTagging else false
callback=if options.callback? then options.callback else null
basePath=""
if !path? then throw new Error('path not defined')
if path.indexOf('/')!=-1
totalPath= path
fileName= totalPath
else
fileName= path
if basePath=="" && DocUtils.pathConfig? #set basePath only if it wasn't set as an argument
basePath=DocUtils.getPathConfig()
totalPath= basePath+path
loadFile = (data) ->
DocUtils.docXData[fileName]=data
if noDocx==false
DocUtils.docX[fileName]=new DocxGen(data,{},{intelligentTagging:intelligentTagging})
return DocUtils.docX[fileName]
if callback?
return callback(DocUtils.docXData[fileName])
if async==false
return DocUtils.docXData[fileName]
if DocUtils.env=='browser'
return DocUtils.loadHttp totalPath,(err,result)->
if err
console.error 'error'
if callback? then callback(true)
return
loadFile(result)
,async
else
if path.indexOf("http")==0
urloptions=(url.parse(path))
options =
hostname:urloptions.hostname
path:urloptions.path
method: 'GET'
rejectUnauthorized:false
errorCallback= (e) ->
throw new Error("Error on HTTPS Call")
reqCallback= (res)->
res.setEncoding('binary')
data = ""
res.on('data', (chunk)->
data += chunk
)
res.on('end', ()->
loadFile(data))
switch urloptions.protocol
when "https:"
req = https.request(options, reqCallback).on('error',errorCallback)
when 'http:'
req = http.request(options, reqCallback).on('error',errorCallback)
req.end();
else
if async==true
fs.readFile totalPath,"binary", (err, data) ->
if err
if callback? then return callback(err)
else
return loadFile(data)
else
try
data=fs.readFileSync(totalPath,"binary")
return loadFile(data)
catch e
if callback? then return callback(e)
DocUtils.loadHttp=(result,callback,async=false)->
if DocUtils.env=='node'
urloptions=(url.parse(result))
options =
hostname:urloptions.hostname
path:urloptions.path
method: 'GET'
rejectUnauthorized:false
errorCallback= (e) ->
callback(e)
reqCallback= (res)->
res.setEncoding('binary')
data = ""
res.on 'data',(chunk)-> data += chunk
res.on 'end',()->callback(null,data)
switch urloptions.protocol
when "https:"
req = https.request(options, reqCallback).on('error',errorCallback)
when 'http:'
req = http.request(options, reqCallback).on('error',errorCallback)
req.end()
else
response=""
xhrDoc= new XMLHttpRequest()
xhrDoc.open('GET', result , async)
if xhrDoc.overrideMimeType
xhrDoc.overrideMimeType('text/plain; charset=x-user-defined')
xhrDoc.onreadystatechange =(e)->
if this.readyState == 4
if this.status == 200
response=this.response
callback(null,this.response)
else
callback(true)
xhrDoc.send()
return response
DocUtils.unsecureQrCode=(result,callback)->
if DocUtils.env=='node'
console.error 'Your are using an insecure qrcode image finder. With this function, a malicious user could read anyfile that is on the server where docxtemplater resides. The qrcode module now accepts a function as its first parameter instead of a bool see http://docxtemplater.readthedocs.org/en/latest/configuration.html#image-replacing'
if result.substr(0,5)=='http:' or result.substr(0,6)=='https:'
DocUtils.loadHttp(result,callback)
else if result.substr(0,4)=='gen:'
defaultImageCreator=(arg,callback) ->
#This is the image of an arrow, you can replace this function by whatever you want to generate an image
res=JSZip.base64.decode("iVBORw0KGgoAAAANSUhEUgAAABcAAAAXCAIAAABvSEP3AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACXSURBVDhPtY7BDYAwDAMZhCf7b8YMxeCoatOQJhWc/KGxT2zlCyaWcz8Y+X7Bs1TFVJSwIHIYyFkQufWIRVX9cNJyW1QpEo4rixaEe7JuQagAUctb7ZFYFh5MVJPBe84CVBnB42//YsZRgKjFDBVg3cI9WbRwXLktQJX8cNIiFhM1ZuTWk7PIYSBhkVcLzwIiCjCxhCjlAkBqYnqFoQQ2AAAAAElFTkSuQmCC")
callback(null,res)
defaultImageCreator(result,callback)
else if result!=null and result!= undefined and result.substr(0,22)!= 'error decoding QR Code'
if DocUtils.env=='node'
fs.readFile(DocUtils.getPathConfig()+result,callback)
else
DocUtils.loadHttp(DocUtils.getPathConfig()+result,callback)
else
callback(new Error("Image undefined"))
DocUtils.tags=
start:'{'
end:'}'
DocUtils.clone = (obj) ->
if not obj? or typeof obj isnt 'object'
return obj
if obj instanceof Date
return new Date(obj.getTime())
if obj instanceof RegExp
flags = ''
flags += 'g' if obj.global?
flags += 'i' if obj.ignoreCase?
flags += 'm' if obj.multiline?
flags += 'y' if obj.sticky?
return new RegExp(obj.source, flags)
newInstance = new obj.constructor()
for key of obj
newInstance[key] = DocUtils.clone obj[key]
return newInstance
DocUtils.xml2Str = (xmlNode) ->
a= new XMLSerializer()
a.serializeToString(xmlNode)
DocUtils.Str2xml= (str,errorHandler) ->
parser=new DOMParser({errorHandler})
xmlDoc=parser.parseFromString(str,"text/xml")
DocUtils.replaceFirstFrom = (string,search,replace,from) -> #replace first occurence of search (can be regex) after *from* offset
string.substr(0,from)+string.substr(from).replace(search,replace)
DocUtils.encode_utf8 = (s)->
unescape(encodeURIComponent(s))
DocUtils.convert_spaces= (s) ->
s.replace(new RegExp(String.fromCharCode(160),"g")," ")
DocUtils.decode_utf8= (s) ->
try
if s==undefined then return undefined
return decodeURIComponent(escape(DocUtils.convert_spaces(s))) #replace Ascii 160 space by the normal space, Ascii 32
catch e
console.error s
console.error 'could not decode'
throw new Error('end')
DocUtils.base64encode= (b) ->
btoa(unescape(encodeURIComponent(b)))
DocUtils.preg_match_all= (regex, content) ->
###regex is a string, content is the content. It returns an array of all matches with their offset, for example:
regex=la
content=lolalolilala
returns: [{0:'la',offset:2},{0:'la',offset:8},{0:'la',offset:10}]
###
regex= (new RegExp(regex,'g')) unless (typeof regex=='object')
matchArray= []
replacer = (match,pn ..., offset, string)->
pn.unshift match #add match so that pn[0] = whole match, pn[1]= first parenthesis,...
pn.offset= offset
matchArray.push pn
content.replace regex,replacer
matchArray
DocUtils.sizeOfObject = (obj) ->
size=0
log = 0
for key of obj
size++
size
DocUtils.maxArray = (a) -> Math.max.apply(null, a)
DocUtils.getOuterXml=(text,start,end,xmlTag)-> #tag: w:t
endTag= text.indexOf('</'+xmlTag+'>',end)
if endTag==-1 then throw new Error("can't find endTag #{endTag}")
endTag+=('</'+xmlTag+'>').length
startTag = Math.max text.lastIndexOf('<'+xmlTag+'>',start), text.lastIndexOf('<'+xmlTag+' ',start)
if startTag==-1 then throw new Error("can't find startTag")
{"text":text.substr(startTag,endTag-startTag),startTag,endTag}
module.exports=DocUtils
DocUtils=require('./docUtils')
DocXTemplater=require('./docxTemplater')
vm=require('vm')
JSZip=require('jszip')
QrCode=require('qrcode-reader')
module.exports= class DocxQrCode
constructor:(imageData, @xmlTemplater,@imgName="",@num,@callback)->
@callbacked=false
@data=imageData
if @data==undefined then throw new Error("data of qrcode can't be undefined")
if DocUtils.env=='browser'
@base64Data=JSZip.base64.encode(@data)
@ready=false
@result=null
decode:(@callback) ->
_this= this
@qr= new QrCode()
@qr.callback= () ->
_this.ready= true
_this.result= this.result
testdoc= new _this.xmlTemplater.currentClass this.result, _this.xmlTemplater.toJson()
testdoc.applyTags()
_this.result=testdoc.content
_this.searchImage()
if DocUtils.env=='browser'
@qr.decode("data:image/png;base64,#{@base64Data}")
else
@qr.decode(@data,@data.decoded)
searchImage:() ->
cb=(err,@data=@data.data)=>
if err then console.error err
@callback(this,@imgName,@num)
if !@result? then return cb()
@xmlTemplater.DocxGen.qrCode(@result,cb)
DocUtils=require('./docUtils')
module.exports = class ImgManager
imageExtensions=['gif','jpeg','jpg','emf','png']
constructor:(@zip,@fileName)->
@endFileName=@fileName.replace(/^.*?([a-z0-9]+)\.xml$/,"$1")
getImageList: () ->
regex= ///
[^.]+ #name
\. #dot
([^.]+) #extension
///
imageList= []
for index of @zip.files
extension= index.replace(regex,'$1')
if extension in imageExtensions #Defined in constructor
imageList.push {"path":index,files:@zip.files[index]}
imageList
setImage:(fileName,data,options={})->
@zip.remove(fileName)
@zip.file(fileName,data,options)
hasImage:(fileName)->
@zip.files[fileName]?
loadImageRels: () ->
file=@zip.files["word/_rels/#{@endFileName}.xml.rels"]
if file==undefined then return
content= DocUtils.decode_utf8 file.asText()
@xmlDoc= DocUtils.Str2xml content
RidArray = ((parseInt tag.getAttribute("Id").substr(3)) for tag in @xmlDoc.getElementsByTagName('Relationship')) #Get all Rids
@maxRid=DocUtils.maxArray(RidArray)
@imageRels=[]
this
addExtensionRels: (contentType,extension) -> #Add an extension type in the [Content_Types.xml], is used if for example you want word to be able to read png files (for every extension you add you need a contentType)
#content = DocUtils.decode_utf8 @zip.files["[Content_Types].xml"].asText()
content = @zip.files["[Content_Types].xml"].asText()
xmlDoc= DocUtils.Str2xml content
addTag= true
defaultTags=xmlDoc.getElementsByTagName('Default')
for tag in defaultTags
if tag.getAttribute('Extension')==extension then addTag= false
if addTag
types=xmlDoc.getElementsByTagName("Types")[0]
newTag=xmlDoc.createElement 'Default'
newTag.namespaceURI= null
newTag.setAttribute('ContentType',contentType)
newTag.setAttribute('Extension',extension)
types.appendChild newTag
@setImage "[Content_Types].xml",DocUtils.encode_utf8 DocUtils.xml2Str xmlDoc
addImageRels: (imageName,imageData,i=0) -> #Adding an image and returns it's Rid
realImageName=if i==0 then imageName else imageName+"(#{i})"
if @zip.files["word/media/#{realImageName}"]?
return @addImageRels(imageName,imageData,i+1)
@maxRid++
file=
'name':"word/media/#{realImageName}"
'data':imageData
'options':
base64: false
binary: true
compression: null
date: new Date()
dir: false
@zip.file file.name,file.data,file.options
extension= realImageName.replace(/[^.]+\.([^.]+)/,'$1')
@addExtensionRels("image/#{extension}",extension)
relationships= @xmlDoc.getElementsByTagName("Relationships")[0]
newTag= @xmlDoc.createElement 'Relationship' #,relationships.namespaceURI
newTag.namespaceURI= null
newTag.setAttribute('Id',"rId#{@maxRid}")
newTag.setAttribute('Type','http://schemas.openxmlformats.org/officeDocument/2006/relationships/image')
newTag.setAttribute('Target',"media/#{realImageName}")
relationships.appendChild newTag
@setImage("word/_rels/#{@endFileName}.xml.rels",DocUtils.encode_utf8 DocUtils.xml2Str @xmlDoc)
@maxRid
getImageName:(id)->
nameCandidate="Copie_"+id+".png"
fullPath=@getFullPath(nameCandidate)
if @hasImage(fullPath)
return @getImageName(id+1)
nameCandidate
getFullPath:(imgName)->"word/media/#{imgName}"
getImageByRid:(rId)-> #This is to get an image by it's rId (returns null if no img was found)
relationships= @xmlDoc.getElementsByTagName('Relationship')
for relationship in relationships
cRId= relationship.getAttribute('Id')
if rId==cRId
path=relationship.getAttribute('Target')
if path.substr(0,6)=='media/'
return @zip.files["word/#{path}"]
else
throw new Error("Rid is not an image")
throw new Error("No Media with this Rid found")
DocUtils=require('./docUtils')
DocxQrCode=require('./docxQrCode')
PNG=require('png-js')
JSZip=require('jszip')
module.exports= class ImgReplacer
constructor: (@xmlTemplater)->
@imgMatches=[]
@xmlTemplater.numQrCode=0
this
findImages:() ->
@imgMatches= DocUtils.preg_match_all ///
<w:drawing[^>]*>
.*?<a:blip.r:embed.*?
</w:drawing>
///g, @xmlTemplater.content
this
replaceImages: ()->
@qr=[]
@xmlTemplater.numQrCode+=@imgMatches.length
@replaceImage(match,u) for match,u in @imgMatches
this
imageSetter:(docxqrCode) ->
if docxqrCode.callbacked==true then return
docxqrCode.callbacked=true
docxqrCode.xmlTemplater.numQrCode--
docxqrCode.xmlTemplater.imgManager.setImage("word/media/#{docxqrCode.imgName}",docxqrCode.data,{binary:true})
docxqrCode.xmlTemplater.DocxGen.qrCodeCallBack(docxqrCode.xmlTemplater.fileName+'-'+docxqrCode.num,false)
replaceImage:(match,u)->
num=@xmlTemplater.DocxGen.qrCodeNumCallBack
@xmlTemplater.DocxGen.qrCodeNumCallBack++
try
xmlImg= DocUtils.Str2xml '<?xml version="1.0" ?><w:document mc:Ignorable="w14 wp14" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">'+match[0]+'</w:document>',(_i,type)->if _i=='fatalError' then throw "fatalError"
catch e
return
tagrId= xmlImg.getElementsByTagName("a:blip")[0]
if tagrId==undefined then throw new Error('tagRiD undefined !')
rId = tagrId.getAttribute('r:embed')
oldFile= @xmlTemplater.imgManager.getImageByRid(rId)
tag= xmlImg.getElementsByTagName("wp:docPr")[0]
if tag==undefined then throw new Error('tag undefined')
if tag.getAttribute("name").substr(0,6)=="Copie_" then return #if image is already a replacement then do nothing
imgName= @xmlTemplater.imgManager.getImageName(@xmlTemplater.imageId)
@xmlTemplater.DocxGen.qrCodeCallBack(@xmlTemplater.fileName+'-'+num,true)
newId= @xmlTemplater.imgManager.addImageRels(imgName,"")
@xmlTemplater.imageId++
@xmlTemplater.imgManager.setImage(@xmlTemplater.imgManager.getFullPath(imgName),oldFile.data,{binary:true})
tag.setAttribute('name',"#{imgName}")
tagrId.setAttribute('r:embed',"rId#{newId}")
imageTag=xmlImg.getElementsByTagName('w:drawing')[0]
if imageTag==undefined then throw new Error('imageTag undefined')
replacement= DocUtils.xml2Str imageTag
@xmlTemplater.content= @xmlTemplater.content.replace(match[0], replacement)
if DocUtils.env=='browser'
@qr[u]= new DocxQrCode(oldFile.asBinary(),@xmlTemplater,imgName,num)
@qr[u].decode(@imageSetter)
else
mockedQrCode={xmlTemplater:@xmlTemplater,imgName:imgName,data:oldFile.asBinary(),num:num}
if /\.png$/.test(oldFile.name)
do (imgName) =>
base64= JSZip.base64.encode oldFile.asBinary()
binaryData = new Buffer(base64, 'base64')
png= new PNG(binaryData)
finished= (a) =>
png.decoded= a
try
@qr[u]= new DocxQrCode(png,@xmlTemplater,imgName,num)
@qr[u].decode(@imageSetter)
catch e
@imageSetter(mockedQrCode)
dat= png.decode(finished)
else
@imageSetter(mockedQrCode)
SubContent=require('../node_modules/docxtemplater/js/subContent.js')
ImgManager=require('./imgManager')
fs=require('fs')
class ImageModule
constructor:(@options={})->
if !@options.centered? then @options.centered=false
@imageNumber=1
handleEvent:(event,eventData)->
if event=='rendering-file'
@renderingFileName=eventData
gen=@manager.getInstance('gen')
@imgManager=new ImgManager(gen.zip,@renderingFileName)
get:(data)->
if data=='loopType'
templaterState=@manager.getInstance('templaterState')
if templaterState.textInsideTag[0]=='%'
return 'image'
null
getNextImageName:()->
name="image_generated_#{@imageNumber}.png"
@imageNumber++
name
handle:(type,data)->
if type=='replaceTag' and data=='image'
scopeManager=@manager.getInstance('scopeManager')
xmlTemplater=@manager.getInstance('xmlTemplater')
templaterState=@manager.getInstance('templaterState')
tag = templaterState.textInsideTag.substr(1)
imgName=scopeManager.getValueFromScope(tag)
if imgName=='undefined' then throw new Error "imageName is undefined for:#{tag}"
try
imgData=fs.readFileSync(imgName)
catch e
console.error "image not defined #{imgName}"
throw e
rId=@imgManager
.loadImageRels()
.addImageRels(@getNextImageName(),imgData)
if @options.centered==false
subContent=new SubContent(xmlTemplater.content).getInnerTag(templaterState).getOuterXml('w:t')
newText=@getImageXml(rId,"description")
if @options.centered==true
subContent=new SubContent(xmlTemplater.content).getInnerTag(templaterState).getOuterXml('w:p')
newText=@getImageXmlCentered(rId)
xmlTemplater.replaceXml(subContent,newText)
null
getImageXml:(rId="1",imageDescription="")->
return """
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="1905000" cy="1905000"/>
<wp:effectExtent l="0" t="0" r="0" b="0"/>
<wp:docPr id="2" name="Image 2" descr="#{imageDescription}"/>
<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="#{imageDescription}"/>
<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="1905000" cy="1905000"/>
</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>
"""
getImageXmlCentered:(rId="1")->
"""
<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="1905000" cy="1905000"/>
<wp:docPr id="15" name="rId6.png"/>
<a:graphic>
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic>
<pic:nvPicPr>
<pic:cNvPr id="15" name="rId6.png"/>
<pic:cNvPicPr/>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId#{rId}"/>
</pic:blipFill>
<pic:spPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="1905000" cy="1905000"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
</w:p>
"""
module.exports=ImageModule
fs=require('fs')
DocxGen=require('docxtemplater')
expect=require('chai').expect
fileNames=[
'imageExample.docx',
'imageLoopExample.docx',
]
ImageModule=require('../js/index.js')
docX={}
loadFile=(name)->
if fs.readFileSync? then return fs.readFileSync(__dirname+"/../examples/"+name,"binary")
xhrDoc= new XMLHttpRequest()
xhrDoc.open('GET',"../examples/"+name,false)
if (xhrDoc.overrideMimeType)
xhrDoc.overrideMimeType('text/plain; charset=x-user-defined')
xhrDoc.send()
xhrDoc.response
for name in fileNames
content=loadFile(name)
docX[name]=new DocxGen()
docX[name].loadedContent=content
describe 'image adding with {% image} syntax', ()->
it 'should work with one image',()->
name='imageExample.docx'
imageModule=new ImageModule({centered:false})
docX[name].attachModule(imageModule)
out=docX[name]
.load(docX[name].loadedContent)
.setData({image:'examples/image.png'})
.render()
zip=out.getZip()
imageFile=zip.files['word/media/image_generated_1.png']
expect(imageFile?).to.equal(true)
expect(imageFile.asText().length).to.equal(17417)
relsFile=zip.files['word/_rels/document.xml.rels']
expect(relsFile?).to.equal(true)
relsFileContent=relsFile.asText()
expect(relsFileContent).to.equal("""<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes" Target="footnotes.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes" Target="endnotes.xml"/><Relationship Id="hId0" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" Target="header0.xml"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image_generated_1.png"/></Relationships>""")
documentFile=zip.files['word/document.xml']
expect(documentFile?).to.equal(true)
documentContent=documentFile.asText()
expect(documentContent).to.equal("""<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:pPr></w:pPr><w:r><w:rPr></w:rPr><w:drawing>\n <wp:inline distT="0" distB="0" distL="0" distR="0">\n <wp:extent cx="1905000" cy="1905000"/>\n <wp:effectExtent l="0" t="0" r="0" b="0"/>\n <wp:docPr id="2" name="Image 2" descr="description"/>\n <wp:cNvGraphicFramePr>\n <a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/>\n </wp:cNvGraphicFramePr>\n <a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">\n <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">\n <pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">\n <pic:nvPicPr>\n <pic:cNvPr id="0" name="Picture 1" descr="description"/>\n <pic:cNvPicPr>\n <a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>\n </pic:cNvPicPr>\n </pic:nvPicPr>\n <pic:blipFill>\n <a:blip r:embed="rId6">\n <a:extLst>\n <a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">\n <a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>\n </a:ext>\n </a:extLst>\n </a:blip>\n <a:srcRect/>\n <a:stretch>\n <a:fillRect/>\n </a:stretch>\n </pic:blipFill>\n <pic:spPr bwMode="auto">\n <a:xfrm>\n <a:off x="0" y="0"/>\n <a:ext cx="1905000" cy="1905000"/>\n </a:xfrm>\n <a:prstGeom prst="rect">\n <a:avLst/>\n </a:prstGeom>\n <a:noFill/>\n <a:ln>\n <a:noFill/>\n </a:ln>\n </pic:spPr>\n </pic:pic>\n </a:graphicData>\n </a:graphic>\n </wp:inline>\n</w:drawing></w:r><w:bookmarkStart w:id="13" w:name="_GoBack"/><w:bookmarkEnd w:id="13"/></w:p><w:sectPr><w:headerReference w:type="default" r:id="hId0"/><w:type w:val="continuous"/><w:pgSz w:w="12240" w:h="15840" w:orient="portrait"/><w:pgMar w:top="2810" w:left="1800" w:right="1800" w:bottom="1440"/><w:cols w:num="1" w:sep="off" w:equalWidth="1"/></w:sectPr></w:body></w:document>""")
fs.writeFile("test.docx",zip.generate({type:"nodebuffer"}));
it 'should work with centering',()->
d=new DocxGen()
name='imageExample.docx'
imageModule=new ImageModule({centered:true})
d.attachModule(imageModule)
out=d
.load(docX[name].loadedContent)
.setData({image:'examples/image.png'})
.render()
zip=out.getZip()
imageFile=zip.files['word/media/image_generated_1.png']
expect(imageFile?).to.equal(true)
expect(imageFile.asText().length).to.equal(17417)
relsFile=zip.files['word/_rels/document.xml.rels']
expect(relsFile?).to.equal(true)
relsFileContent=relsFile.asText()
expect(relsFileContent).to.equal("""<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/><Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering\" Target=\"numbering.xml\"/><Relationship Id=\"rId3\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings\" Target=\"settings.xml\"/><Relationship Id=\"rId4\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes\" Target=\"footnotes.xml\"/><Relationship Id=\"rId5\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes\" Target=\"endnotes.xml\"/><Relationship Id=\"hId0\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header\" Target=\"header0.xml\"/><Relationship Id=\"rId6\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\" Target=\"media/image_generated_1.png\"/></Relationships>""")
documentFile=zip.files['word/document.xml']
expect(documentFile?).to.equal(true)
documentContent=documentFile.asText()
expect(documentContent).to.equal("""<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><w:document xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:wx=\"http://schemas.microsoft.com/office/word/2003/auxHint\" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"><w:body><w:p>\n\t<w:pPr>\n\t<w:jc w:val=\"center\"/>\n </w:pPr>\n <w:r>\n\t<w:rPr/>\n\t<w:drawing>\n\t <wp:inline distT=\"0\" distB=\"0\" distL=\"0\" distR=\"0\">\n\t\t<wp:extent cx=\"1905000\" cy=\"1905000\"/>\n\t\t<wp:docPr id=\"15\" name=\"rId6.png\"/>\n\t\t<a:graphic>\n\t\t <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">\n\t\t\t<pic:pic>\n\t\t\t <pic:nvPicPr>\n\t\t\t\t<pic:cNvPr id=\"15\" name=\"rId6.png\"/>\n\t\t\t\t<pic:cNvPicPr/>\n\t\t\t </pic:nvPicPr>\n\t\t\t <pic:blipFill>\n\t\t\t\t<a:blip r:embed=\"rId6\"/>\n\t\t\t\t</pic:blipFill>\n\t\t\t <pic:spPr>\n\t\t\t\t<a:xfrm>\n\t\t\t\t <a:off x=\"0\" y=\"0\"/>\n\t\t\t\t <a:ext cx=\"1905000\" cy=\"1905000\"/>\n\t\t\t\t</a:xfrm>\n\t\t\t\t<a:prstGeom prst=\"rect\">\n\t\t\t\t <a:avLst/>\n\t\t\t\t</a:prstGeom>\n\t\t\t </pic:spPr>\n\t\t\t</pic:pic>\n\t\t </a:graphicData>\n\t\t </a:graphic>\n\t\t </wp:inline>\n\t\t </w:drawing>\n\t</w:r>\n</w:p><w:sectPr><w:headerReference w:type=\"default\" r:id=\"hId0\"/><w:type w:val=\"continuous\"/><w:pgSz w:w=\"12240\" w:h=\"15840\" w:orient=\"portrait\"/><w:pgMar w:top=\"2810\" w:left=\"1800\" w:right=\"1800\" w:bottom=\"1440\"/><w:cols w:num=\"1\" w:sep=\"off\" w:equalWidth=\"1\"/></w:sectPr></w:body></w:document>""")
fs.writeFile("test_center.docx",zip.generate({type:"nodebuffer"}));
it 'should work with loops',()->
name='imageLoopExample.docx'
imageModule=new ImageModule({centered:false})
docX[name].attachModule(imageModule)
out=docX[name]
.load(docX[name].loadedContent)
.setData({images:['examples/image.png','examples/image2.png']})
out
.render()
zip=out.getZip()
imageFile=zip.files['word/media/image_generated_1.png']
expect(imageFile?).to.equal(true)
expect(imageFile.asText().length).to.equal(17417)
imageFile2=zip.files['word/media/image_generated_2.png']
expect(imageFile2?).to.equal(true)
expect(imageFile2.asText().length).to.equal(7177)
relsFile=zip.files['word/_rels/document.xml.rels']
expect(relsFile?).to.equal(true)
relsFileContent=relsFile.asText()
expect(relsFileContent).to.equal("""<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/><Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering\" Target=\"numbering.xml\"/><Relationship Id=\"rId3\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings\" Target=\"settings.xml\"/><Relationship Id=\"rId4\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes\" Target=\"footnotes.xml\"/><Relationship Id=\"rId5\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes\" Target=\"endnotes.xml\"/><Relationship Id=\"hId0\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header\" Target=\"header0.xml\"/><Relationship Id=\"rId6\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\" Target=\"media/image_generated_1.png\"/><Relationship Id=\"rId7\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\" Target=\"media/image_generated_2.png\"/></Relationships>""")
documentFile=zip.files['word/document.xml']
expect(documentFile?).to.equal(true)
documentContent=documentFile.asText()
expect(documentContent).to.equal("""<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><w:document xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:wx=\"http://schemas.microsoft.com/office/word/2003/auxHint\" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"><w:body><w:p><w:pPr></w:pPr><w:r><w:rPr></w:rPr><w:t xml:space=\"preserve\"></w:t></w:r></w:p><w:p><w:pPr></w:pPr></w:p><w:p><w:pPr></w:pPr><w:r><w:rPr></w:rPr><w:drawing>\n <wp:inline distT=\"0\" distB=\"0\" distL=\"0\" distR=\"0\">\n <wp:extent cx=\"1905000\" cy=\"1905000\"/>\n <wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>\n <wp:docPr id=\"2\" name=\"Image 2\" descr=\"description\"/>\n <wp:cNvGraphicFramePr>\n <a:graphicFrameLocks xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" noChangeAspect=\"1\"/>\n </wp:cNvGraphicFramePr>\n <a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">\n <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">\n <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">\n <pic:nvPicPr>\n <pic:cNvPr id=\"0\" name=\"Picture 1\" descr=\"description\"/>\n <pic:cNvPicPr>\n <a:picLocks noChangeAspect=\"1\" noChangeArrowheads=\"1\"/>\n </pic:cNvPicPr>\n </pic:nvPicPr>\n <pic:blipFill>\n <a:blip r:embed=\"rId6\">\n <a:extLst>\n <a:ext uri=\"{28A0092B-C50C-407E-A947-70E740481C1C}\">\n <a14:useLocalDpi xmlns:a14=\"http://schemas.microsoft.com/office/drawing/2010/main\" val=\"0\"/>\n </a:ext>\n </a:extLst>\n </a:blip>\n <a:srcRect/>\n <a:stretch>\n <a:fillRect/>\n </a:stretch>\n </pic:blipFill>\n <pic:spPr bwMode=\"auto\">\n <a:xfrm>\n <a:off x=\"0\" y=\"0\"/>\n <a:ext cx=\"1905000\" cy=\"1905000\"/>\n </a:xfrm>\n <a:prstGeom prst=\"rect\">\n <a:avLst/>\n </a:prstGeom>\n <a:noFill/>\n <a:ln>\n <a:noFill/>\n </a:ln>\n </pic:spPr>\n </pic:pic>\n </a:graphicData>\n </a:graphic>\n </wp:inline>\n</w:drawing></w:r></w:p><w:p><w:pPr></w:pPr></w:p><w:p><w:pPr></w:pPr><w:r><w:rPr></w:rPr><w:t xml:space=\"preserve\"></w:t></w:r></w:p><w:p><w:pPr></w:pPr></w:p><w:p><w:pPr></w:pPr><w:r><w:rPr></w:rPr><w:drawing>\n <wp:inline distT=\"0\" distB=\"0\" distL=\"0\" distR=\"0\">\n <wp:extent cx=\"1905000\" cy=\"1905000\"/>\n <wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>\n <wp:docPr id=\"2\" name=\"Image 2\" descr=\"description\"/>\n <wp:cNvGraphicFramePr>\n <a:graphicFrameLocks xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" noChangeAspect=\"1\"/>\n </wp:cNvGraphicFramePr>\n <a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">\n <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">\n <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">\n <pic:nvPicPr>\n <pic:cNvPr id=\"0\" name=\"Picture 1\" descr=\"description\"/>\n <pic:cNvPicPr>\n <a:picLocks noChangeAspect=\"1\" noChangeArrowheads=\"1\"/>\n </pic:cNvPicPr>\n </pic:nvPicPr>\n <pic:blipFill>\n <a:blip r:embed=\"rId7\">\n <a:extLst>\n <a:ext uri=\"{28A0092B-C50C-407E-A947-70E740481C1C}\">\n <a14:useLocalDpi xmlns:a14=\"http://schemas.microsoft.com/office/drawing/2010/main\" val=\"0\"/>\n </a:ext>\n </a:extLst>\n </a:blip>\n <a:srcRect/>\n <a:stretch>\n <a:fillRect/>\n </a:stretch>\n </pic:blipFill>\n <pic:spPr bwMode=\"auto\">\n <a:xfrm>\n <a:off x=\"0\" y=\"0\"/>\n <a:ext cx=\"1905000\" cy=\"1905000\"/>\n </a:xfrm>\n <a:prstGeom prst=\"rect\">\n <a:avLst/>\n </a:prstGeom>\n <a:noFill/>\n <a:ln>\n <a:noFill/>\n </a:ln>\n </pic:spPr>\n </pic:pic>\n </a:graphicData>\n </a:graphic>\n </wp:inline>\n</w:drawing></w:r></w:p><w:p><w:pPr></w:pPr></w:p><w:p><w:pPr></w:pPr><w:r><w:rPr></w:rPr><w:t xml:space=\"preserve\"></w:t></w:r><w:bookmarkStart w:id=\"20\" w:name=\"_GoBack\"/><w:bookmarkEnd w:id=\"20\"/></w:p><w:sectPr><w:headerReference w:type=\"default\" r:id=\"hId0\"/><w:type w:val=\"continuous\"/><w:pgSz w:w=\"12240\" w:h=\"15840\" w:orient=\"portrait\"/><w:pgMar w:top=\"2810\" w:left=\"1800\" w:right=\"1800\" w:bottom=\"1440\"/><w:cols w:num=\"1\" w:sep=\"off\" w:equalWidth=\"1\"/></w:sectPr></w:body></w:document>""")
buffer=zip.generate({type:"nodebuffer"})
fs.writeFile("test_multi.docx",buffer);
DocUtils=require('./docUtils')
XmlUtil={}
XmlUtil.getListXmlElements= (text,start=0,end=text.length-1) ->
###
get the different closing and opening tags between two texts (doesn't take into account tags that are opened then closed (those that are closed then opened are returned)):
returns:[{"tag":"</w:r>","offset":13},{"tag":"</w:p>","offset":265},{"tag":"</w:tc>","offset":271},{"tag":"<w:tc>","offset":828},{"tag":"<w:p>","offset":883},{"tag":"<w:r>","offset":1483}]
###
tags= DocUtils.preg_match_all("<(\/?[^/> ]+)([^>]*)>",text.substr(start,end)) #getThemAll (the opening and closing tags)!
result=[]
for tag,i in tags
if tag[1][0]=='/' #closing tag
justOpened= false
if result.length>0
lastTag= result[result.length-1]
innerLastTag= lastTag.tag.substr(1,lastTag.tag.length-2)
innerCurrentTag= tag[1].substr(1)
if innerLastTag==innerCurrentTag then justOpened= true #tag was just opened
if justOpened then result.pop() else result.push {tag:'<'+tag[1]+'>',offset:tag.offset}
else if tag[2][tag[2].length-1]=='/' #open/closing tag aren't taken into account(for example <w:style/>)
else #opening tag
result.push {tag:'<'+tag[1]+'>',offset:tag.offset}
result
XmlUtil.getListDifferenceXmlElements= (text,start=0,end=text.length-1) -> #it returns the difference between two scopes, ie simplifyes closes and opens. If it is not null, it means that the beginning is for example in a table, and the second one is not. If you hard copy this text, the XML will break
scope= @getListXmlElements text,start,end
while(1)
if (scope.length<=1) #if scope.length==1, then they can't be an opeining and closing tag
break;
if ((scope[0]).tag.substr(2)==(scope[scope.length-1]).tag.substr(1)) #if the first closing is the same than the last opening, ie: [</tag>,...,<tag>]
scope.pop() #remove both the first and the last one
scope.shift()
else break;
scope
module.exports=XmlUtil
var gulp = require('gulp');
var gutil = require('gulp-util');
var watch= require('gulp-watch');
var rename= require('gulp-rename');
var coffee= require('gulp-coffee');
var concat= require('gulp-concat');
var uglify= require('gulp-uglify');
var spawn = require('child_process').spawn;
// var livereload = require('gulp-livereload');
// var browserify = require('gulp-browserify');
var server=null;
var config={uglify:false}
var paths = {
coffee: ['coffee/docxQrCode.coffee','coffee/xmlUtil.coffee','coffee/docUtils.coffee','coffee/imgManager.coffee','coffee/imgReplacer.coffee','coffee/index.coffee'],
coffeeTest: ['coffee/test.coffee'],
testDirectory:__dirname+'/test',
js:'js/'
};
// gulp.task('browserify', function() {
// browserified=gulp.src(__dirname+'/test.test.js')
// .pipe(browserify({}))
// browserified
// .pipe(gulp.dest(__dirname+'/browser/'))
// // Single entry point to browserify
// browserified=gulp.src(__dirname+'/examples/main.js')
// .pipe(browserify({}))
// browserified
// .pipe(uglify())
// .pipe(rename('main.min.js'))
// .pipe(gulp.dest(__dirname+'/browser'))
// browserified
// .pipe(gulp.dest(__dirname+'/browser/'))
// });
gulp.task('allCoffee', function () {
gulp.src(paths.coffee)
.pipe(coffee({bare:true}))
.pipe(gulp.dest(paths.js))
a=gulp.src(paths.coffeeTest)
.pipe(coffee({map:true}))
if(config.uglify)
a=a.pipe(uglify())
a=a
.pipe(gulp.dest(paths.testDirectory));
});
gulp.task('watch', function () {
gulp.src(paths.coffee)
.pipe(watch(function(files) {
var f=files.pipe(coffee({bare:true}))
.pipe(gulp.dest(paths.js))
// gulp.run('browserify');
// gulp.run('jasmine');
// gulp.run('livereload');
return f;
}));
gulp.watch(paths.coffeeTest,['coffeeTest']);
});
gulp.task('coffeeTest', function() {
a=gulp.src(paths.coffeeTest)
.pipe(coffee({map:true}))
if(config.uglify)
a=a.pipe(uglify())
a=a
.pipe(gulp.dest(paths.testDirectory));
// gulp.run('livereload');
// gulp.run('jasmine');
return a;
});
gulp.task('default',['coffeeTest','watch']);
{
"name": "docxtemplater-image-module",
"version": "0.1.0",
"description": "Image Module for docxtemplater v1.0",
"main": "js/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"chai": "^1.10.0",
"gulp": "~3.8.0",
"gulp-coffee": "~2.0.1",
"gulp-concat": "~2.2.0",
"gulp-jasmine": "^0.2.0",
"gulp-livereload": "~2.1.0",
"gulp-notify": "^1.2.5",
"gulp-rename": "^1.2.0",
"gulp-uglify": "~0.3.0",
"gulp-util": "~2.2.14",
"gulp-watch": "~0.6.5"
},
"author": "Edgar Hipp",
"license": "MIT",
"dependencies": {
"docxtemplater": "^1.0.0-beta.2",
"gulp-browserify": "^0.5.0",
"png-js": "^0.1.1",
"qrcode-reader": "0.0.5",
"xmldom": "^0.1.19"
}
}
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