2

I am trying to make an application, which contains html tables.

I want a download button, and onClick event, downloads a PDF file containing all the html tables in it.

Can it be done?

Can someone help me with a function that converts the html table into an object so that it can be pushed into a PDF file through pdfmake?

2

3 Answers 3

10

I found a working solution over here: https://github.com/bpampuch/pdfmake/issues/205

function ParseContainer(cnt, e, p, styles) { var elements = []; var children = e.childNodes; if (children.length != 0) { for (var i = 0; i < children.length; i++) p = ParseElement(elements, children[i], p, styles); } if (elements.length != 0) { for (var i = 0; i < elements.length; i++) cnt.push(elements[i]); } return p; } function ComputeStyle(o, styles) { for (var i = 0; i < styles.length; i++) { var st = styles[i].trim().toLowerCase().split(":"); if (st.length == 2) { switch (st[0]) { case "font-size":{ o.fontSize = parseInt(st[1]); break; } case "text-align": { switch (st[1]) { case "right": o.alignment = 'right'; break; case "center": o.alignment = 'center'; break; } break; } case "font-weight": { switch (st[1]) { case "bold": o.bold = true; break; } break; } case "text-decoration": { switch (st[1]) { case "underline": o.decoration = "underline"; break; } break; } case "font-style": { switch (st[1]) { case "italic": o.italics = true; break; } break; } } } } } function ParseElement(cnt, e, p, styles) { if (!styles) styles = []; if (e.getAttribute) { var nodeStyle = e.getAttribute("style"); if (nodeStyle) { var ns = nodeStyle.split(";"); for (var k = 0; k < ns.length; k++) styles.push(ns[k]); } } switch (e.nodeName.toLowerCase()) { case "#text": { var t = { text: e.textContent.replace(/\n/g, "") }; if (styles) ComputeStyle(t, styles); p.text.push(t); break; } case "b":case "strong": { //styles.push("font-weight:bold"); ParseContainer(cnt, e, p, styles.concat(["font-weight:bold"])); break; } case "u": { //styles.push("text-decoration:underline"); ParseContainer(cnt, e, p, styles.concat(["text-decoration:underline"])); break; } case "i": { //styles.push("font-style:italic"); ParseContainer(cnt, e, p, styles.concat(["font-style:italic"])); //styles.pop(); break; //cnt.push({ text: e.innerText, bold: false }); } case "span": { ParseContainer(cnt, e, p, styles); break; } case "br": { p = CreateParagraph(); cnt.push(p); break; } case "table": { var t = { table: { widths: [], body: [] } } var border = e.getAttribute("border"); var isBorder = false; if (border) if (parseInt(border) == 1) isBorder = true; if (!isBorder) t.layout = 'noBorders'; ParseContainer(t.table.body, e, p, styles); var widths = e.getAttribute("widths"); if (!widths) { if (t.table.body.length != 0) { if (t.table.body[0].length != 0) for (var k = 0; k < t.table.body[0].length; k++) t.table.widths.push("*"); } } else { var w = widths.split(","); for (var k = 0; k < w.length; k++) t.table.widths.push(w[k]); } cnt.push(t); break; } case "tbody": { ParseContainer(cnt, e, p, styles); //p = CreateParagraph(); break; } case "tr": { var row = []; ParseContainer(row, e, p, styles); cnt.push(row); break; } case "td": { p = CreateParagraph(); var st = {stack: []} st.stack.push(p); var rspan = e.getAttribute("rowspan"); if (rspan) st.rowSpan = parseInt(rspan); var cspan = e.getAttribute("colspan"); if (cspan) st.colSpan = parseInt(cspan); ParseContainer(st.stack, e, p, styles); cnt.push(st); break; } case "div":case "p": { p = CreateParagraph(); var st = {stack: []} st.stack.push(p); ComputeStyle(st, styles); ParseContainer(st.stack, e, p); cnt.push(st); break; } default: { console.log("Parsing for node " + e.nodeName + " not found"); break; } } return p; } function ParseHtml(cnt, htmlText) { var html = $(htmlText.replace(/\t/g, "").replace(/\n/g, "")); var p = CreateParagraph(); for (var i = 0; i < html.length; i++) ParseElement(cnt, html.get(i), p); } function CreateParagraph() { var p = {text:[]}; return p; } //currently should be wraped in tag div or span var simpleHtm = "<div>"; simpleHtm += "This is <u>simple</u> html parser demo.<br>"; simpleHtm += "<p style='font-size:20px; text-align:center'>You can set font size and align from style</p>"; simpleHtm += "<table border='1'><tr><td>you</td><td>can</td></tr><tr> <td>use</td><td>tables</td></tr></table>" simpleHtm += "<table border='1' widths='30%,60%'><tr><td>or</td><td>set</td></tr><tr><td>table</td><td>width from html</td></tr></table><br>" simpleHtm += "<table border='1' widths='20%,50%'><tr><td>nested</td><td>table</td></tr><tr><td><table border='1'><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table></td><td></td></tr></table>" simpleHtm += "</div>"; content = []; ParseHtml(content, simpleHtm); pdfMake.createPdf({content: content}).download(); 

You can get the HTML code of any HTML element(I am demonstrating table) using the following code & call the ParseHTML function:

var simpleHtm = $('#TableID').prop('outerHTML'); ParseHtml(tablecontent, simpleHtm); 
Sign up to request clarification or add additional context in comments.

3 Comments

There was an update and here is a jsFidle with the actual working code above.
This is a good start to what I need to use pdfmake. Unfortunately this parser doesn't deal with colspan in tables. Has anyone done that part???
@mjwrazor I get this error 'Cannot read property '_span' of undefined'
4

Looking at the PDFMake getting started page, you have to format your table like this:

var docDefinition = { content: [{ table: { // headers are automatically repeated if the table spans over multiple pages // you can declare how many rows should be treated as headers headerRows: 1, widths: [ '*', 'auto', 100, '*' ], body: [ [ 'First', 'Second', 'Third', 'The last one' ], [ 'Value 1', 'Value 2', 'Value 3', 'Value 4' ], [ { text: 'Bold value', bold: true }, 'Val 2', 'Val 3', 'Val 4' ] ] } }] }; 

So you would take your table and get eh widths of each column and then cycle through the rows to create the body array:

function createTableDoc() { var table = document.getElementById("table"); var wdths; for (var i = 0; i < table.rows[0].cells.length; i++) { var cell = table.rows[0].cells[i]; widths[i] = (cell.style.width != ""? cell.style.width : cell.style.offsetWidth); //if the cell's style width is not set, get its' actual width } var bdy; for (var y = 0; y < table.rows.length; y++) for (var x = 0; x < table.rows[y].cells.length; x++) { body[y][x] = table.rows[y].cells[x].innerHTML; var docDef = { content: [{ table: { headerRows: 1, widths: wdths, body: bdy } }] }; return docDef; } 

3 Comments

Its giving me error that cannot set property '0' of undefined.
@vbuser2004 check the above accepted solution, it works perfectly fine :)
@TanujDhaundiyal - my bad! I missed that somehow. Deleted the original comment. Txs.
0

The full working solution from here. Try yourself running here the example. If you have a main container div with a id="test", with this set of functions you just need to call pdfForElement('test').download();

function pdfForElement(id) { function ParseContainer(cnt, e, p, styles) { var elements = []; var children = e.childNodes; if (children.length != 0) { for (var i = 0; i < children.length; i++) p = ParseElement(elements, children[i], p, styles); } if (elements.length != 0) { for (var i = 0; i < elements.length; i++) cnt.push(elements[i]); } return p; } function ComputeStyle(o, styles) { for (var i = 0; i < styles.length; i++) { var st = styles[i].trim().toLowerCase().split(":"); if (st.length == 2) { switch (st[0]) { case "font-size": { o.fontSize = parseInt(st[1]); break; } case "text-align": { switch (st[1]) { case "right": o.alignment = 'right'; break; case "center": o.alignment = 'center'; break; } break; } case "font-weight": { switch (st[1]) { case "bold": o.bold = true; break; } break; } case "text-decoration": { switch (st[1]) { case "underline": o.decoration = "underline"; break; } break; } case "font-style": { switch (st[1]) { case "italic": o.italics = true; break; } break; } } } } } function ParseElement(cnt, e, p, styles) { if (!styles) styles = []; if (e.getAttribute) { var nodeStyle = e.getAttribute("style"); if (nodeStyle) { var ns = nodeStyle.split(";"); for (var k = 0; k < ns.length; k++) styles.push(ns[k]); } } switch (e.nodeName.toLowerCase()) { case "#text": { var t = { text: e.textContent.replace(/\n/g, "") }; if (styles) ComputeStyle(t, styles); p.text.push(t); break; } case "b": case "strong": { //styles.push("font-weight:bold"); ParseContainer(cnt, e, p, styles.concat(["font-weight:bold"])); break; } case "u": { //styles.push("text-decoration:underline"); ParseContainer(cnt, e, p, styles.concat(["text-decoration:underline"])); break; } case "i": { //styles.push("font-style:italic"); ParseContainer(cnt, e, p, styles.concat(["font-style:italic"])); //styles.pop(); break; //cnt.push({ text: e.innerText, bold: false }); } case "span": { ParseContainer(cnt, e, p, styles); break; } case "br": { p = CreateParagraph(); cnt.push(p); break; } case "table": { var t = { table: { widths: [], body: [] } } var border = e.getAttribute("border"); var isBorder = false; if (border) if (parseInt(border) == 1) isBorder = true; if (!isBorder) t.layout = 'noBorders'; ParseContainer(t.table.body, e, p, styles); var widths = e.getAttribute("widths"); if (!widths) { if (t.table.body.length != 0) { if (t.table.body[0].length != 0) for (var k = 0; k < t.table.body[0].length; k++) t.table.widths.push("*"); } } else { var w = widths.split(","); for (var k = 0; k < w.length; k++) t.table.widths.push(w[k]); } cnt.push(t); break; } case "tbody": { ParseContainer(cnt, e, p, styles); //p = CreateParagraph(); break; } case "tr": { var row = []; ParseContainer(row, e, p, styles); cnt.push(row); break; } case "td": { p = CreateParagraph(); var st = { stack: [] } st.stack.push(p); var rspan = e.getAttribute("rowspan"); if (rspan) st.rowSpan = parseInt(rspan); var cspan = e.getAttribute("colspan"); if (cspan) st.colSpan = parseInt(cspan); ParseContainer(st.stack, e, p, styles); cnt.push(st); break; } case "div": case "p": { p = CreateParagraph(); var st = { stack: [] } st.stack.push(p); ComputeStyle(st, styles); ParseContainer(st.stack, e, p); cnt.push(st); break; } default: { console.log("Parsing for node " + e.nodeName + " not found"); break; } } return p; } function ParseHtml(cnt, htmlText) { var html = $(htmlText.replace(/\t/g, "").replace(/\n/g, "")); var p = CreateParagraph(); for (var i = 0; i < html.length; i++) ParseElement(cnt, html.get(i), p); } function CreateParagraph() { var p = { text: [] }; return p; } content = []; ParseHtml(content, document.getElementById(id).outerHTML); return pdfMake.createPdf({ content: content }); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/pdfmake.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/vfs_fonts.js"></script> <div id="test"> This is <u>simple</u> html parser demo. <br> <p style='font-size:20px; text-align:center'>You can set font size and align from style</p> <table border='1'> <tr> <td>you</td> <td>can</td> </tr> <tr> <td>use</td> <td>tables</td> </tr> </table> <table border='1' widths='30%,60%'> <tr> <td>or</td> <td>set</td> </tr> <tr> <td>table</td> <td>width from html</td> </tr> </table> <br> <table border='1' widths='20%,50%'> <tr> <td>nested</td> <td>table</td> </tr> <tr> <td> <table border='1'> <tr> <td>1</td> <td>2</td> </tr> <tr> <td>3</td> <td>4</td> </tr> </table> </td> <td></td> </tr> </table> </div> <br> <input type="button" value="Download PDF" onclick="pdfForElement('test').download();">

1 Comment

@Spikolynn it was long time ago, so I don't remember, but I quickly see a difference: you have a full working snippet so that you can test

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.