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<columns; i++) {
td = document.createElement('td');
td.setAttribute('style', cell_style);
tr.appendChild(td);
}
return(table);
}
/**
* Webkit-only solution to get the CSS rules applied to a node (not the computed ones)
*/
function getAppliedCSS(node) {
var css = ''
var style_attribute = node.getAttribute('style');
if (style_attribute != null)
css += style_attribute;
var rules = node.ownerDocument.defaultView.getMatchedCSSRules(node, '');
if (rules != null) {
var i = rules.length;
while (i--) {
css += rules[i].style.cssText + ';';
}
}
if (css == '')
return null;
return css;
}
/**
* Returns the value of a CSS attribute from the node style attribute, or null if not defined.
*/
function style_value(node, name) {
var style_attribute = node.getAttribute('style');
if (style_attribute == null)
return(null);
style_attribute = style_attribute.replace(/\s/, '');
var css_pairs = style_attribute.split(';');
for (var i=0; i<css_pairs.length; i++) {
var name_value = css_pairs[i].split(':');
if (name_value.length == 2 && name_value[0] == name)
return(name_value[1]);
}
}
/**
* Split the node if it can be, and return true if it was split.
*/
function split_node(node, nodes, i, appliedCSS) {
var i,j;
var child, little_divs, little_div, next_child, future_divs;
// check if there is something to split
future_divs = 0;
for (child=node.firstChild; child != null; child=child.nextSibling) {
if (child.nodeType == 3 && child.nodeValue.trim() == '') {
continue;
}
if (future_divs == 0 || child.offsetHeight > 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<nodes.length; i++) {
node = nodes[i];
if (node.parentNode != null)
node.parentNode.removeChild(node);
td.appendChild(node);
if (node.nodeType == 1) {
computed_style = window.getComputedStyle(node);
appliedCSS = getAppliedCSS(node);
} else {
computed_style = null;
appliedCSS = null;
}
page_break_before = page_break_before || (computed_style != null &&
(computed_style.getPropertyValue('page-break-before') == 'always' ||
computed_style.getPropertyValue('break-before') == 'page' ||
style_value(node, 'break-before') == 'page'));
if (computed_style != null && page_break_before)
node.style.pageBreakBefore = 'auto';
if (!page_break_before && node.nodeName == 'DIV' && node.firstChild != null) {
// look for a page break before in the first child of a div
child_computed_style = window.getComputedStyle(node.firstChild);
if (child_computed_style != null && (
child_computed_style.getPropertyValue('page-break-before') == 'always' ||
child_computed_style.getPropertyValue('break-before') == 'page' ||
style_value(node.firstChild, 'break-before') == 'page')) {
page_break_before = true;
node.firstChild.style.pageBreakBefore = 'auto';
}
}
column_break_before = column_break_before || (computed_style != null &&
computed_style.getPropertyValue('break-before') == 'column');
// unfortunately, browsers often do not support break-before, so we need to look at the style attribute directly
// (this means CSS in .css files will be ignored for break-before)
if (!column_break_before && node.nodeType == 1) {
if (style_value(node, 'break-before') == 'column')
column_break_before = true;
}
if (!column_break_before && node.nodeName == 'DIV' && node.firstChild != null && node.firstChild.nodeType == 1) {
// look for a column break before in the first child of a div
child_computed_style = window.getComputedStyle(node.firstChild);
if ((child_computed_style != null && child_computed_style.getPropertyValue('break-before') == 'column') ||
style_value(node.firstChild, 'break-before') == 'column') {
column_break_before = true;
}
}
avoid_page_break_inside = (computed_style != null &&
computed_style.getPropertyValue('page-break-inside') == 'avoid');
table_height = table.offsetHeight;
if (table_height > 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
});
}();
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>