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>