Annotation of modules/damieng/wkhtmltopdf_test/print_column_layout.js, revision 1.1
1.1 ! damieng 1: var LCPRINT = function () {
! 2:
! 3: var paper_width = 11; // in inches
! 4: var paper_height = 8.5;
! 5:
! 6: /**
! 7: * Sets paper width and height, in inches.
! 8: */
! 9: function set_paper_size(width, height) {
! 10: paper_width = width;
! 11: paper_height = height;
! 12: }
! 13:
! 14: /**
! 15: * Returns a new table element with one row and the given number of columns, using given style for td.
! 16: */
! 17: function new_table(columns, cell_style) {
! 18: var table, tr, td, i;
! 19: table = document.createElement('table');
! 20: table.setAttribute('class', 'page-table');
! 21: document.body.appendChild(table);
! 22: tr = document.createElement('tr');
! 23: table.appendChild(tr);
! 24: for (i=0; i<columns; i++) {
! 25: td = document.createElement('td');
! 26: td.setAttribute('style', cell_style);
! 27: tr.appendChild(td);
! 28: }
! 29: return(table);
! 30: }
! 31:
! 32: /**
! 33: * Webkit-only solution to get the CSS rules applied to a node (not the computed ones)
! 34: */
! 35: function getAppliedCSS(node) {
! 36: var css = ''
! 37: var style_attribute = node.getAttribute('style');
! 38: if (style_attribute != null)
! 39: css += style_attribute;
! 40: var rules = node.ownerDocument.defaultView.getMatchedCSSRules(node, '');
! 41: if (rules != null) {
! 42: var i = rules.length;
! 43: while (i--) {
! 44: css += rules[i].style.cssText + ';';
! 45: }
! 46: }
! 47: if (css == '')
! 48: return null;
! 49: return css;
! 50: }
! 51:
! 52: /**
! 53: * Returns the value of a CSS attribute from the node style attribute, or null if not defined.
! 54: */
! 55: function style_value(node, name) {
! 56: var style_attribute = node.getAttribute('style');
! 57: if (style_attribute == null)
! 58: return(null);
! 59: style_attribute = style_attribute.replace(/\s/, '');
! 60: var css_pairs = style_attribute.split(';');
! 61: for (var i=0; i<css_pairs.length; i++) {
! 62: var name_value = css_pairs[i].split(':');
! 63: if (name_value.length == 2 && name_value[0] == name)
! 64: return(name_value[1]);
! 65: }
! 66: }
! 67:
! 68: /**
! 69: * Split the node if it can be, and return true if it was split.
! 70: */
! 71: function split_node(node, nodes, i, appliedCSS) {
! 72: var i,j;
! 73: var child, little_divs, little_div, next_child, future_divs;
! 74: // check if there is something to split
! 75: future_divs = 0;
! 76: for (child=node.firstChild; child != null; child=child.nextSibling) {
! 77: if (child.nodeType == 3 && child.nodeValue.trim() == '') {
! 78: continue;
! 79: }
! 80: if (future_divs == 0 || child.offsetHeight > 0) {
! 81: future_divs++;
! 82: }
! 83: }
! 84: if (future_divs > 1) {
! 85:
! 86: // split the div into little divs with the same style
! 87: little_divs = [];
! 88: little_div = null;
! 89: for (child=node.firstChild; child != null; child=next_child) {
! 90: //console.log(child);
! 91: next_child = child.nextSibling;
! 92: if (child.nodeType == 3 && child.nodeValue.trim() == '') {
! 93: continue;
! 94: }
! 95: if (little_div == null || child.offsetHeight > 0) {
! 96: // only create another div if the child has a height, otherwise keep it with previous div
! 97: little_div = document.createElement('div');
! 98: if (appliedCSS != null)
! 99: little_div.style.cssText = appliedCSS;
! 100: little_divs.push(little_div);
! 101: }
! 102: node.removeChild(child);
! 103: little_div.appendChild(child);
! 104: }
! 105: nodes.splice(i, 1);
! 106: for (j=little_divs.length-1; j >= 0; j--) {
! 107: nodes.splice(i, 0, little_divs[j]);
! 108: }
! 109: return true;
! 110: }
! 111: return false;
! 112: }
! 113:
! 114: /**
! 115: * Changes layout to given number of columns, and changes window status to "done".
! 116: * Should only be called once, after page loading.
! 117: */
! 118: function layout(columns) {
! 119: console.log('column layout...');
! 120: // This function puts all the body children in tables, one table for each page.
! 121: // It inserts each node in the current cell, and checks if the table extends beyond the page height.
! 122: // If it does, it switches to the next cell in the table or the next table and moves the node there.
! 123: // It works with CSS "page-break-before: always" and "page-break-after: always", but only at the top level.
! 124: // For column break, use "break-before: column" or "break-after: column" on a top level element style attribute.
! 125: // top-level div elements without "page-break-inside: avoid" can now be split.
! 126: var i;
! 127: var nodes = [];
! 128: var node;
! 129: for (node=document.body.firstChild; node != null; node=node.nextSibling)
! 130: nodes.push(node);
! 131: var page_height = paper_width * 96; // 96 dpi
! 132: var table_height;
! 133: var body_width = paper_height * 96;
! 134: var cell_width = body_width/columns;
! 135: var cell_style = 'min-width:'+(cell_width-1)+'px; max-width:'+cell_width+'px';
! 136: var table = new_table(columns, cell_style);
! 137: var td = table.firstChild.firstChild;
! 138: var computed_style, appliedCSS, child_computed_style;
! 139: var page_break_before = false;
! 140: var column_break_before = false;
! 141: var avoid_page_break_inside = false;
! 142: for (i=0; i<nodes.length; i++) {
! 143: node = nodes[i];
! 144: if (node.parentNode != null)
! 145: node.parentNode.removeChild(node);
! 146: td.appendChild(node);
! 147: if (node.nodeType == 1) {
! 148: computed_style = window.getComputedStyle(node);
! 149: appliedCSS = getAppliedCSS(node);
! 150: } else {
! 151: computed_style = null;
! 152: appliedCSS = null;
! 153: }
! 154: page_break_before = page_break_before || (computed_style != null &&
! 155: (computed_style.getPropertyValue('page-break-before') == 'always' ||
! 156: computed_style.getPropertyValue('break-before') == 'page' ||
! 157: style_value(node, 'break-before') == 'page'));
! 158: if (computed_style != null && page_break_before)
! 159: node.style.pageBreakBefore = 'auto';
! 160: if (!page_break_before && node.nodeName == 'DIV' && node.firstChild != null) {
! 161: // look for a page break before in the first child of a div
! 162: child_computed_style = window.getComputedStyle(node.firstChild);
! 163: if (child_computed_style != null && (
! 164: child_computed_style.getPropertyValue('page-break-before') == 'always' ||
! 165: child_computed_style.getPropertyValue('break-before') == 'page' ||
! 166: style_value(node.firstChild, 'break-before') == 'page')) {
! 167: page_break_before = true;
! 168: node.firstChild.style.pageBreakBefore = 'auto';
! 169: }
! 170: }
! 171: column_break_before = column_break_before || (computed_style != null &&
! 172: computed_style.getPropertyValue('break-before') == 'column');
! 173: // unfortunately, browsers often do not support break-before, so we need to look at the style attribute directly
! 174: // (this means CSS in .css files will be ignored for break-before)
! 175: if (!column_break_before && node.nodeType == 1) {
! 176: if (style_value(node, 'break-before') == 'column')
! 177: column_break_before = true;
! 178: }
! 179: if (!column_break_before && node.nodeName == 'DIV' && node.firstChild != null && node.firstChild.nodeType == 1) {
! 180: // look for a column break before in the first child of a div
! 181: child_computed_style = window.getComputedStyle(node.firstChild);
! 182: if ((child_computed_style != null && child_computed_style.getPropertyValue('break-before') == 'column') ||
! 183: style_value(node.firstChild, 'break-before') == 'column') {
! 184: column_break_before = true;
! 185: }
! 186: }
! 187: avoid_page_break_inside = (computed_style != null &&
! 188: computed_style.getPropertyValue('page-break-inside') == 'avoid');
! 189: table_height = table.offsetHeight;
! 190: if (table_height > page_height || page_break_before || column_break_before) {
! 191: if (!page_break_before && !column_break_before && !avoid_page_break_inside &&
! 192: node.nodeName == 'DIV' && node.childNodes.length > 1) {
! 193: if (split_node(node, nodes, i, appliedCSS)) {
! 194: td.removeChild(node);
! 195: i--;
! 196: page_break_before = false;
! 197: column_break_before = false;
! 198: continue;
! 199: }
! 200: }
! 201: td.removeChild(node);
! 202: if (td.nextSibling == null || page_break_before) {
! 203: table = new_table(columns, cell_style);
! 204: td = table.firstChild.firstChild;
! 205: } else {
! 206: td = td.nextSibling;
! 207: }
! 208: td.appendChild(node);
! 209: }
! 210: page_break_before = (computed_style != null && (computed_style.getPropertyValue('page-break-after') == 'always' ||
! 211: computed_style.getPropertyValue('break-after') == 'page' ||
! 212: style_value(node, 'break-after') == 'page'));
! 213: if (page_break_before)
! 214: node.style.pageBreakAfter = 'auto';
! 215: if (!page_break_before && node.nodeName == 'DIV' && node.lastChild != null) {
! 216: // look for a page break after in the last child of a div
! 217: child_computed_style = window.getComputedStyle(node.lastChild);
! 218: if (child_computed_style != null && (
! 219: child_computed_style.getPropertyValue('page-break-after') == 'always' ||
! 220: child_computed_style.getPropertyValue('break-after') == 'page' ||
! 221: style_value(node.lastChild, 'break-after') == 'page')) {
! 222: page_break_before = true;
! 223: node.lastChild.style.pageBreakBefore = 'auto';
! 224: }
! 225: }
! 226: column_break_before = (computed_style != null && computed_style.getPropertyValue('break-after') == 'column');
! 227: if (!column_break_before && node.nodeType == 1) {
! 228: if (style_value(node, 'break-after') == 'column')
! 229: column_break_before = true;
! 230: }
! 231: if (!column_break_before && node.nodeName == 'DIV' && node.lastChild != null) {
! 232: // look for a column break after in the last child of a div
! 233: child_computed_style = window.getComputedStyle(node.lastChild);
! 234: if (child_computed_style != null && (
! 235: child_computed_style.getPropertyValue('break-after') == 'column' ||
! 236: style_value(node.lastChild, 'break-after') == 'column')) {
! 237: column_break_before = true;
! 238: }
! 239: }
! 240: }
! 241: console.log('layout done');
! 242: window.status = "done";
! 243: }
! 244:
! 245: return({
! 246: "set_paper_size": set_paper_size,
! 247: "layout": layout
! 248: });
! 249: }();
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>