var LCPRINT = function () { var paper_width = 11; // in inches var paper_height = 8.5; /** * Sets paper width and height, in inches. */ function set_paper_size(width, height) { paper_width = width; paper_height = height; } /** * Returns a new table element with one row and the given number of columns, using given style for td. */ function new_table(columns, cell_style) { var table, tr, td, i; table = document.createElement('table'); table.setAttribute('class', 'page-table'); document.body.appendChild(table); tr = document.createElement('tr'); table.appendChild(tr); for (i=0; i 0) { future_divs++; } } if (future_divs > 1) { // split the div into little divs with the same style little_divs = []; little_div = null; for (child=node.firstChild; child != null; child=next_child) { //console.log(child); next_child = child.nextSibling; if (child.nodeType == 3 && child.nodeValue.trim() == '') { continue; } if (little_div == null || child.offsetHeight > 0) { // only create another div if the child has a height, otherwise keep it with previous div little_div = document.createElement('div'); if (appliedCSS != null) little_div.style.cssText = appliedCSS; little_divs.push(little_div); } node.removeChild(child); little_div.appendChild(child); } nodes.splice(i, 1); for (j=little_divs.length-1; j >= 0; j--) { nodes.splice(i, 0, little_divs[j]); } return true; } return false; } /** * Changes layout to given number of columns, and changes window status to "done". * Should only be called once, after page loading. */ function layout(columns) { console.log('column layout...'); // This function puts all the body children in tables, one table for each page. // It inserts each node in the current cell, and checks if the table extends beyond the page height. // If it does, it switches to the next cell in the table or the next table and moves the node there. // It works with CSS "page-break-before: always" and "page-break-after: always", but only at the top level. // For column break, use "break-before: column" or "break-after: column" on a top level element style attribute. // top-level div elements without "page-break-inside: avoid" can now be split. var i; var nodes = []; var node; for (node=document.body.firstChild; node != null; node=node.nextSibling) nodes.push(node); var page_height = paper_width * 96; // 96 dpi var table_height; var body_width = paper_height * 96; var cell_width = body_width/columns; var cell_style = 'min-width:'+(cell_width-1)+'px; max-width:'+cell_width+'px'; var table = new_table(columns, cell_style); var td = table.firstChild.firstChild; var computed_style, appliedCSS, child_computed_style; var page_break_before = false; var column_break_before = false; var avoid_page_break_inside = false; for (i=0; i page_height || page_break_before || column_break_before) { if (!page_break_before && !column_break_before && !avoid_page_break_inside && node.nodeName == 'DIV' && node.childNodes.length > 1) { if (split_node(node, nodes, i, appliedCSS)) { td.removeChild(node); i--; page_break_before = false; column_break_before = false; continue; } } td.removeChild(node); if (td.nextSibling == null || page_break_before) { table = new_table(columns, cell_style); td = table.firstChild.firstChild; } else { td = td.nextSibling; } td.appendChild(node); } page_break_before = (computed_style != null && (computed_style.getPropertyValue('page-break-after') == 'always' || computed_style.getPropertyValue('break-after') == 'page' || style_value(node, 'break-after') == 'page')); if (page_break_before) node.style.pageBreakAfter = 'auto'; if (!page_break_before && node.nodeName == 'DIV' && node.lastChild != null) { // look for a page break after in the last child of a div child_computed_style = window.getComputedStyle(node.lastChild); if (child_computed_style != null && ( child_computed_style.getPropertyValue('page-break-after') == 'always' || child_computed_style.getPropertyValue('break-after') == 'page' || style_value(node.lastChild, 'break-after') == 'page')) { page_break_before = true; node.lastChild.style.pageBreakBefore = 'auto'; } } column_break_before = (computed_style != null && computed_style.getPropertyValue('break-after') == 'column'); if (!column_break_before && node.nodeType == 1) { if (style_value(node, 'break-after') == 'column') column_break_before = true; } if (!column_break_before && node.nodeName == 'DIV' && node.lastChild != null) { // look for a column break after in the last child of a div child_computed_style = window.getComputedStyle(node.lastChild); if (child_computed_style != null && ( child_computed_style.getPropertyValue('break-after') == 'column' || style_value(node.lastChild, 'break-after') == 'column')) { column_break_before = true; } } } console.log('layout done'); window.status = "done"; } return({ "set_paper_size": set_paper_size, "layout": layout }); }();