html-to-pdfmake
Advanced tools
Comparing version 1.3.6 to 2.0.0
@@ -1,1 +0,1 @@ | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.htmlToPdfmake=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r}()({1:[function(require,module,exports){module.exports=function(htmlText,options){var wndw=options&&options.window?options.window:window;var defaultStyles={b:{bold:true},strong:{bold:true},u:{decoration:"underline"},s:{decoration:"lineThrough"},em:{italics:true},i:{italics:true},h1:{fontSize:24,bold:true,marginBottom:5},h2:{fontSize:22,bold:true,marginBottom:5},h3:{fontSize:20,bold:true,marginBottom:5},h4:{fontSize:18,bold:true,marginBottom:5},h5:{fontSize:16,bold:true,marginBottom:5},h6:{fontSize:14,bold:true,marginBottom:5},a:{color:"blue",decoration:"underline"},strike:{decoration:"lineThrough"},p:{margin:[0,5,0,10]},ul:{marginBottom:5},li:{marginLeft:5},table:{marginBottom:5},th:{bold:true,fillColor:"#EEEEEE"}};var inlineTags=["p","li","span","strong","em","b","i","u","th","td"];function changeDefaultStyles(){for(var keyStyle in options.defaultStyles){if(defaultStyles.hasOwnProperty(keyStyle)){if(options.defaultStyles.hasOwnProperty(keyStyle)&&!options.defaultStyles[keyStyle]){delete defaultStyles[keyStyle]}else{for(var k in options.defaultStyles[keyStyle]){if(!options.defaultStyles[keyStyle][k])delete defaultStyles[keyStyle][k];else defaultStyles[keyStyle][k]=options.defaultStyles[keyStyle][k]}}}}}if(options&&options.defaultStyles){changeDefaultStyles()}var convertHtml=function(htmlText){var docDef=[];var parser=new wndw.DOMParser;var parsedHtml=parser.parseFromString(htmlText,"text/html");[].forEach.call(parsedHtml.body.childNodes,function(child){var ret=parseElement(child);if(ret){if(Array.isArray(ret)&&ret.length===1)ret=ret[0];docDef.push(ret)}});return docDef};var parseElement=function(element,parentNode,parents){var nodeName=element.nodeName.toLowerCase();var parentNodeName=parentNode?parentNode.nodeName.toLowerCase():"";var ret,text,cssClass,dataset,key,dist,isInlineTag,hasStackTags;parents=parents||[];switch(element.nodeType){case 3:{if(element.textContent){text=element.textContent.replace(/\n(\s+)?/g,"");if(text){if(/^\s+$/.test(text)&&["table","thead","tbody","tr"].indexOf(parentNodeName)>-1)return ret;ret={text:text};if(parentNodeName){applyParentsStyle(ret,element);if(parentNodeName==="a"){ret.link=parentNode.getAttribute("href")}}else{ret=text}}}return ret}case 1:{ret=[];parents.push(nodeName);if(element.childNodes.length===0&&(parents.indexOf("th")>-1||parents.indexOf("td")>-1)){var ancestor=element;var allEmpty=true;while(ancestor.nodeName!=="TH"&&ancestor.nodeName!=="TD"){if(ancestor.textContent!==""){allEmpty=false;break}ancestor=ancestor.parentNode}if(allEmpty)ret.push({text:""})}else{[].forEach.call(element.childNodes,function(child){child=parseElement(child,element,parents);if(child){if(Array.isArray(child)&&child.length===1)child=child[0];ret.push(child)}});parents.pop()}if(ret.length===0)ret="";switch(nodeName){case"svg":{ret={svg:element.outerHTML};ret.style=["html-"+nodeName];break}case"br":{ret="\n";break}case"hr":{var styleHR={width:514,type:"line",margin:[0,12,0,12],thickness:.5,color:"#000000",left:0};if(element.dataset&&element.dataset.pdfmake){dataset=JSON.parse(element.dataset.pdfmake);for(key in dataset){styleHR[key]=dataset[key]}}ret={margin:styleHR.margin,canvas:[{type:styleHR.type,x1:styleHR.left,y1:0,x2:styleHR.width,y2:0,lineWidth:styleHR.thickness,lineColor:styleHR.color}]};break}case"ol":case"ul":{ret={_:ret};ret[nodeName]=ret._;delete ret._;ret.style=["html-"+nodeName];cssClass=element.getAttribute("class");if(cssClass){ret.style=ret.style.concat(cssClass.split(" "))}setComputedStyle(ret,element);break}case"table":{ret={_:ret,table:{body:[]}};ret._.forEach(function(re){if(re.stack){var td=[],rowspan={};re.stack.forEach(function(r,indexRow){var c,cell,i,indexCell;if(r.stack){if(rowspan[indexRow]){rowspan[indexRow].forEach(function(cell){r.stack.splice(cell.index,0,{text:"",style:["html-td","html-tr"],colSpan:cell.colspan})})}for(c=0,cell;c<r.stack.length;){cell=r.stack[c];if(cell.colSpan>1){for(i=0;i<cell.colSpan-1;i++){r.stack.splice(c+1,0,"")}c+=cell.colSpan}else c++}indexCell=0;r.stack.forEach(function(cell){if(cell.rowSpan){for(var i=0;i<cell.rowSpan;i++){if(!rowspan[indexRow+i])rowspan[indexRow+i]=[];rowspan[indexRow+i].push({index:indexCell,colspan:cell.colSpan||1})}}indexCell+=cell.colSpan||1});ret.table.body.push(r.stack)}else{td.push(r);if(r.colSpan>1){for(i=0;i<r.colSpan-1;i++){td.push("")}}}});if(td.length>0)ret.table.body.push(td)}else{ret.table.body.push([re])}});delete ret._;setComputedStyle(ret,element);var insertBreakLine=function(content,prevContent,firstStyleOnly){var breakLine=false,i;if(prevContent&&Array.isArray(content.style)){for(i=0;i<content.style.length;i++){if(firstStyleOnly&&i>0)break;if(content.style[i]==="html-p"||content.style[i]==="html-div"){breakLine=true;break}else if(content.style[i]==="html-td")break}if(breakLine){if(typeof prevContent.text==="string"){prevContent.text+="\n"}else if(Array.isArray(prevContent.text)){prevContent.text.push({text:"\n"})}}else if(!firstStyleOnly&&Array.isArray(prevContent.style)){for(i=0;i<prevContent.style.length;i++){if(prevContent.style[i]==="html-p"||prevContent.style[i]==="html-div"){breakLine=true;break}else if(prevContent.style[i]==="html-td")break}if(breakLine){if(typeof prevContent.text==="string"){prevContent.text+="\n"}else if(Array.isArray(prevContent.text)){prevContent.text.push({text:"\n"})}}}}if(Array.isArray(content.text)){for(i=0;i<content.text.length;i++){insertBreakLine(content.text[i],content.text[i-1],true)}}};ret.table.body.forEach(function(row){row.forEach(function(cell){if(Array.isArray(cell.text)){for(var i=0;i<cell.text.length;i++){insertBreakLine(cell.text[i],cell.text[i-1])}}})});break}case"img":{ret={image:element.getAttribute("src")};ret.style=["html-img"];cssClass=element.getAttribute("class");if(cssClass){ret.style=ret.style.concat(cssClass.split(" "))}if(element.getAttribute("width")){ret.width=parseFloat(element.getAttribute("width"))}if(element.getAttribute("height")){ret.height=parseFloat(element.getAttribute("height"))}setComputedStyle(ret,element);break}case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":{ret={stack:[{text:ret}]};ret.stack[0].style=["html-"+nodeName];cssClass=element.getAttribute("class");if(cssClass){ret.stack[0].style=ret.stack[0].style.concat(cssClass.split(" "))}applyDefaultStyle(ret.stack[0],nodeName);setComputedStyle(ret.stack[0],element);break}}if(ret){if(Array.isArray(ret)){if(ret.length===1&&nodeName!=="tr"){ret=ret[0];if(typeof ret==="string")ret={text:ret};if(ret.text){applyDefaultStyle(ret,nodeName);setComputedStyle(ret,element)}ret.style=(ret.style||[]).concat(["html-"+nodeName]);if(nodeName==="td"||nodeName==="th")ret.style.push("html-tr")}else{isInlineTag=inlineTags.indexOf(nodeName)>-1;hasStackTags=/{"(stack|table|ol|ul|image)"/.test(JSON.stringify(ret));if(nodeName==="div"&&!hasStackTags)ret={text:ret};else if(isInlineTag&&hasStackTags)ret={stack:ret};else if(!isInlineTag)ret={stack:ret};else ret={text:ret};if(isInlineTag){applyDefaultStyle(ret,nodeName)}ret.style=["html-"+nodeName]}applyParentsStyle(ret,element);if(nodeName==="td"||nodeName==="th"){if(element.getAttribute("rowspan"))ret.rowSpan=element.getAttribute("rowspan")*1;if(element.getAttribute("colspan"))ret.colSpan=element.getAttribute("colspan")*1}cssClass=element.getAttribute("class");if(cssClass){ret.style=(ret.style||[]).concat(cssClass.split(" "))}if(ret.text){setComputedStyle(ret,element)}}else if(ret.table||ret.ol||ret.ul){ret.style=["html-"+nodeName];cssClass=element.getAttribute("class");if(cssClass){ret.style=ret.style.concat(cssClass.split(" "))}applyDefaultStyle(ret,nodeName)}if(element.dataset&&element.dataset.pdfmake){dataset=JSON.parse(element.dataset.pdfmake);dist=ret[nodeName]||ret;for(key in dataset){dist[key]=dataset[key]}}cssClass=element.getAttribute("class");if(cssClass&&typeof ret==="object"){ret.style=(ret.style||[]).concat(cssClass.split(" "))}if(typeof ret==="object"&&Array.isArray(ret.style)){ret.style=ret.style.filter(function(value,index,self){return self.indexOf(value)===index})}}return ret}}return""};var applyDefaultStyle=function(ret,nodeName){if(defaultStyles[nodeName]){for(var style in defaultStyles[nodeName]){if(defaultStyles[nodeName].hasOwnProperty(style)){ret[style]=defaultStyles[nodeName][style]}}}};var applyParentsStyle=function(ret,node){var classes=[],defaultStyles=[],cssClass,cssStyles=[];var parentNode=node.parentNode;var inlineParentNode=node.parentNode;while(inlineParentNode){var defaultStyle={};var inlineParentNodeName=inlineParentNode.nodeName.toLowerCase();if(inlineTags.indexOf(inlineParentNodeName)>-1){cssClass=inlineParentNode.getAttribute("class");classes=classes.concat(["html-"+inlineParentNodeName],cssClass||[]);applyDefaultStyle(defaultStyle,inlineParentNodeName);defaultStyles.push(defaultStyle);inlineParentNode=inlineParentNode.parentNode}else break}ret.style=(ret.style||[]).concat(classes);defaultStyles.forEach(function(defaultStyle){for(var key in defaultStyle){if(key.indexOf("margin")===-1&&ret[key]===undefined)ret[key]=defaultStyle[key]}});while(parentNode.nodeType===1){cssStyles=cssStyles.concat(computeStyle(parentNode.getAttribute("style"),parentNode));parentNode=parentNode.parentNode}cssStyles.reverse();cssStyles.forEach(function(stl){ret[stl.key]=stl.value});if(ret.style.length===0)delete ret.style};var computeStyle=function(style,element){if(!style)return[];var styleDefs=style.split(";").map(function(style){return style.toLowerCase().split(":")});var ret=[];var borders=[];var nodeName=element.nodeName.toUpperCase();styleDefs.forEach(function(styleDef){if(styleDef.length===2){var key=styleDef[0].trim();var value=styleDef[1].trim();switch(key){case"margin":{value=value.replace(/(\d+)(\.\d+)?([^\d]+)/g,"$1$2 ").trim().split(" ");if(value.length===1)value=+value[0];else if(value.length===2)value=[+value[1],+value[0]];else if(value.length===3)value=[+value[1],+value[0],+value[1],+value[2]];else if(value.length===4)value=[+value[3],+value[0],+value[1],+value[2]];ret.push({key:key,value:value});break}case"text-align":{ret.push({key:"alignment",value:value});break}case"font-weight":{if(value==="bold")ret.push({key:"bold",value:true});break}case"text-decoration":{ret.push({key:"decoration",value:toCamelCase(value)});break}case"font-style":{if(value==="italic")ret.push({key:"italics",value:true});break}case"font-family":{ret.push({key:"font",value:value.replace(/"|^'|'$/g,"")});break}case"color":{ret.push({key:"color",value:parseColor(value)});break}case"background-color":{ret.push({key:nodeName==="TD"||nodeName==="TH"?"fillColor":"background",value:parseColor(value)});break}default:{if(key==="border"||key.indexOf("border-left")===0||key.indexOf("border-top")===0||key.indexOf("border-right")===0||key.indexOf("border-bottom")===0){borders.push({key:key,value:value})}else{if(key.indexOf("-")>-1)key=toCamelCase(key);if(value){value=value.replace(/(\d+)(\.\d+)?([^\d]+)/g,"$1$2 ").trim();if(!isNaN(value))value=+value;ret.push({key:key,value:value})}}}}}});if(borders.length>0){var border=[];var borderColor=[];borders.forEach(function(b){var properties=b.value.split(" ");var width=properties[0].replace(/(\d+)(\.\d+)?([^\d]+)/g,"$1$2 ").trim();var index=-1,i;if(b.key.indexOf("-left")>-1)index=0;else if(b.key.indexOf("-top")>-1)index=1;else if(b.key.indexOf("-right")>-1)index=2;else if(b.key.indexOf("-bottom")>-1)index=3;if(index>-1){border[index]=width>0}else{for(i=0;i<4;i++)border[i]=width>0}if(properties.length>2){var color=properties.slice(2).join(" ");if(index>-1){borderColor[index]=parseColor(color)}else{for(i=0;i<4;i++)borderColor[i]=parseColor(color)}}});for(var i=0;i<4;i++){if(border.length>0&&typeof border[i]==="undefined")border[i]=true;if(borderColor.length>0&&typeof borderColor[i]==="undefined")borderColor[i]="#000000"}if(border.length>0)ret.push({key:"border",value:border});if(borderColor.length>0)ret.push({key:"borderColor",value:borderColor})}return ret};var setComputedStyle=function(ret,element){var cssStyle=element.getAttribute("style");if(cssStyle){cssStyle=computeStyle(cssStyle,element);cssStyle.forEach(function(style){ret[style.key]=style.value})}};var toCamelCase=function(str){return str.replace(/-([a-z])/g,function(g){return g[1].toUpperCase()})};var parseColor=function(color){var haxRegex=new RegExp("^#([0-9a-f]{3}|[0-9a-f]{6})$");var rgbRegex=new RegExp("^rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)$");var nameRegex=new RegExp("^[a-z]+$");if(haxRegex.test(color)){return color}else if(rgbRegex.test(color)){var decimalColors=rgbRegex.exec(color).slice(1);for(var i=0;i<3;i++){var decimalValue=+decimalColors[i];if(decimalValue>255){decimalValue=255}var hexString="0"+decimalValue.toString(16);hexString=hexString.slice(-2);decimalColors[i]=hexString}return"#"+decimalColors.join("")}else if(nameRegex.test(color)){return color}else{console.error('Could not parse color "'+color+'"');return color}};return convertHtml(htmlText)}},{}]},{},[1])(1)}); | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.htmlToPdfmake=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r}()({1:[function(require,module,exports){module.exports=function(htmlText,options){var wndw=options&&options.window?options.window:window;var tableAutoSize=options&&typeof options.tableAutoSize==="boolean"?options.tableAutoSize:false;var defaultStyles={b:{bold:true},strong:{bold:true},u:{decoration:"underline"},s:{decoration:"lineThrough"},em:{italics:true},i:{italics:true},h1:{fontSize:24,bold:true,marginBottom:5},h2:{fontSize:22,bold:true,marginBottom:5},h3:{fontSize:20,bold:true,marginBottom:5},h4:{fontSize:18,bold:true,marginBottom:5},h5:{fontSize:16,bold:true,marginBottom:5},h6:{fontSize:14,bold:true,marginBottom:5},a:{color:"blue",decoration:"underline"},strike:{decoration:"lineThrough"},p:{margin:[0,5,0,10]},ul:{marginBottom:5,marginLeft:5},table:{marginBottom:5},th:{bold:true,fillColor:"#EEEEEE"}};function changeDefaultStyles(){for(var keyStyle in options.defaultStyles){if(defaultStyles.hasOwnProperty(keyStyle)){if(options.defaultStyles.hasOwnProperty(keyStyle)&&!options.defaultStyles[keyStyle]){delete defaultStyles[keyStyle]}else{for(var k in options.defaultStyles[keyStyle]){if(!options.defaultStyles[keyStyle][k])delete defaultStyles[keyStyle][k];else defaultStyles[keyStyle][k]=options.defaultStyles[keyStyle][k]}}}}}if(options&&options.defaultStyles){changeDefaultStyles()}var convertHtml=function(htmlText){var parser=new wndw.DOMParser;var parsedHtml=parser.parseFromString(htmlText,"text/html");var docDef=parseElement(parsedHtml.body,[]);return docDef.stack||docDef.text};var parseElement=function(element,parents){var nodeName=element.nodeName.toUpperCase();var nodeNameLowerCase=nodeName.toLowerCase();var ret={text:[]};var text,needStack=false;var dataset,i,key;if(["COLGROUP","COL"].indexOf(nodeName)>-1)return"";switch(element.nodeType){case 3:{if(element.textContent){text=element.textContent.replace(/\n(\s+)?/g,"");if(text){ret={text:text};ret=applyStyle({ret:ret,parents:parents});return ret}}return""}case 1:{ret.nodeName=nodeName;parents.push(element);if(element.childNodes&&element.childNodes.length>0){[].forEach.call(element.childNodes,function(child){var res=parseElement(child,parents);if(res){if(Array.isArray(res.text)&&res.text.length===0)res.text="";ret.text.push(res)}});needStack=searchForStack(ret);if(needStack){ret.stack=ret.text.slice(0);delete ret.text}else{ret=applyStyle({ret:ret,parents:parents})}}parents.pop();switch(nodeName){case"TABLE":{var rowIndex,cellIndex;ret.table={body:[]};rowIndex=0;(ret.stack||ret.text).forEach(function(tbody){var rows=tbody.stack||tbody.text;rows.forEach(function(row){ret.table.body[rowIndex]=[];var cells=row.stack||row.text;cellIndex=0;cells.forEach(function(cell){ret.table.body[rowIndex].push(cell);if(cell.colSpan){i=cell.colSpan;setRowSpan({rows:rows,cell:cell,rowIndex:rowIndex,cellIndex:cellIndex});while(--i>0){ret.table.body[rowIndex].push({text:""});setRowSpan({rows:rows,cell:cell,rowIndex:rowIndex,cellIndex:cellIndex});cellIndex++}}else{setRowSpan({rows:rows,cell:cell,rowIndex:rowIndex,cellIndex:cellIndex})}cellIndex++});rowIndex++})});delete ret.stack;delete ret.text;ret=applyStyle({ret:ret,parents:parents.concat([element])});if(tableAutoSize){var cellsWidths=[];var cellsHeights=[];var tableWidths=[];var tableHeights=[];ret.table.body.forEach(function(row,rowIndex){cellsWidths.push([]);cellsHeights.push([]);row.forEach(function(cell){var width=typeof cell.width!=="undefined"?cell.width:"auto";var height=typeof cell.height!=="undefined"?cell.height:"auto";if(width!=="auto"&&cell.colSpan)width/=cell.colSpan;if(height!=="auto"&&cell.rowSpan)height/=cell.colSpan;cellsWidths[rowIndex].push(width);cellsHeights[rowIndex].push(height)})});cellsWidths.forEach(function(row){row.forEach(function(cellWidth,cellIndex){var type=typeof tableWidths[cellIndex];if(type==="undefined"||cellWidth!=="auto"&&type==="number"&&cellWidth>tableWidths[cellIndex]||cellWidth!=="auto"&&tableWidths[cellIndex]==="auto"){tableWidths[cellIndex]=cellWidth}})});cellsHeights.forEach(function(row,rowIndex){row.forEach(function(cellHeight){var type=typeof tableHeights[rowIndex];if(type==="undefined"||cellHeight!=="auto"&&type==="number"&&cellHeight>tableHeights[rowIndex]||cellHeight!=="auto"&&tableHeights[rowIndex]==="auto"){tableHeights[rowIndex]=cellHeight}})});if(tableWidths.length>0)ret.table.widths=tableWidths;if(tableHeights.length>0)ret.table.heights=tableHeights}if(element.dataset&&element.dataset.pdfmake){dataset=JSON.parse(element.dataset.pdfmake);for(key in dataset){ret.table[key]=dataset[key]}}break}case"TH":case"TD":{if(element.getAttribute("rowspan"))ret.rowSpan=element.getAttribute("rowspan")*1;if(element.getAttribute("colspan"))ret.colSpan=element.getAttribute("colspan")*1;ret=applyStyle({ret:ret,parents:parents.concat([element])});break}case"SVG":{ret.svg=element.outerHTML.replace(/\n(\s+)?/g,"");delete ret.stack;delete ret.text;ret=applyStyle({ret:ret,parents:parents.concat([element])});break}case"BR":{ret.text=[{text:"\n"}];break}case"HR":{var styleHR={width:514,type:"line",margin:[0,12,0,12],thickness:.5,color:"#000000",left:0};if(element.dataset&&element.dataset.pdfmake){dataset=JSON.parse(element.dataset.pdfmake);for(key in dataset){styleHR[key]=dataset[key]}}ret.margin=styleHR.margin;ret.canvas=[{type:styleHR.type,x1:styleHR.left,y1:0,x2:styleHR.width,y2:0,lineWidth:styleHR.thickness,lineColor:styleHR.color}];delete ret.text;break}case"OL":case"UL":{ret[nodeNameLowerCase]=(ret.stack||ret.text).slice(0);delete ret.stack;delete ret.text;ret=applyStyle({ret:ret,parents:parents.concat([element])});break}case"IMG":{ret.image=element.getAttribute("src");delete ret.stack;delete ret.text;ret=applyStyle({ret:ret,parents:parents.concat([element])});break}case"A":{ret.link=element.getAttribute("href");break}}if(Array.isArray(ret.text)&&ret.text.length===1&&ret.text[0].text&&!ret.text[0].nodeName){ret.text=ret.text[0].text}if(nodeName!=="HR"&&nodeName!=="TABLE"&&element.dataset&&element.dataset.pdfmake){dataset=JSON.parse(element.dataset.pdfmake);for(key in dataset){ret[key]=dataset[key]}}return ret}}};var searchForStack=function(ret){if(Array.isArray(ret.text)){for(var i=0;i<ret.text.length;i++){if(ret.text[i].stack||["P","DIV","TABLE","SVG","UL","OL","IMG","H1","H2","H3","H4","H5","H6"].indexOf(ret.text[i].nodeName)>-1)return true;if(searchForStack(ret.text[i])===true)return true}}return false};var setRowSpan=function(params){var cells;if(params.cell.rowSpan){for(var i=1;i<=params.cell.rowSpan-1;i++){cells=params.rows[params.rowIndex+i].text||params.rows[params.rowIndex+i].stack;cells.splice(params.cellIndex,0,{text:""})}}};var applyStyle=function(params){var cssClass=[];var lastIndex=params.parents.length-1;params.parents.forEach(function(parent,parentIndex){var parentNodeName=parent.nodeName.toLowerCase();var htmlClass="html-"+parentNodeName;if(htmlClass!=="html-body"&&cssClass.indexOf(htmlClass)===-1)cssClass.unshift(htmlClass);var parentClass=(parent.getAttribute("class")||"").split(" ");parentClass.forEach(function(p){if(p)cssClass.push(p)});var style;var ignoreNonDescendentProperties=parentIndex!==lastIndex;if(defaultStyles[parentNodeName]){for(style in defaultStyles[parentNodeName]){if(defaultStyles[parentNodeName].hasOwnProperty(style)){if(!ignoreNonDescendentProperties||ignoreNonDescendentProperties&&style.indexOf("margin")===-1&&style.indexOf("border")===-1)params.ret[style]=defaultStyles[parentNodeName][style]}}}if(parentNodeName==="tr")ignoreNonDescendentProperties=false;style=parseStyle(parent,ignoreNonDescendentProperties);style.forEach(function(stl){params.ret[stl.key]=stl.value})});params.ret.style=cssClass;return params.ret};var parseStyle=function(element,ignoreProperties){var style=element.getAttribute("style");if(!style)return[];style=style.split(";");if(element.getAttribute("width")){style.unshift("width:"+element.getAttribute("width")+"px")}if(element.getAttribute("height")){style.unshift("height:"+element.getAttribute("height")+"px")}var styleDefs=style.map(function(style){return style.toLowerCase().split(":")});var ret=[];var borders=[];var nodeName=element.nodeName.toUpperCase();styleDefs.forEach(function(styleDef){if(styleDef.length===2){var key=styleDef[0].trim();var value=styleDef[1].trim();switch(key){case"margin":{if(ignoreProperties)break;value=value.split(" ");if(value.length===1)value=[value[0],value[0],value[0],value[0]];else if(value.length===2)value=[value[1],value[0]];else if(value.length===3)value=[value[1],value[0],value[1],value[2]];else if(value.length===4)value=[value[3],value[0],value[1],value[2]];value.forEach(function(val,i){value[i]=convertToUnit(val)});if(value.indexOf(false)===-1)ret.push({key:key,value:value});break}case"text-align":{ret.push({key:"alignment",value:value});break}case"font-weight":{if(value==="bold")ret.push({key:"bold",value:true});break}case"text-decoration":{ret.push({key:"decoration",value:toCamelCase(value)});break}case"font-style":{if(value==="italic")ret.push({key:"italics",value:true});break}case"font-family":{ret.push({key:"font",value:value.replace(/"|^'|'$/g,"")});break}case"color":{ret.push({key:"color",value:parseColor(value)});break}case"background-color":{ret.push({key:nodeName==="TD"||nodeName==="TH"?"fillColor":"background",value:parseColor(value)});break}default:{if(key==="border"||key.indexOf("border-left")===0||key.indexOf("border-top")===0||key.indexOf("border-right")===0||key.indexOf("border-bottom")===0){if(!ignoreProperties)borders.push({key:key,value:value})}else{if(ignoreProperties&&(key.indexOf("margin-")===0||key==="width"||key==="height"))break;if(key.indexOf("padding")===0)break;if(key.indexOf("-")>-1)key=toCamelCase(key);if(value){var parsedValue=convertToUnit(value);ret.push({key:key,value:parsedValue===false?value:parsedValue})}}}}}});if(borders.length>0){var border=[];var borderColor=[];borders.forEach(function(b){var properties=b.value.split(" ");var width=properties[0].replace(/(\d+)(\.\d+)?([^\d]+)/g,"$1$2 ").trim();var index=-1,i;if(b.key.indexOf("-left")>-1)index=0;else if(b.key.indexOf("-top")>-1)index=1;else if(b.key.indexOf("-right")>-1)index=2;else if(b.key.indexOf("-bottom")>-1)index=3;if(index>-1){border[index]=width>0}else{for(i=0;i<4;i++)border[i]=width>0}if(properties.length>2){var color=properties.slice(2).join(" ");if(index>-1){borderColor[index]=parseColor(color)}else{for(i=0;i<4;i++)borderColor[i]=parseColor(color)}}});for(var i=0;i<4;i++){if(border.length>0&&typeof border[i]==="undefined")border[i]=true;if(borderColor.length>0&&typeof borderColor[i]==="undefined")borderColor[i]="#000000"}if(border.length>0)ret.push({key:"border",value:border});if(borderColor.length>0)ret.push({key:"borderColor",value:borderColor})}return ret};var toCamelCase=function(str){return str.replace(/-([a-z])/g,function(g){return g[1].toUpperCase()})};var parseColor=function(color){var haxRegex=new RegExp("^#([0-9a-f]{3}|[0-9a-f]{6})$");var rgbRegex=new RegExp("^rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)$");var nameRegex=new RegExp("^[a-z]+$");if(haxRegex.test(color)){return color}else if(rgbRegex.test(color)){var decimalColors=rgbRegex.exec(color).slice(1);for(var i=0;i<3;i++){var decimalValue=+decimalColors[i];if(decimalValue>255){decimalValue=255}var hexString="0"+decimalValue.toString(16);hexString=hexString.slice(-2);decimalColors[i]=hexString}return"#"+decimalColors.join("")}else if(nameRegex.test(color)){return color}else{console.error('Could not parse color "'+color+'"');return color}};var convertToUnit=function(val){if(!isNaN(parseFloat(val))&&isFinite(val))return val;var mtch=(val+"").trim().match(/^(\d+(\.\d+)?)(pt|px|rem)$/);if(!mtch)return false;val=mtch[1];switch(mtch[3]){case"px":{val=Math.round(val*.75292857248934);break}case"rem":{val*=12;break}}return val*1};return convertHtml(htmlText)}},{}]},{},[1])(1)}); |
@@ -153,8 +153,8 @@ var pdfMake = require("pdfmake/build/pdfmake"); | ||
<tr> | ||
<td colspan="3">Table with <b>widths=[100,"*","auto"]</b> and <b>heights=40</b></th> | ||
<td colspan="3">Table with <b>widths=[100,"*","auto"]</b> and <b>heights=40</b> using "data-pdfmake" attribute</th> | ||
</tr> | ||
<tr> | ||
<td>Cell1</td> | ||
<td>Cell2</td> | ||
<td>Cell3</td> | ||
<td style="text-align:center">Cell2</td> | ||
<td style="text-align:right">Cell3</td> | ||
</tr> | ||
@@ -171,2 +171,14 @@ </table> | ||
<p>Table autosized based on style "height" and "width" using "tableAutoSize:true" option:</p> | ||
<table> | ||
<tr style="height:100px"> | ||
<td style="width:250px">height:100px / width:250px</td> | ||
<td>height:100px / width:'auto'</td> | ||
</tr> | ||
<tr> | ||
<td style="width:100px">Here "<td width="100">" will use 250px for the width because we have to use the largest col's width</td> | ||
<td style="height:200px">height:200px / width:'auto'</td> | ||
</tr> | ||
</table> | ||
<svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"> | ||
@@ -185,4 +197,12 @@ <rect width="100%" height="100%" fill="red" /> | ||
<span class="a">text "bold" <span class="b">text "bold & italic" <span class="c">text "bold & italic & red"</span> text "bold & italic"</span> text "bold"</span> | ||
`, {window:window}); | ||
<div style="margin-top:20px"> | ||
Below we preserve the spaces: | ||
<p class="with-spaces"> this is just an example.</p> | ||
</div> | ||
`, {window:window, tableAutoSize:true}); | ||
//var html = htmlToPdfMake(``, {window:window, tableAutoSize:true}); | ||
//console.log(JSON.stringify(html)) | ||
var docDefinition = { | ||
@@ -193,4 +213,4 @@ content: [ | ||
pageBreakBefore: function(currentNode) { | ||
// we add a page break before elements with the classname "pdf-pagebreak-before" | ||
return currentNode.style && currentNode.style.indexOf('pdf-pagebreak-before') > -1; | ||
// we add a page break before TABLE with the classname "pdf-pagebreak-before" | ||
return currentNode.table && currentNode.style && currentNode.style.indexOf('pdf-pagebreak-before') > -1; | ||
}, | ||
@@ -201,2 +221,5 @@ styles:{ | ||
}, | ||
blue:{ | ||
color:'blue' | ||
}, | ||
bold:{ | ||
@@ -220,2 +243,5 @@ bold:true | ||
italics: false | ||
}, | ||
'with-spaces':{ | ||
preserveLeadingSpaces: true | ||
} | ||
@@ -222,0 +248,0 @@ } |
722
index.js
@@ -15,3 +15,4 @@ // source: https://github.com/OpenSlides/OpenSlides/blob/f4f8b8422f9b3fbab58e35ac3f8f870d35813b7d/client/src/app/core/ui-services/html-to-pdf.service.ts | ||
* @param {Object} [defaultStyles] An object with the default styles for each elements | ||
* @param {Object} [window] The `window` object (only used for the tests) | ||
* @param {Boolean} [tableAutoSize=false] It permits to use the width/height defined in styles for a table's cells and rows | ||
* @param {Object} [window] The `window` object (required for NodeJS server side use) | ||
* @return {Object} it returns a PdfMake object | ||
@@ -38,2 +39,3 @@ * | ||
var wndw = (options && options.window ? options.window : window); | ||
var tableAutoSize = (options && typeof options.tableAutoSize === "boolean" ? options.tableAutoSize : false); | ||
@@ -57,4 +59,3 @@ // set default styles | ||
p: {margin:[0, 5, 0, 10]}, | ||
ul: {marginBottom:5}, | ||
li: {marginLeft:5}, | ||
ul: {marginBottom:5,marginLeft:5}, | ||
table: {marginBottom:5}, | ||
@@ -64,7 +65,4 @@ th: {bold:true, fillColor:'#EEEEEE'} | ||
var inlineTags = [ 'p', 'li', 'span', 'strong', 'em', 'b', 'i', 'u', 'th', 'td' ]; | ||
/** | ||
* Permit to change the default styles based on the options | ||
* @return {[type]} [description] | ||
*/ | ||
@@ -100,4 +98,2 @@ function changeDefaultStyles () { | ||
var convertHtml = function(htmlText) { | ||
var docDef = []; | ||
// Create a HTML DOM tree out of html string | ||
@@ -107,14 +103,5 @@ var parser = new wndw.DOMParser(); | ||
// Go thru each child | ||
[].forEach.call(parsedHtml.body.childNodes, function(child) { | ||
var ret = parseElement(child); | ||
if (ret) { | ||
// to reduce the amount of code | ||
if (Array.isArray(ret) && ret.length === 1) ret=ret[0]; | ||
//console.log(util.inspect(ret, {showHidden: false, depth: null})); // to debug | ||
docDef.push(ret); | ||
} | ||
}); | ||
return docDef; | ||
var docDef = parseElement(parsedHtml.body, []); | ||
// remove first level | ||
return docDef.stack || docDef.text; | ||
} | ||
@@ -130,9 +117,12 @@ | ||
*/ | ||
var parseElement = function(element, parentNode, parents) { | ||
var nodeName = element.nodeName.toLowerCase(); | ||
var parentNodeName = (parentNode ? parentNode.nodeName.toLowerCase() : ''); | ||
var ret, text, cssClass, dataset, key, dist, isInlineTag, hasStackTags; | ||
parents = parents || []; | ||
var parseElement = function(element, parents) { | ||
var nodeName = element.nodeName.toUpperCase(); | ||
var nodeNameLowerCase = nodeName.toLowerCase(); | ||
var ret = {text:[]}; | ||
var text, needStack=false; | ||
var dataset, i, key; | ||
// check the node type | ||
// ignore some HTML tags | ||
if (['COLGROUP','COL'].indexOf(nodeName) > -1) return ''; | ||
switch(element.nodeType) { | ||
@@ -142,69 +132,158 @@ case 3: { // TEXT_NODE | ||
text = element.textContent.replace(/\n(\s+)?/g, ""); | ||
if (text) { | ||
// if 'text' is just blank and parentNodeName is a TABLE/THEAD/TBODY/TR, then ignore it | ||
if (/^\s+$/.test(text) && ['table','thead','tbody','tr'].indexOf(parentNodeName) > -1) return ret; | ||
ret = {'text': text}; | ||
if (parentNodeName) { | ||
// check if we have inherent styles to apply when a text is inside several <tag> | ||
applyParentsStyle(ret, element); | ||
// for links | ||
if (parentNodeName === "a") { | ||
ret.link = parentNode.getAttribute("href"); | ||
} | ||
} else { | ||
ret = text; | ||
} | ||
ret = {'text':text}; | ||
ret = applyStyle({ret:ret, parents:parents}); | ||
return ret; | ||
} | ||
} | ||
return ret; | ||
return ''; | ||
} | ||
case 1: { // ELEMENT_NODE | ||
ret = []; | ||
parents.push(nodeName); | ||
// check children | ||
// if it's a table cell (TH/TD) with an empty content | ||
if (element.childNodes.length === 0 && (parents.indexOf('th') > -1 || parents.indexOf('td') > -1)) { | ||
// if one of the parents is a TH or a TD, then we go up until them to see if all ancestors are empty too | ||
var ancestor = element; | ||
var allEmpty = true; | ||
while (ancestor.nodeName !== "TH" && ancestor.nodeName !== "TD") { | ||
if (ancestor.textContent !== '') { | ||
allEmpty=false; | ||
break; | ||
} | ||
ancestor = ancestor.parentNode; | ||
} | ||
// if empty, we need to add it | ||
if (allEmpty) ret.push({text:''}); | ||
} | ||
else { | ||
ret.nodeName = nodeName; | ||
parents.push(element); | ||
if (element.childNodes && element.childNodes.length>0) { | ||
[].forEach.call(element.childNodes, function(child) { | ||
child = parseElement(child, element, parents); | ||
if (child) { | ||
if (Array.isArray(child) && child.length === 1) child=child[0]; | ||
ret.push(child); | ||
var res = parseElement(child, parents); | ||
if (res) { | ||
if (Array.isArray(res.text) && res.text.length===0) res.text=''; | ||
ret.text.push(res); | ||
} | ||
}); | ||
parents.pop(); | ||
//console.log(nodeName,'=>',util.inspect(ret.text, {showHidden: false, depth: null})); // to debug | ||
// find if we need a 'stack' instead of a 'text' | ||
needStack = searchForStack(ret); | ||
if (needStack) { | ||
ret.stack = ret.text.slice(0); | ||
delete ret.text; | ||
} else { | ||
// apply all the inhirent classes and styles from the parents | ||
ret = applyStyle({ret:ret, parents:parents}); | ||
} | ||
} | ||
parents.pop(); | ||
if (ret.length===0) ret=""; | ||
switch(nodeName) { | ||
case "TABLE":{ | ||
var rowIndex, cellIndex; | ||
// the format for the table is table.body[[], [], …] | ||
ret.table = {body:[]}; | ||
rowIndex = 0; | ||
// for each THEAD / TBODY | ||
(ret.stack || ret.text).forEach(function(tbody) { | ||
// for each row | ||
var rows = (tbody.stack || tbody.text); | ||
rows.forEach(function(row) { | ||
ret.table.body[rowIndex] = []; | ||
var cells = (row.stack || row.text); | ||
// for each cell | ||
cellIndex = 0; | ||
cells.forEach(function(cell) { | ||
ret.table.body[rowIndex].push(cell); | ||
// check which kind of tag we have | ||
switch (nodeName) { | ||
case "svg": { | ||
ret = { | ||
svg: element.outerHTML | ||
// do we have a colSpan? | ||
// if yes, insert empty cells due to colspan | ||
if (cell.colSpan) { | ||
i = cell.colSpan; | ||
// do we have a rowSpan in addition of the colSpan? | ||
setRowSpan({rows:rows, cell:cell, rowIndex:rowIndex, cellIndex:cellIndex}); | ||
while (--i > 0) { | ||
ret.table.body[rowIndex].push({text:''}); | ||
// keep adding empty cell due to rowspan | ||
setRowSpan({rows:rows, cell:cell, rowIndex:rowIndex, cellIndex:cellIndex}); | ||
cellIndex++; | ||
} | ||
} else { | ||
// do we have a rowSpan ? | ||
setRowSpan({rows:rows, cell:cell, rowIndex:rowIndex, cellIndex:cellIndex}); | ||
} | ||
cellIndex++; | ||
}); | ||
rowIndex++; | ||
}); | ||
}); | ||
delete ret.stack; | ||
delete ret.text; | ||
// apply all the inhirent classes and styles from the parents, or for the current element | ||
ret = applyStyle({ret:ret, parents:parents.concat([element])}); | ||
// if option tableAutoSize, then we try to apply the correct width/height on the table | ||
if (tableAutoSize) { | ||
var cellsWidths = []; | ||
var cellsHeights = []; | ||
var tableWidths = []; | ||
var tableHeights = []; | ||
ret.table.body.forEach(function(row, rowIndex) { | ||
cellsWidths.push([]); | ||
cellsHeights.push([]); | ||
row.forEach(function(cell) { | ||
// we want to remember the different sizes | ||
var width = typeof cell.width !== 'undefined' ? cell.width : 'auto'; | ||
var height = typeof cell.height !== 'undefined' ? cell.height : 'auto'; | ||
// check if we have colspan or rowspan | ||
if (width !== 'auto' && cell.colSpan) width /= cell.colSpan; | ||
if (height !== 'auto' && cell.rowSpan) height /= cell.colSpan; | ||
cellsWidths[rowIndex].push(width); | ||
cellsHeights[rowIndex].push(height); | ||
}); | ||
}); | ||
// determine the max width for each cell | ||
cellsWidths.forEach(function(row) { | ||
row.forEach(function(cellWidth, cellIndex) { | ||
var type = typeof tableWidths[cellIndex]; | ||
if (type === "undefined" || (cellWidth !== 'auto' && type === "number" && cellWidth > tableWidths[cellIndex]) || (cellWidth !== 'auto' && tableWidths[cellIndex] === 'auto')) { | ||
tableWidths[cellIndex] = cellWidth; | ||
} | ||
}); | ||
}); | ||
// determine the max height for each row | ||
cellsHeights.forEach(function(row, rowIndex) { | ||
row.forEach(function(cellHeight) { | ||
var type = typeof tableHeights[rowIndex]; | ||
if (type === "undefined" || (cellHeight !== 'auto' && type === "number" && cellHeight > tableHeights[rowIndex]) || (cellHeight !== 'auto' && tableHeights[rowIndex] === 'auto')) { | ||
tableHeights[rowIndex] = cellHeight; | ||
} | ||
}); | ||
}); | ||
if (tableWidths.length > 0) ret.table.widths = tableWidths; | ||
if (tableHeights.length > 0) ret.table.heights = tableHeights; | ||
} | ||
ret.style = ['html-'+nodeName]; | ||
// check if we have some data-pdfmake to apply | ||
if (element.dataset && element.dataset.pdfmake) { | ||
dataset = JSON.parse(element.dataset.pdfmake); | ||
for (key in dataset) { | ||
ret.table[key] = dataset[key]; | ||
} | ||
} | ||
break; | ||
} | ||
case "br": { | ||
case "TH": | ||
case "TD":{ | ||
if (element.getAttribute("rowspan")) ret.rowSpan = element.getAttribute("rowspan")*1; | ||
if (element.getAttribute("colspan")) ret.colSpan = element.getAttribute("colspan")*1; | ||
// apply all the inhirent classes and styles from the parents, or for the current element | ||
ret = applyStyle({ret:ret, parents:parents.concat([element])}); | ||
break; | ||
} | ||
case "SVG": { | ||
ret.svg = element.outerHTML.replace(/\n(\s+)?/g, ""); | ||
delete ret.stack; | ||
delete ret.text; | ||
// apply all the inhirent classes and styles from the parents, or for the current element | ||
ret = applyStyle({ret:ret, parents:parents.concat([element])}); | ||
break; | ||
} | ||
case "BR": { | ||
// for BR we return '\n' | ||
ret = '\n'; | ||
ret.text = [{text:'\n'}]; | ||
break; | ||
} | ||
case "hr": { | ||
case "HR": { | ||
// default style for the HR | ||
@@ -219,2 +298,3 @@ var styleHR = { | ||
}; | ||
// we can override the default HR style with "data-pdfmake" | ||
if (element.dataset && element.dataset.pdfmake) { | ||
@@ -227,275 +307,50 @@ dataset = JSON.parse(element.dataset.pdfmake); | ||
ret = { | ||
margin: styleHR.margin, | ||
canvas: [ | ||
{ | ||
type: styleHR.type, | ||
x1: styleHR.left, | ||
y1: 0, | ||
x2: styleHR.width, | ||
y2: 0, | ||
lineWidth: styleHR.thickness, | ||
lineColor: styleHR.color | ||
} | ||
] | ||
}; | ||
ret.margin = styleHR.margin; | ||
ret.canvas = [ | ||
{ | ||
type: styleHR.type, | ||
x1: styleHR.left, | ||
y1: 0, | ||
x2: styleHR.width, | ||
y2: 0, | ||
lineWidth: styleHR.thickness, | ||
lineColor: styleHR.color | ||
} | ||
]; | ||
delete ret.text; | ||
break; | ||
} | ||
case "ol": | ||
case "ul": { | ||
ret = {"_":ret}; | ||
ret[nodeName] = ret._; | ||
delete ret._; | ||
// add a custom class to let the user customize the element | ||
ret.style = ['html-'+nodeName]; | ||
// is there any class to this element? | ||
cssClass = element.getAttribute("class"); | ||
if (cssClass) { | ||
ret.style = ret.style.concat(cssClass.split(' ')); | ||
} | ||
// check if the element has a "style" attribute | ||
setComputedStyle(ret, element); | ||
case "OL": | ||
case "UL": { | ||
ret[nodeNameLowerCase] = (ret.stack || ret.text).slice(0); | ||
delete ret.stack; | ||
delete ret.text; | ||
// apply all the inhirent classes and styles from the parents, or for the current element | ||
ret = applyStyle({ret:ret, parents:parents.concat([element])}); | ||
break; | ||
} | ||
case "table":{ | ||
ret = {"_":ret, table:{body:[]}}; | ||
ret._.forEach(function(re) { | ||
if (re.stack) { | ||
var td = [], rowspan = {}; | ||
re.stack.forEach(function(r, indexRow) { | ||
var c, cell, i, indexCell; | ||
if (r.stack) { | ||
// do we have a rowspan to apply from previous rows? | ||
if (rowspan[indexRow]) { | ||
// insert empty cell due to rowspan | ||
rowspan[indexRow].forEach(function(cell) { | ||
r.stack.splice(cell.index, 0, {text:'', style: ['html-td', 'html-tr'], colSpan:cell.colspan}); | ||
}); | ||
} | ||
// insert empty cells due to colspan | ||
for (c=0, cell; c<r.stack.length;) { | ||
cell = r.stack[c]; | ||
if (cell.colSpan > 1) { | ||
for (i=0; i<cell.colSpan-1; i++) { | ||
r.stack.splice(c+1, 0, "") | ||
} | ||
c += cell.colSpan; | ||
} else c++; | ||
} | ||
// check rowspan for the current row in order to then apply it to the next ones | ||
indexCell = 0; | ||
r.stack.forEach(function(cell) { | ||
if (cell.rowSpan) { | ||
for (var i=0; i<cell.rowSpan; i++) { | ||
if (!rowspan[indexRow+i]) rowspan[indexRow+i] = []; | ||
// we also remember the colSpan for cells with both rowspan and colspan | ||
rowspan[indexRow+i].push({index:indexCell, colspan:cell.colSpan||1}); | ||
} | ||
} | ||
indexCell += cell.colSpan || 1; | ||
}); | ||
ret.table.body.push(r.stack) | ||
} else { | ||
td.push(r); | ||
// insert empty cells due to colspan | ||
if (r.colSpan > 1) { | ||
for (i=0; i<r.colSpan-1; i++) { | ||
td.push(""); | ||
} | ||
} | ||
} | ||
}); | ||
if (td.length>0) ret.table.body.push(td); | ||
} else { | ||
// only one row | ||
ret.table.body.push([re]); | ||
} | ||
}); | ||
delete ret._; | ||
// check if the element has a "style" attribute | ||
setComputedStyle(ret, element); | ||
// if we have P or DIV in a table cell TD or TH, then we need to add \n | ||
var insertBreakLine = function(content, prevContent, firstStyleOnly) { | ||
var breakLine=false, i; | ||
// if the previous content was not P or DIV, then we need to add a \n too | ||
if (prevContent && Array.isArray(content.style)) { | ||
for (i=0; i<content.style.length; i++) { | ||
if (firstStyleOnly && i>0) break; | ||
if (content.style[i] === 'html-p' || content.style[i] === 'html-div') { | ||
breakLine=true; | ||
break; | ||
} | ||
else if (content.style[i] === 'html-td') break; | ||
} | ||
if (breakLine) { | ||
if (typeof prevContent.text === 'string') { | ||
prevContent.text += "\n"; | ||
} | ||
else if (Array.isArray(prevContent.text)) { | ||
prevContent.text.push({text:"\n"}); | ||
} | ||
} else if (!firstStyleOnly && Array.isArray(prevContent.style)) { | ||
for (i=0; i<prevContent.style.length; i++) { | ||
if (prevContent.style[i] === 'html-p' || prevContent.style[i] === 'html-div') { | ||
breakLine=true; | ||
break; | ||
} | ||
else if (prevContent.style[i] === 'html-td') break; | ||
} | ||
if (breakLine) { | ||
if (typeof prevContent.text === 'string') { | ||
prevContent.text += "\n"; | ||
} | ||
else if (Array.isArray(prevContent.text)) { | ||
prevContent.text.push({text:"\n"}); | ||
} | ||
} | ||
} | ||
} | ||
if (Array.isArray(content.text)) { | ||
for (i=0; i<content.text.length; i++) { | ||
insertBreakLine(content.text[i], content.text[i-1], true) | ||
} | ||
} | ||
} | ||
ret.table.body.forEach(function(row) { | ||
row.forEach(function(cell) { | ||
if (Array.isArray(cell.text)) { | ||
for (var i=0; i<cell.text.length; i++) { | ||
insertBreakLine(cell.text[i], cell.text[i-1]); | ||
} | ||
} | ||
}) | ||
}) | ||
case "IMG": { | ||
ret.image = element.getAttribute("src"); | ||
delete ret.stack; | ||
delete ret.text; | ||
// apply all the inhirent classes and styles from the parents, or for the current element | ||
ret = applyStyle({ret:ret, parents:parents.concat([element])}); | ||
break; | ||
} | ||
case "img": { | ||
ret = {image:element.getAttribute("src")}; | ||
ret.style = ['html-img']; | ||
cssClass = element.getAttribute("class"); | ||
if (cssClass) { | ||
ret.style = ret.style.concat(cssClass.split(' ')); | ||
} | ||
// check if we have 'width' and 'height' | ||
if (element.getAttribute("width")) { | ||
ret.width = parseFloat(element.getAttribute("width")) | ||
} | ||
if (element.getAttribute("height")) { | ||
ret.height = parseFloat(element.getAttribute("height")) | ||
} | ||
// check if the element has a "style" attribute | ||
setComputedStyle(ret, element); | ||
case "A": { | ||
ret.link = element.getAttribute("href"); | ||
break; | ||
} | ||
case "h1": | ||
case "h2": | ||
case "h3": | ||
case "h4": | ||
case "h5": | ||
case "h6": { | ||
// encapsule in a stack | ||
ret = {stack:[{text:ret}]}; | ||
ret.stack[0].style = ['html-'+nodeName]; | ||
cssClass = element.getAttribute("class"); | ||
if (cssClass) { | ||
ret.stack[0].style = ret.stack[0].style.concat(cssClass.split(' ')); | ||
} | ||
// apply default style | ||
applyDefaultStyle(ret.stack[0], nodeName); | ||
// check if the element has a "style" attribute | ||
setComputedStyle(ret.stack[0], element); | ||
break; | ||
} | ||
} | ||
// add a custom class to let the user customize the element | ||
if (ret) { | ||
if (Array.isArray(ret)) { | ||
// "tr" elements should always contain an array | ||
if (ret.length === 1 && nodeName !== "tr") { | ||
ret=ret[0]; | ||
if (typeof ret === "string") ret={text:ret}; | ||
if (ret.text) { | ||
applyDefaultStyle(ret, nodeName); | ||
setComputedStyle(ret, element); | ||
} | ||
if (Array.isArray(ret.text) && ret.text.length === 1 && ret.text[0].text && !ret.text[0].nodeName) { | ||
ret.text = ret.text[0].text; | ||
} | ||
ret.style = (ret.style||[]).concat(['html-'+nodeName]); | ||
// for TD and TH we want to include the style from TR | ||
if (nodeName === "td" || nodeName === "th") ret.style.push('html-tr'); | ||
} else { | ||
isInlineTag = (inlineTags.indexOf(nodeName) > -1); | ||
hasStackTags = /{"(stack|table|ol|ul|image)"/.test(JSON.stringify(ret)); | ||
// if it's a DIV and only inline tags in its section, then we want to use 'text' instead of 'stack' | ||
if (nodeName === 'div' && !hasStackTags) ret = {text:ret}; | ||
// if we have an inline tag with stack tags in its section, then we want to use 'stack' | ||
else if (isInlineTag && hasStackTags) ret = {stack:ret}; | ||
// if it's not an inline tag, then we use 'stack' | ||
else if (!isInlineTag) ret = {stack:ret}; | ||
// otherwise, for inline tags, we use 'text' | ||
else ret = {text:ret}; | ||
// we apply the default style for the inline tags | ||
if (isInlineTag) { | ||
applyDefaultStyle(ret, nodeName); | ||
} | ||
ret.style = ['html-'+nodeName]; | ||
} | ||
// check if we have inherent styles to apply when a text is inside several <tag> | ||
applyParentsStyle(ret, element); | ||
// for 'td' and 'th' we check if we have "rowspan" or "colspan" | ||
if (nodeName === "td" || nodeName === "th") { | ||
if (element.getAttribute("rowspan")) ret.rowSpan = element.getAttribute("rowspan")*1; | ||
if (element.getAttribute("colspan")) ret.colSpan = element.getAttribute("colspan")*1; | ||
} | ||
// is there any class to this element? | ||
cssClass = element.getAttribute("class"); | ||
if (cssClass) { | ||
ret.style = (ret.style||[]).concat(cssClass.split(' ')); | ||
} | ||
// check if the element has a "style" attribute | ||
if (ret.text) { | ||
setComputedStyle(ret, element); | ||
} | ||
} else if (ret.table || ret.ol || ret.ul) { // for TABLE / UL / OL | ||
ret.style = ['html-'+nodeName]; | ||
// is there any class to this element? | ||
cssClass = element.getAttribute("class"); | ||
if (cssClass) { | ||
ret.style = ret.style.concat(cssClass.split(' ')); | ||
} | ||
// do we have a default style to apply? | ||
applyDefaultStyle(ret, nodeName); | ||
// chekck if we have some data-pdfmake to apply | ||
if (nodeName !== 'HR' && nodeName !== 'TABLE' && element.dataset && element.dataset.pdfmake) { | ||
dataset = JSON.parse(element.dataset.pdfmake); | ||
for (key in dataset) { | ||
ret[key] = dataset[key]; | ||
} | ||
if (element.dataset && element.dataset.pdfmake) { | ||
dataset = JSON.parse(element.dataset.pdfmake); | ||
dist = ret[nodeName] || ret; | ||
for (key in dataset) { | ||
dist[key] = dataset[key]; | ||
} | ||
} | ||
// retrieve the class from the parent | ||
cssClass = element.getAttribute("class"); | ||
if (cssClass && typeof ret === 'object') { | ||
ret.style = (ret.style || []) | ||
.concat(cssClass.split(' ')) | ||
} | ||
// remove doublon in classes | ||
if (typeof ret === 'object' && Array.isArray(ret.style)) { | ||
ret.style = ret.style | ||
.filter(function (value, index, self) { | ||
return self.indexOf(value) === index; | ||
}); | ||
} | ||
} | ||
@@ -506,48 +361,81 @@ | ||
} | ||
return ""; | ||
} | ||
var applyDefaultStyle = function(ret, nodeName) { | ||
if (defaultStyles[nodeName]) { | ||
for (var style in defaultStyles[nodeName]) { | ||
if (defaultStyles[nodeName].hasOwnProperty(style)) { | ||
ret[style] = defaultStyles[nodeName][style]; | ||
} | ||
var searchForStack = function(ret) { | ||
if (Array.isArray(ret.text)) { | ||
for (var i=0; i<ret.text.length; i++) { | ||
if (ret.text[i].stack || ['P','DIV','TABLE','SVG','UL','OL','IMG','H1','H2','H3','H4','H5','H6'].indexOf(ret.text[i].nodeName) > -1) return true; | ||
if (searchForStack(ret.text[i]) === true) return true; | ||
} | ||
} | ||
return false; | ||
} | ||
var applyParentsStyle = function(ret, node) { | ||
// while the parents are an inline tag, we want to apply the default style and the class to the children too | ||
var classes = [], defaultStyles = [], cssClass, cssStyles=[]; | ||
var parentNode=node.parentNode; | ||
var inlineParentNode=node.parentNode; | ||
while (inlineParentNode) { | ||
var defaultStyle = {}; | ||
var inlineParentNodeName=inlineParentNode.nodeName.toLowerCase(); | ||
if (inlineTags.indexOf(inlineParentNodeName) > -1) { | ||
cssClass = inlineParentNode.getAttribute("class"); | ||
classes = classes.concat(['html-'+inlineParentNodeName], cssClass||[]); | ||
applyDefaultStyle(defaultStyle, inlineParentNodeName); | ||
defaultStyles.push(defaultStyle); | ||
/** | ||
* Add empty cells due to rowspan | ||
* | ||
* @param {Object} params | ||
* @param {Array} rows | ||
* @param {Object} cell | ||
* @param {Number} rowIndex Current row index | ||
* @param {Number} cellIndex Current cell index | ||
*/ | ||
var setRowSpan = function(params) { | ||
var cells; | ||
if (params.cell.rowSpan) { | ||
for (var i=1; i <= params.cell.rowSpan-1; i++) { | ||
cells = (params.rows[params.rowIndex+i].text || params.rows[params.rowIndex+i].stack); | ||
cells.splice(params.cellIndex, 0, {text:''}); | ||
} | ||
} | ||
} | ||
inlineParentNode=inlineParentNode.parentNode; | ||
} else break; | ||
} | ||
ret.style = (ret.style||[]).concat(classes); | ||
defaultStyles.forEach(function(defaultStyle) { | ||
for (var key in defaultStyle) { | ||
if (key.indexOf("margin") === -1 && ret[key] === undefined) ret[key] = defaultStyle[key]; | ||
/** | ||
* Apply style and classes from all the parents | ||
* | ||
* @param {Object} params | ||
* @param {Object} ret The object that will receive the 'style' and other properties | ||
* @param {Array} parents Array of node elements | ||
* @return {Object} the modified 'ret' | ||
*/ | ||
var applyStyle = function(params) { | ||
var cssClass = []; | ||
var lastIndex = params.parents.length-1; | ||
params.parents.forEach(function(parent, parentIndex) { | ||
// classes | ||
var parentNodeName = parent.nodeName.toLowerCase(); | ||
var htmlClass = 'html-' + parentNodeName; | ||
if (htmlClass !== 'html-body' && cssClass.indexOf(htmlClass) === -1) cssClass.unshift(htmlClass); | ||
var parentClass = (parent.getAttribute("class")||"").split(' '); | ||
parentClass.forEach(function(p) { | ||
if (p) cssClass.push(p); | ||
}); | ||
// styles | ||
var style; | ||
// not all the CSS properties should be inhirent | ||
var ignoreNonDescendentProperties = (parentIndex!==lastIndex); | ||
// 1) the default styles | ||
if (defaultStyles[parentNodeName]) { | ||
for (style in defaultStyles[parentNodeName]) { | ||
if (defaultStyles[parentNodeName].hasOwnProperty(style)) { | ||
if (!ignoreNonDescendentProperties || | ||
(ignoreNonDescendentProperties && | ||
style.indexOf('margin') === -1 && | ||
style.indexOf('border') === -1 | ||
) | ||
) | ||
params.ret[style] = defaultStyles[parentNodeName][style]; | ||
} | ||
} | ||
} | ||
// 2) element's style | ||
// we want TD/TH to receive descendent properties from TR | ||
if (parentNodeName === 'tr') ignoreNonDescendentProperties=false; | ||
style = parseStyle(parent, ignoreNonDescendentProperties); | ||
style.forEach(function(stl) { | ||
params.ret[stl.key] = stl.value; | ||
}); | ||
}); | ||
// all the css 'style' of the parents must be transferred to the children | ||
while (parentNode.nodeType === 1) { | ||
cssStyles = cssStyles.concat(computeStyle(parentNode.getAttribute('style'), parentNode)); | ||
parentNode = parentNode.parentNode; | ||
} | ||
cssStyles.reverse(); | ||
cssStyles.forEach(function(stl) { | ||
ret[stl.key] = stl.value; | ||
}) | ||
if (ret.style.length===0) delete ret.style; | ||
params.ret.style = cssClass; | ||
return params.ret; | ||
} | ||
@@ -560,7 +448,17 @@ | ||
* @param {DOMElement} element | ||
* @param {Boolean} ignoreProperties TRUE when we have to ignore some properties, like border, padding, margin | ||
* @returns {Array} array of {key, value} | ||
*/ | ||
var computeStyle = function(style, element) { | ||
var parseStyle = function(element, ignoreProperties) { | ||
var style = element.getAttribute("style"); | ||
if (!style) return []; | ||
var styleDefs = style.split(';').map(function(style) { return style.toLowerCase().split(':') }); | ||
style = style.split(';'); | ||
// check if we have "width" or "height" | ||
if (element.getAttribute("width")) { | ||
style.unshift("width:" + element.getAttribute("width") + "px"); | ||
} | ||
if (element.getAttribute("height")) { | ||
style.unshift("height:" + element.getAttribute("height") + "px"); | ||
} | ||
var styleDefs = style.map(function(style) { return style.toLowerCase().split(':') }); | ||
var ret = []; | ||
@@ -575,13 +473,20 @@ var borders = []; // special treatment for borders | ||
case "margin": { | ||
value = value.replace(/(\d+)(\.\d+)?([^\d]+)/g,"$1$2 ").trim().split(' '); | ||
if (ignoreProperties) break; | ||
// pdfMake uses a different order than CSS | ||
if (value.length===1) value=+value[0]; // single value | ||
else if (value.length===2) value=[+value[1], +value[0]]; // vertical | horizontal ==> horizontal | vertical | ||
else if (value.length===3) value=[+value[1], +value[0], +value[1], +value[2]]; // top | horizontal | bottom ==> left | top | right | bottom | ||
else if (value.length===4) value=[+value[3], +value[0], +value[1], +value[2]]; // top | right | bottom | left ==> left | top | right | bottom | ||
ret.push({key:key, value:value}); | ||
value = value.split(' '); | ||
if (value.length===1) value=[value[0], value[0], value[0], value[0]]; | ||
else if (value.length===2) value=[value[1], value[0]]; // vertical | horizontal ==> horizontal | vertical | ||
else if (value.length===3) value=[value[1], value[0], value[1], value[2]]; // top | horizontal | bottom ==> left | top | right | bottom | ||
else if (value.length===4) value=[value[3], value[0], value[1], value[2]]; // top | right | bottom | left ==> left | top | right | bottom | ||
// we now need to convert to PT | ||
value.forEach(function(val, i) { | ||
value[i] = convertToUnit(val); | ||
}); | ||
// ignore if we have a FALSE in the table | ||
if (value.indexOf(false) === -1) ret.push({key:key, value:value}); | ||
break; | ||
} | ||
case "text-align": { | ||
ret.push({key:"alignment", value:value}) | ||
ret.push({key:"alignment", value:value}); | ||
break; | ||
@@ -617,9 +522,13 @@ } | ||
if (key === 'border' || key.indexOf('border-left') === 0 || key.indexOf('border-top') === 0 || key.indexOf('border-right') === 0 || key.indexOf('border-bottom') === 0) { | ||
borders.push({key:key, value:value}); | ||
if (!ignoreProperties) borders.push({key:key, value:value}); | ||
} else { | ||
// ignore some properties | ||
if (ignoreProperties && (key.indexOf("margin-") === 0 || key === 'width' || key === 'height')) break; | ||
// padding is not supported by PDFMake | ||
if (key.indexOf("padding") === 0) break; | ||
if (key.indexOf("-") > -1) key=toCamelCase(key); | ||
if (value) { | ||
value = value.replace(/(\d+)(\.\d+)?([^\d]+)/g,"$1$2 ").trim(); | ||
if (!isNaN(value)) value=+value; // turn it into a number | ||
ret.push({key:key, value:value}); | ||
// convert value to a 'pt' when possible | ||
var parsedValue = convertToUnit(value); | ||
ret.push({key:key, value:(parsedValue === false ? value : parsedValue)}); | ||
} | ||
@@ -672,17 +581,2 @@ } | ||
/** | ||
* Go throught the CSS styles for the element and apply them | ||
* @param {Object} ret Our pdfmake object | ||
* @param {DOMElement} element The DOM elemnet | ||
*/ | ||
var setComputedStyle = function(ret, element) { | ||
var cssStyle = element.getAttribute("style"); | ||
if (cssStyle) { | ||
cssStyle = computeStyle(cssStyle, element); | ||
cssStyle.forEach(function(style) { | ||
ret[style.key] = style.value; | ||
}) | ||
} | ||
} | ||
var toCamelCase = function(str) { | ||
@@ -730,3 +624,29 @@ return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() }); | ||
/** | ||
* Convert 'px'/'rem' to 'pt', and return false for the other ones. If it's only a number, it will just return it | ||
* | ||
* @param {String} val The value with units (e.g. 12px) | ||
* @return {Number|Boolean} Return the pt value, or false | ||
*/ | ||
var convertToUnit = function(val) { | ||
// if it's just a number, then return it | ||
if (!isNaN(parseFloat(val)) && isFinite(val)) return val; | ||
var mtch = (val+"").trim().match(/^(\d+(\.\d+)?)(pt|px|rem)$/); | ||
// if we don't have a number with supported units, then return false | ||
if (!mtch) return false; | ||
val = mtch[1]; | ||
switch(mtch[3]) { | ||
case 'px':{ | ||
val = Math.round(val * 0.75292857248934); // 1px => 0.75292857248934pt | ||
break; | ||
} | ||
case 'rem':{ | ||
val *= 12; // default font-size is 12pt | ||
break; | ||
} | ||
} | ||
return val*1; | ||
} | ||
return convertHtml(htmlText) | ||
} |
{ | ||
"name": "html-to-pdfmake", | ||
"version": "1.3.6", | ||
"version": "2.0.0", | ||
"description": "Convert HTML code to PDFMake", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -101,2 +101,34 @@ # html-to-pdfmake | ||
### Options | ||
Some options can be passed to `htmlToPdfmake` function as a second argument. | ||
#### `window` | ||
If you use Node, then you'll have to pass the `window` object ([see below](https://github.com/Aymkdn/html-to-pdfmake#use-with-node)). | ||
#### `defaultStyles` | ||
You can overwrite the default stles using `defaultStyles` ([see below](https://github.com/Aymkdn/html-to-pdfmake#default-styles)). | ||
#### `tableAutoSize` | ||
By pass `tableAutoSize` with `true`, then the program will try to define `widths` and `heights` for the tables, based on CSS properties `width` and `height` that have been provided to `TH` or `TD`. | ||
Example: | ||
```javascript | ||
var html = htmlToPdfmake(`<table> | ||
<tr style="height:100px"> | ||
<td style="width:250px">height:100px / width:250px</td> | ||
<td>height:100px / width:'auto'</td> | ||
</tr> | ||
<tr> | ||
<td style="width:100px">Here it will use 250px for the width because we have to use the largest col's width</td> | ||
<td style="height:200px">height:200px / width:'auto'</td> | ||
</tr> | ||
</table>`, { | ||
tableAutoSize:true | ||
}); | ||
``` | ||
### HTML tags supported | ||
@@ -103,0 +135,0 @@ |
342
test/unit.js
@@ -6,6 +6,7 @@ var htmlToPdfMake = require('../index.js'); | ||
var { window } = new JSDOM(""); | ||
var debug = false; | ||
// { text: 'bold word', bold: true, style: [ 'html-b' ] } | ||
test("b",function(t) { | ||
var ret = htmlToPdfMake("<b>bold word</b>", window); | ||
var ret = htmlToPdfMake("<b>bold word</b>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -24,3 +25,4 @@ ret = ret[0]; | ||
test("strong",function(t) { | ||
var ret = htmlToPdfMake("<strong>bold word</strong>", window); | ||
var ret = htmlToPdfMake("<strong>bold word</strong>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -38,5 +40,5 @@ ret = ret[0]; | ||
// { text: 'underline word', decoration: 'underline', style: [ 'html-u' ] } | ||
test("u",function(t) { | ||
var ret = htmlToPdfMake("<u>underline word</u>", window); | ||
var ret = htmlToPdfMake("<u>underline word</u>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -55,3 +57,4 @@ ret = ret[0]; | ||
test("em",function(t) { | ||
var ret = htmlToPdfMake("<em>italic word</em>", window); | ||
var ret = htmlToPdfMake("<em>italic word</em>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -70,3 +73,4 @@ ret = ret[0]; | ||
test("i",function(t) { | ||
var ret = htmlToPdfMake("<i>italic word</i>", window); | ||
var ret = htmlToPdfMake("<i>italic word</i>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -84,10 +88,9 @@ ret = ret[0]; | ||
// [{stack:[{text:[{text:'level 1'}],style:[ 'html-h1' ],fontSize:24,bold:true,marginBottom:5}]] | ||
test("h1",function(t) { | ||
var ret = htmlToPdfMake("<h1>level 1</h1>", window); | ||
t.check(Array.isArray(ret) && ret[0].stack && Array.isArray(ret[0].stack), "return is OK"); | ||
ret = ret[0].stack[0]; | ||
var ret = htmlToPdfMake("<h1>level 1</h1>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret), "return is OK"); | ||
ret = ret[0]; | ||
t.check( | ||
Array.isArray(ret.text) && | ||
ret.text[0].text === "level 1" && | ||
ret.text === "level 1" && | ||
ret.fontSize === 24 && | ||
@@ -104,8 +107,8 @@ ret.bold === true && | ||
test("h2",function(t) { | ||
var ret = htmlToPdfMake("<h2>level 2</h2>", window); | ||
t.check(Array.isArray(ret) && ret[0].stack && Array.isArray(ret[0].stack), "return is OK"); | ||
ret = ret[0].stack[0]; | ||
var ret = htmlToPdfMake("<h2>level 2</h2>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret), "return is OK"); | ||
ret = ret[0]; | ||
t.check( | ||
Array.isArray(ret.text) && | ||
ret.text[0].text === "level 2" && | ||
ret.text === "level 2" && | ||
ret.fontSize === 22 && | ||
@@ -122,8 +125,8 @@ ret.bold === true && | ||
test("h3",function(t) { | ||
var ret = htmlToPdfMake("<h3>level 3</h3>", window); | ||
t.check(Array.isArray(ret) && ret[0].stack && Array.isArray(ret[0].stack), "return is OK"); | ||
ret = ret[0].stack[0]; | ||
var ret = htmlToPdfMake("<h3>level 3</h3>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret), "return is OK"); | ||
ret = ret[0]; | ||
t.check( | ||
Array.isArray(ret.text) && | ||
ret.text[0].text === "level 3" && | ||
ret.text === "level 3" && | ||
ret.fontSize === 20 && | ||
@@ -140,8 +143,8 @@ ret.bold === true && | ||
test("h4",function(t) { | ||
var ret = htmlToPdfMake("<h4>level 4</h4>", window); | ||
t.check(Array.isArray(ret) && ret[0].stack && Array.isArray(ret[0].stack), "return is OK"); | ||
ret = ret[0].stack[0]; | ||
var ret = htmlToPdfMake("<h4>level 4</h4>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret), "return is OK"); | ||
ret = ret[0]; | ||
t.check( | ||
Array.isArray(ret.text) && | ||
ret.text[0].text === "level 4" && | ||
ret.text === "level 4" && | ||
ret.fontSize === 18 && | ||
@@ -158,8 +161,8 @@ ret.bold === true && | ||
test("h5",function(t) { | ||
var ret = htmlToPdfMake("<h5>level 5</h5>", window); | ||
t.check(Array.isArray(ret) && ret[0].stack && Array.isArray(ret[0].stack), "return is OK"); | ||
ret = ret[0].stack[0]; | ||
var ret = htmlToPdfMake("<h5>level 5</h5>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret), "return is OK"); | ||
ret = ret[0]; | ||
t.check( | ||
Array.isArray(ret.text) && | ||
ret.text[0].text === "level 5" && | ||
ret.text === "level 5" && | ||
ret.fontSize === 16 && | ||
@@ -176,8 +179,8 @@ ret.bold === true && | ||
test("h6",function(t) { | ||
var ret = htmlToPdfMake("<h6>level 6</h6>", window); | ||
t.check(Array.isArray(ret) && ret[0].stack && Array.isArray(ret[0].stack), "return is OK"); | ||
ret = ret[0].stack[0]; | ||
var ret = htmlToPdfMake("<h6>level 6</h6>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret), "return is OK"); | ||
ret = ret[0]; | ||
t.check( | ||
Array.isArray(ret.text) && | ||
ret.text[0].text === "level 6" && | ||
ret.text === "level 6" && | ||
ret.fontSize === 14 && | ||
@@ -193,5 +196,5 @@ ret.bold === true && | ||
// { text: 'link', color: 'blue', decoration: 'underline', link: 'https://www.somewhere.com', style: [ 'html-a' ] } | ||
test("a",function(t) { | ||
var ret = htmlToPdfMake('<a href="https://www.somewhere.com">link</a>', window); | ||
var ret = htmlToPdfMake('<a href="https://www.somewhere.com">link</a>', {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -211,5 +214,5 @@ ret = ret[0]; | ||
// { text: 'strike', decoration: 'lineThrough', style: [ 'html-strike' ] } | ||
test("strike",function(t) { | ||
var ret = htmlToPdfMake("<strike>strike</strike>", window); | ||
var ret = htmlToPdfMake("<strike>strike</strike>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -247,3 +250,4 @@ ret = ret[0]; | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -275,3 +279,4 @@ ret = ret[0]; | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -299,3 +304,4 @@ ret = ret[0]; | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -329,3 +335,4 @@ ret = ret[0]; | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -387,3 +394,4 @@ ret = ret[0]; | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -412,13 +420,13 @@ ret = ret[0]; | ||
ret.table.body[3][1].text === "Cell B3 & C3" && | ||
ret.table.body[3][2] === "" && | ||
ret.table.body[3][2].text === "" && | ||
ret.table.body[3][3].text === "Cell D3", "row 4"); | ||
t.check( | ||
ret.table.body[4][0].text === "Cell A4 & A5 & B4 & B5 & C4 & C5" && | ||
ret.table.body[4][1] === "" && | ||
ret.table.body[4][2] === "" && | ||
ret.table.body[4][1].text === "" && | ||
ret.table.body[4][2].text === "" && | ||
ret.table.body[4][3].text === "Cell D4", "row 5"); | ||
t.check( | ||
ret.table.body[5][0].text === "" && | ||
ret.table.body[5][1] === "" && | ||
ret.table.body[5][2] === "" && | ||
ret.table.body[5][1].text === "" && | ||
ret.table.body[5][2].text === "" && | ||
ret.table.body[5][3].text === "Cell D5", "row 6"); | ||
@@ -450,3 +458,4 @@ t.check( | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -480,3 +489,4 @@ ret = ret[0]; | ||
test("img",function(t) { | ||
var ret = htmlToPdfMake('<img width="10" style="height:10px" src="data:image/jpeg;base64,...encodedContent...">', window); | ||
var ret = htmlToPdfMake('<img width="10" style="height:10px" src="data:image/jpeg;base64,...encodedContent...">', {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -486,3 +496,3 @@ ret = ret[0]; | ||
ret.image === "data:image/jpeg;base64,...encodedContent..." && | ||
ret.width === 10 && ret.height === 10 && | ||
ret.width === 8 && ret.height === 8 && | ||
Array.isArray(ret.style) && | ||
@@ -501,3 +511,4 @@ ret.style[0] === 'html-img', | ||
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text> | ||
</svg>`, window); | ||
</svg>`, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -520,5 +531,6 @@ ret = ret[0]; | ||
test("cascade_tags", function(t) { | ||
var ret = htmlToPdfMake('<p style="text-align: center;"><span style="font-size: 14px;"><em><strong>test</strong></em></span></p>', window); | ||
var ret = htmlToPdfMake('<p style="text-align: center;"><span style="font-size: 14px;"><em><strong>test</strong></em></span></p>', {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
ret = ret[0]; | ||
ret = ret[0].text[0].text[0].text[0]; | ||
t.check( | ||
@@ -528,3 +540,3 @@ ret.text === "test" && | ||
ret.italics && | ||
ret.fontSize === 14 && | ||
ret.fontSize === 11 && | ||
ret.alignment === 'center' && | ||
@@ -542,3 +554,4 @@ Array.isArray(ret.style) && | ||
test("hr", function(t) { | ||
var ret = htmlToPdfMake("<hr>", window); | ||
var ret = htmlToPdfMake("<hr>", {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length === 1, "return is OK"); | ||
@@ -574,3 +587,4 @@ ret = ret[0]; | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -606,3 +620,4 @@ ret = ret[0]; | ||
</table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -621,3 +636,4 @@ ret = ret[0]; | ||
var html = `<div style="color:red;"><span style="color:blue">blue<strong style="color:green">green</strong>blue</span><span>red</span></div>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -648,3 +664,4 @@ ret = ret[0]; | ||
var html = `<table><tr><td style="border-top-width: 0; border-right: 1pt solid #0080C0; border-bottom: 0; border-left: 1px solid #0080C0;">Cell with border left and right in blue</td></tr></table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
@@ -669,161 +686,54 @@ ret = ret[0]; | ||
/*[ | ||
{ | ||
"table": { | ||
"body": [ | ||
[ | ||
{ | ||
"text": [ | ||
{ | ||
"text": "some text\n", | ||
"style": [ | ||
"html-td" | ||
] | ||
}, | ||
{ | ||
"text": [ | ||
{ | ||
"text": "p1", | ||
"style": [ | ||
"html-p", | ||
"html-td" | ||
] | ||
}, | ||
{ | ||
"text": "span1", | ||
"style": [ | ||
"html-span", | ||
"html-p", | ||
"html-td" | ||
] | ||
}, | ||
{ | ||
"text": "span2", | ||
"style": [ | ||
"html-span", | ||
"html-p", | ||
"html-td" | ||
] | ||
}, | ||
{ | ||
"text": "\n" | ||
} | ||
], | ||
"margin": [ | ||
0, | ||
5, | ||
0, | ||
10 | ||
], | ||
"style": [ | ||
"html-p", | ||
"html-td" | ||
] | ||
}, | ||
{ | ||
"text": "p2\n", | ||
"style": [ | ||
"html-p", | ||
"html-td" | ||
], | ||
"margin": [ | ||
0, | ||
5, | ||
0, | ||
10 | ||
] | ||
}, | ||
{ | ||
"text": "span3\n", | ||
"style": [ | ||
"html-span", | ||
"html-td" | ||
] | ||
}, | ||
{ | ||
"text": "p3span4\n", | ||
"style": [ | ||
"html-span", | ||
"html-p", | ||
"html-td" | ||
], | ||
"margin": [ | ||
0, | ||
5, | ||
0, | ||
10 | ||
] | ||
}, | ||
{ | ||
"text": [ | ||
{ | ||
"text": "span5\n", | ||
"style": [ | ||
"html-span" | ||
] | ||
}, | ||
{ | ||
"text": "p4", | ||
"style": [ | ||
"html-p" | ||
], | ||
"margin": [ | ||
0, | ||
5, | ||
0, | ||
10 | ||
] | ||
}, | ||
{ | ||
"text": "\n" | ||
} | ||
], | ||
"style": [ | ||
"html-div", | ||
"html-td" | ||
] | ||
}, | ||
{ | ||
"text": "strong", | ||
"style": [ | ||
"html-strong", | ||
"html-td" | ||
], | ||
"bold": true | ||
} | ||
], | ||
"style": [ | ||
"html-td" | ||
] | ||
} | ||
] | ||
] | ||
}, | ||
"style": [ | ||
"html-table" | ||
], | ||
"marginBottom": 5 | ||
} | ||
]*/ | ||
test("cell with P and DIV", function(t) { | ||
var html = `<table><tr><td>some text<p>p1<span>span1</span><span>span2</span></p><p>p2</p><span>span3</span><p><span>p3span4</span></p><div><span>span5</span><p>p4</p></div><strong>strong</strong></td></tr></table>`; | ||
var ret = htmlToPdfMake(html, window); | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
ret = ret[0].table.body[0][0]; | ||
t.check(ret.stack[0].text === "some text", "some text"); | ||
t.check(ret.stack[1].text[0].text === "p1", "p1"); | ||
t.check(ret.stack[1].text[1].text === "span1", "span1"); | ||
t.check(ret.stack[1].text[2].text === "span2", "span2"); | ||
t.check(ret.stack[2].text === "p2", "p2"); | ||
t.check(ret.stack[3].text === "span3", "span3"); | ||
t.check(ret.stack[4].text[0].text === "p3span4", "p3span4"); | ||
t.check(ret.stack[5].stack[0].text === "span5", "span5"); | ||
t.check(ret.stack[5].stack[1].text === "p4", "p4"); | ||
t.check(ret.stack[6].text === "strong", "strong"); | ||
t.finish(); | ||
}) | ||
test("tableAutoSize", function(t) { | ||
var html = `<table><tr style="height:100px"><td style="width:350px"></td><td></td></tr><tr><td style="width:100px"></td><td style="height:200px"></td></tr></table>`; | ||
var ret = htmlToPdfMake(html, {window:window, tableAutoSize:true}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
ret = ret[0]; | ||
t.check( | ||
ret.table.body[0][0].text[0].text === "some text\n" && | ||
ret.table.body[0][0].text[1].text[0].text === "p1" && | ||
ret.table.body[0][0].text[1].text[1].text === "span1" && | ||
ret.table.body[0][0].text[1].text[2].text === "span2" && | ||
ret.table.body[0][0].text[1].text[3].text === "\n" && | ||
ret.table.body[0][0].text[2].text === "p2\n" && | ||
ret.table.body[0][0].text[3].text === "span3\n" && | ||
ret.table.body[0][0].text[4].text === "p3span4\n" && | ||
ret.table.body[0][0].text[5].text[0].text === "span5\n" && | ||
ret.table.body[0][0].text[5].text[1].text === "p4" && | ||
ret.table.body[0][0].text[5].text[2].text === "\n" && | ||
ret.table.body[0][0].text[6].text === "strong", | ||
"cell with P and DIV"); | ||
Array.isArray(ret.table.widths) && | ||
ret.table.widths.length === 2 && | ||
ret.table.widths[0] === 264 && | ||
ret.table.widths[1] === 'auto' && | ||
ret.table.heights.length === 2 && | ||
ret.table.heights[0] === 75 && | ||
ret.table.heights[1] === 151 | ||
, "tableAutoSize"); | ||
t.finish(); | ||
}) | ||
test("convertUnit and stack", function(t) { | ||
var html = `<div><div style="font-size:16px;margin-left:12pt">points</div><div style="margin-left:1rem">points</div></div>`; | ||
var ret = htmlToPdfMake(html, {window:window}); | ||
if (debug) console.log(JSON.stringify(ret)); | ||
t.check(Array.isArray(ret) && ret.length===1, "return is OK"); | ||
ret = ret[0]; | ||
t.check(Array.isArray(ret.stack), "stack"); | ||
t.check( | ||
ret.stack[0].marginLeft===12 && | ||
ret.stack[0].fontSize===12 && | ||
ret.stack[1].marginLeft===12 | ||
, "convertUnit"); | ||
t.finish(); | ||
}) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
411
131912
12
1576