function PdfPrinter(fontDescriptors) { this.fontDescriptors = fontDescriptors; }
n/a
function DocMeasure(fontProvider, styleDictionary, defaultStyle, imageMeasure, tableLayouts, images) { this.textTools = new TextTools(fontProvider); this.styleStack = new StyleContextStack(styleDictionary, defaultStyle); this.imageMeasure = imageMeasure; this.tableLayouts = tableLayouts; this.images = images; this.autoImageIndex = 1; }
n/a
function DocumentContext(pageSize, pageMargins) { this.pages = []; this.pageMargins = pageMargins; this.x = pageMargins.left; this.availableWidth = pageSize.width - pageMargins.left - pageMargins.right; this.availableHeight = 0; this.page = -1; this.snapshots = []; this.endingCell = null; this.tracker = new TraversalTracker(); this.addPage(pageSize); }
n/a
function ElementWriter(context, tracker) { this.context = context; this.contextStack = []; this.tracker = tracker; }
n/a
function FontProvider(fontDescriptors, pdfKitDoc) { this.fonts = {}; this.pdfKitDoc = pdfKitDoc; this.fontCache = {}; for (var font in fontDescriptors) { if (fontDescriptors.hasOwnProperty(font)) { var fontDef = fontDescriptors[font]; this.fonts[font] = { normal: fontDef.normal, bold: fontDef.bold, italics: fontDef.italics, bolditalics: fontDef.bolditalics }; } } }
n/a
function ImageMeasure(pdfKitDoc, imageDictionary) { this.pdfKitDoc = pdfKitDoc; this.imageDictionary = imageDictionary || {}; }
n/a
function LayoutBuilder(pageSize, pageMargins, imageMeasure) { this.pageSize = pageSize; this.pageMargins = pageMargins; this.tracker = new TraversalTracker(); this.imageMeasure = imageMeasure; this.tableLayouts = {}; }
n/a
function Line(maxWidth) { this.maxWidth = maxWidth; this.leadingCut = 0; this.trailingCut = 0; this.inlineWidths = 0; this.inlines = []; }
n/a
function PageElementWriter(context, tracker) { this.transactionLevel = 0; this.repeatables = []; this.tracker = tracker; this.writer = new ElementWriter(context, tracker); }
n/a
function StyleContextStack(styleDictionary, defaultStyle) { this.defaultStyle = defaultStyle || {}; this.styleDictionary = styleDictionary; this.styleOverrides = []; }
n/a
function TableProcessor(tableNode) { this.tableNode = tableNode; }
n/a
function TextTools(fontProvider) { this.fontProvider = fontProvider; }
n/a
function TraversalTracker() { this.events = {}; }
n/a
function buildColumnWidths(columns, availableWidth) { var autoColumns = [], autoMin = 0, autoMax = 0, starColumns = [], starMaxMin = 0, starMaxMax = 0, fixedColumns = [], initial_availableWidth = availableWidth; columns.forEach(function (column) { if (isAutoColumn(column)) { autoColumns.push(column); autoMin += column._minWidth; autoMax += column._maxWidth; } else if (isStarColumn(column)) { starColumns.push(column); starMaxMin = Math.max(starMaxMin, column._minWidth); starMaxMax = Math.max(starMaxMax, column._maxWidth); } else { fixedColumns.push(column); } }); fixedColumns.forEach(function (col) { // width specified as % if (typeof col.width === 'string' && /\d+%/.test(col.width)) { col.width = parseFloat(col.width) * initial_availableWidth / 100; } if (col.width < (col._minWidth) && col.elasticWidth) { col._calcWidth = col._minWidth; } else { col._calcWidth = col.width; } availableWidth -= col._calcWidth; }); // http://www.freesoft.org/CIE/RFC/1942/18.htm // http://www.w3.org/TR/CSS2/tables.html#width-layout // http://dev.w3.org/csswg/css3-tables-algorithms/Overview.src.htm var minW = autoMin + starMaxMin * starColumns.length; var maxW = autoMax + starMaxMax * starColumns.length; if (minW >= availableWidth) { // case 1 - there's no way to fit all columns within available width // that's actually pretty bad situation with PDF as we have no horizontal scroll // no easy workaround (unless we decide, in the future, to split single words) // currently we simply use minWidths for all columns autoColumns.forEach(function (col) { col._calcWidth = col._minWidth; }); starColumns.forEach(function (col) { col._calcWidth = starMaxMin; // starMaxMin already contains padding }); } else { if (maxW < availableWidth) { // case 2 - we can fit rest of the table within available space autoColumns.forEach(function (col) { col._calcWidth = col._maxWidth; availableWidth -= col._calcWidth; }); } else { // maxW is too large, but minW fits within available width var W = availableWidth - minW; var D = maxW - minW; autoColumns.forEach(function (col) { var d = col._maxWidth - col._minWidth; col._calcWidth = col._minWidth + d * W / D; availableWidth -= col._calcWidth; }); } if (starColumns.length > 0) { var starSize = availableWidth / starColumns.length; starColumns.forEach(function (col) { col._calcWidth = starSize; }); } } }
...
var availableWidth = this.writer.context().availableWidth;
var gaps = gapArray(columnNode._gap);
if (gaps) {
availableWidth -= (gaps.length - 1) * columnNode._gap;
}
ColumnCalculator.buildColumnWidths(columns, availableWidth);
var result = this.processRow(columns, columns, gaps);
addAll(columnNode.positions, result.positions);
function gapArray(gap) {
if (!gap) {
return null;
...
function isAutoColumn(column) { return column.width === 'auto'; }
n/a
function isStarColumn(column) { return column.width === null || column.width === undefined || column.width === '*' || column.width === 'star'; }
n/a
function measureMinMax(columns) { var result = {min: 0, max: 0}; var maxStar = {min: 0, max: 0}; var starCount = 0; for (var i = 0, l = columns.length; i < l; i++) { var c = columns[i]; if (isStarColumn(c)) { maxStar.min = Math.max(maxStar.min, c._minWidth); maxStar.max = Math.max(maxStar.max, c._maxWidth); starCount++; } else if (isAutoColumn(c)) { result.min += c._minWidth; result.max += c._maxWidth; } else { result.min += ((c.width !== undefined && c.width) || c._minWidth); result.max += ((c.width !== undefined && c.width) || c._maxWidth); } } if (starCount) { result.min += starCount * maxStar.min; result.max += starCount * maxStar.max; } return result; }
...
var columns = node.columns;
node._gap = this.styleStack.getProperty('columnGap') || 0;
for (var i = 0, l = columns.length; i < l; i++) {
columns[i] = this.measureNode(columns[i]);
}
var measures = ColumnCalculator.measureMinMax(columns);
node._minWidth = measures.min + node._gap * (columns.length - 1);
node._maxWidth = measures.max + node._gap * (columns.length - 1);
return node;
};
...
function DocMeasure(fontProvider, styleDictionary, defaultStyle, imageMeasure, tableLayouts, images) { this.textTools = new TextTools(fontProvider); this.styleStack = new StyleContextStack(styleDictionary, defaultStyle); this.imageMeasure = imageMeasure; this.tableLayouts = tableLayouts; this.images = images; this.autoImageIndex = 1; }
n/a
buildOrderedMarker = function (counter, styleStack, type, separator) { function prepareAlpha(counter) { function toAlpha(num) { return (num >= 26 ? toAlpha((num / 26 >> 0) - 1) : '') + 'abcdefghijklmnopqrstuvwxyz'[num % 26 >> 0]; } if (counter < 1) { return counter.toString(); } return toAlpha(counter - 1); } function prepareRoman(counter) { if (counter < 1 || counter > 4999) { return counter.toString(); } var num = counter; var lookup = {M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1}, roman = '', i; for (i in lookup) { while (num >= lookup[i]) { roman += i; num -= lookup[i]; } } return roman; } function prepareDecimal(counter) { return counter.toString(); } var counterText; switch (type) { case 'none': counterText = null; break; case 'upper-alpha': counterText = prepareAlpha(counter).toUpperCase(); break; case 'lower-alpha': counterText = prepareAlpha(counter); break; case 'upper-roman': counterText = prepareRoman(counter); break; case 'lower-roman': counterText = prepareRoman(counter).toLowerCase(); break; case 'decimal': default: counterText = prepareDecimal(counter); break; } if (counterText === null) { return {}; } if (separator) { if (Array.isArray(separator)) { if (separator[0]) { counterText = separator[0] + counterText; } if (separator[1]) { counterText += separator[1]; } counterText += ' '; } else { counterText += separator + ' '; } } var textArray = {text: counterText}; var markerColor = styleStack.getProperty('markerColor'); if (markerColor) { textArray.color = markerColor; } return {_inlines: this.textTools.buildInlines(textArray, styleStack).items}; }
...
node._maxWidth = 0;
var counter = node.start;
for (var i = 0, l = items.length; i < l; i++) {
var item = items[i] = this.measureNode(items[i]);
if (!item.ol && !item.ul) {
item.listMarker = this.buildOrderedMarker(item.counter || counter, style, node.type
, node.separator);
if (item.listMarker._inlines) {
node._gapSize.width = Math.max(node._gapSize.width, item.listMarker._inlines[0].width);
}
} // TODO: else - nested lists numbering
node._minWidth = Math.max(node._minWidth, items[i]._minWidth);
node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth);
...
buildUnorderedMarker = function (styleStack, gapSize, type) { function buildDisc(gapSize, color) { // TODO: ascender-based calculations var radius = gapSize.fontSize / 6; return { canvas: [{ x: radius, y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - gapSize.fontSize / 3, r1: radius, r2: radius, type: 'ellipse', color: color }] }; } function buildSquare(gapSize, color) { // TODO: ascender-based calculations var size = gapSize.fontSize / 3; return { canvas: [{ x: 0, y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - (gapSize.fontSize / 3) - (size / 2), h: size, w: size, type: 'rect', color: color }] }; } function buildCircle(gapSize, color) { // TODO: ascender-based calculations var radius = gapSize.fontSize / 6; return { canvas: [{ x: radius, y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - gapSize.fontSize / 3, r1: radius, r2: radius, type: 'ellipse', lineColor: color }] }; } var marker; var color = styleStack.getProperty('markerColor') || styleStack.getProperty('color') || 'black'; switch (type) { case 'circle': marker = buildCircle(gapSize, color); break; case 'square': marker = buildSquare(gapSize, color); break; case 'none': marker = {}; break; case 'disc': default: marker = buildDisc(gapSize, color); break; } marker._minWidth = marker._maxWidth = gapSize.width; marker._minHeight = marker._maxHeight = gapSize.height; return marker; }
...
node._minWidth = 0;
node._maxWidth = 0;
for (var i = 0, l = items.length; i < l; i++) {
var item = items[i] = this.measureNode(items[i]);
if (!item.ol && !item.ul) {
item.listMarker = this.buildUnorderedMarker(style, node._gapSize, node.type);
}
node._minWidth = Math.max(node._minWidth, items[i]._minWidth + node._gapSize.width);
node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth + node._gapSize.width);
}
return node;
...
convertIfBase64Image = function (node) { if (/^data:image\/(jpeg|jpg|png);base64,/.test(node.image)) { var label = '$$pdfmake$$' + this.autoImageIndex++; this.images[label] = node.image; node.image = label; } }
...
this.images[label] = node.image;
node.image = label;
}
};
DocMeasure.prototype.measureImage = function (node) {
if (this.images) {
this.convertIfBase64Image(node);
}
var imageSize = this.imageMeasure.measureImage(node.image);
if (node.fit) {
var factor = (imageSize.width / imageSize.height > node.fit[0] / node.fit[1]) ? node.fit[0] / imageSize.width : node.fit[
1] / imageSize.height;
node._width = node._minWidth = node._maxWidth = imageSize.width * factor;
...
gapSizeForList = function () { return this.textTools.sizeOfString('9. ', this.styleStack); }
...
return {_inlines: this.textTools.buildInlines(textArray, styleStack).items};
};
DocMeasure.prototype.measureUnorderedList = function (node) {
var style = this.styleStack.clone();
var items = node.ul;
node.type = node.type || 'disc';
node._gapSize = this.gapSizeForList();
node._minWidth = 0;
node._maxWidth = 0;
for (var i = 0, l = items.length; i < l; i++) {
var item = items[i] = this.measureNode(items[i]);
if (!item.ol && !item.ul) {
...
measureCanvas = function (node) { var w = 0, h = 0; for (var i = 0, l = node.canvas.length; i < l; i++) { var vector = node.canvas[i]; switch (vector.type) { case 'ellipse': w = Math.max(w, vector.x + vector.r1); h = Math.max(h, vector.y + vector.r2); break; case 'rect': w = Math.max(w, vector.x + vector.w); h = Math.max(h, vector.y + vector.h); break; case 'line': w = Math.max(w, vector.x1, vector.x2); h = Math.max(h, vector.y1, vector.y2); break; case 'polyline': for (var i2 = 0, l2 = vector.points.length; i2 < l2; i2++) { w = Math.max(w, vector.points[i2].x); h = Math.max(h, vector.points[i2].y); } break; } } node._minWidth = node._maxWidth = w; node._minHeight = node._maxHeight = h; return node; }
...
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
return extendMargins(self.measureImage(node));
} else if (node.canvas) {
return extendMargins(self.measureCanvas(node));
} else if (node.qr) {
return extendMargins(self.measureQr(node));
} else {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
});
...
measureColumns = function (node) { var columns = node.columns; node._gap = this.styleStack.getProperty('columnGap') || 0; for (var i = 0, l = columns.length; i < l; i++) { columns[i] = this.measureNode(columns[i]); } var measures = ColumnCalculator.measureMinMax(columns); node._minWidth = measures.min + node._gap * (columns.length - 1); node._maxWidth = measures.max + node._gap * (columns.length - 1); return node; }
...
var self = this;
return this.styleStack.auto(node, function () {
// TODO: refactor + rethink whether this is the proper way to handle margins
node._margin = getNodeMargin(node);
if (node.columns) {
return extendMargins(self.measureColumns(node));
} else if (node.stack) {
return extendMargins(self.measureVerticalContainer(node));
} else if (node.ul) {
return extendMargins(self.measureUnorderedList(node));
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
...
measureDocument = function (docStructure) { return this.measureNode(docStructure); }
...
return result.pages;
};
LayoutBuilder.prototype.tryLayoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header
, footer, images, watermark, pageBreakBeforeFct) {
this.linearNodeList = [];
docStructure = this.docMeasure.measureDocument(docStructure);
this.writer = new PageElementWriter(
new DocumentContext(this.pageSize, this.pageMargins), this.tracker);
var _this = this;
this.writer.context().tracker.startTracking('pageAdded', function () {
_this.addBackground(background);
...
measureImage = function (node) { if (this.images) { this.convertIfBase64Image(node); } var imageSize = this.imageMeasure.measureImage(node.image); if (node.fit) { var factor = (imageSize.width / imageSize.height > node.fit[0] / node.fit[1]) ? node.fit[0] / imageSize.width : node.fit[1] / imageSize.height; node._width = node._minWidth = node._maxWidth = imageSize.width * factor; node._height = imageSize.height * factor; } else { node._width = node._minWidth = node._maxWidth = node.width || imageSize.width; node._height = node.height || (imageSize.height * node._width / imageSize.width); } node._alignment = this.styleStack.getProperty('alignment'); return node; }
...
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
return extendMargins(self.measureImage(node));
} else if (node.canvas) {
return extendMargins(self.measureCanvas(node));
} else if (node.qr) {
return extendMargins(self.measureQr(node));
} else {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
...
measureLeaf = function (node) { // Make sure style properties of the node itself are considered when building inlines. // We could also just pass [node] to buildInlines, but that fails for bullet points. var styleStack = this.styleStack.clone(); styleStack.push(node); var data = this.textTools.buildInlines(node.text, styleStack); node._inlines = data.items; node._minWidth = data.minWidth; node._maxWidth = data.maxWidth; return node; }
...
} else if (node.ul) {
return extendMargins(self.measureUnorderedList(node));
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
return extendMargins(self.measureImage(node));
} else if (node.canvas) {
return extendMargins(self.measureCanvas(node));
} else if (node.qr) {
return extendMargins(self.measureQr(node));
} else {
...
measureNode = function (node) { // expand shortcuts if (Array.isArray(node)) { node = {stack: node}; } else if (typeof node === 'string' || node instanceof String) { node = {text: node}; } else if (typeof node === 'number' || typeof node === 'boolean') { node = {text: node.toString()}; } else if (node === null) { node = {text: ''}; } // Deal with empty nodes to prevent crash in getNodeMargin if (Object.keys(node).length === 0) { // A warning could be logged: console.warn('pdfmake: Empty node, ignoring it'); node = {text: ''}; } var self = this; return this.styleStack.auto(node, function () { // TODO: refactor + rethink whether this is the proper way to handle margins node._margin = getNodeMargin(node); if (node.columns) { return extendMargins(self.measureColumns(node)); } else if (node.stack) { return extendMargins(self.measureVerticalContainer(node)); } else if (node.ul) { return extendMargins(self.measureUnorderedList(node)); } else if (node.ol) { return extendMargins(self.measureOrderedList(node)); } else if (node.table) { return extendMargins(self.measureTable(node)); } else if (node.text !== undefined) { return extendMargins(self.measureLeaf(node)); } else if (node.image) { return extendMargins(self.measureImage(node)); } else if (node.canvas) { return extendMargins(self.measureCanvas(node)); } else if (node.qr) { return extendMargins(self.measureQr(node)); } else { throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify); } }); function extendMargins(node) { var margin = node._margin; if (margin) { node._minWidth += margin[0] + margin[2]; node._maxWidth += margin[0] + margin[2]; } return node; } function getNodeMargin() { function processSingleMargins(node, currentMargin) { if (node.marginLeft || node.marginTop || node.marginRight || node.marginBottom) { return [ node.marginLeft || currentMargin[0] || 0, node.marginTop || currentMargin[1] || 0, node.marginRight || currentMargin[2] || 0, node.marginBottom || currentMargin[3] || 0 ]; } return currentMargin; } function flattenStyleArray(styleArray) { var flattenedStyles = {}; for (var i = styleArray.length - 1; i >= 0; i--) { var styleName = styleArray[i]; var style = self.styleStack.styleDictionary[styleName]; for (var key in style) { if (style.hasOwnProperty(key)) { flattenedStyles[key] = style[key]; } } } return flattenedStyles; } function convertMargin(margin) { if (typeof margin === 'number' || margin instanceof Number) { margin = [margin, margin, margin, margin]; } else if (Array.isArray(margin)) { if (margin.length === 2) { margin = [margin[0], margin[1], margin[0], margin[1]]; } } return margin; } var margin = [undefined, undefined, undefined, undefined]; if (node.style) { var styleArray = (Array.isArray(node.style)) ? node.style : [node.style]; var flattenedStyleArray = flattenStyleArray(styleArray); if (flattenedStyleArray) { margin = processSingleMargins(flattenedStyleArray, margin); } if (flattenedStyleArray.margin) { margin = convertMargin(flattenedStyleArray.margin); } } margin = processSingleMargins(node, margin); if (node.margin) { margin = convertMargin(node.margin); } if (margin[0] === undefined && margin[1] === undefined && margin[2] === undefined && margin[3] === undefined) { return null; } else { return margin; } } }
...
/**
* Measures all nodes and sets min/max-width properties required for the second
* layout-pass.
* @param {Object} docStructure document-definition-object
* @return {Object} document-measurement-object
*/
DocMeasure.prototype.measureDocument = function (docStructure) {
return this.measureNode(docStructure);
};
DocMeasure.prototype.measureNode = function (node) {
// expand shortcuts
if (Array.isArray(node)) {
node = {stack: node};
} else if (typeof node === 'string' || node instanceof String) {
...
measureOrderedList = function (node) { var style = this.styleStack.clone(); var items = node.ol; node.type = node.type || 'decimal'; node.separator = node.separator || '.'; node.reversed = node.reversed || false; if (!node.start) { node.start = node.reversed ? items.length : 1; } node._gapSize = this.gapSizeForList(); node._minWidth = 0; node._maxWidth = 0; var counter = node.start; for (var i = 0, l = items.length; i < l; i++) { var item = items[i] = this.measureNode(items[i]); if (!item.ol && !item.ul) { item.listMarker = this.buildOrderedMarker(item.counter || counter, style, node.type, node.separator); if (item.listMarker._inlines) { node._gapSize.width = Math.max(node._gapSize.width, item.listMarker._inlines[0].width); } } // TODO: else - nested lists numbering node._minWidth = Math.max(node._minWidth, items[i]._minWidth); node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth); if (node.reversed) { counter--; } else { counter++; } } node._minWidth += node._gapSize.width; node._maxWidth += node._gapSize.width; for (var i = 0, l = items.length; i < l; i++) { var item = items[i]; if (!item.ol && !item.ul) { item.listMarker._minWidth = item.listMarker._maxWidth = node._gapSize.width; } } return node; }
...
if (node.columns) {
return extendMargins(self.measureColumns(node));
} else if (node.stack) {
return extendMargins(self.measureVerticalContainer(node));
} else if (node.ul) {
return extendMargins(self.measureUnorderedList(node));
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
return extendMargins(self.measureImage(node));
} else if (node.canvas) {
...
measureQr = function (node) { node = qrEncoder.measure(node); node._alignment = this.styleStack.getProperty('alignment'); return node; }
...
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
return extendMargins(self.measureImage(node));
} else if (node.canvas) {
return extendMargins(self.measureCanvas(node));
} else if (node.qr) {
return extendMargins(self.measureQr(node));
} else {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
});
function extendMargins(node) {
var margin = node._margin;
...
measureTable = function (node) { extendTableWidths(node); node._layout = getLayout(this.tableLayouts); node._offsets = getOffsets(node._layout); var colSpans = []; var col, row, cols, rows; for (col = 0, cols = node.table.body[0].length; col < cols; col++) { var c = node.table.widths[col]; c._minWidth = 0; c._maxWidth = 0; for (row = 0, rows = node.table.body.length; row < rows; row++) { var rowData = node.table.body[row]; var data = rowData[col]; if (data === undefined) { console.error('Malformed table row ', rowData, 'in node ', node); throw 'Malformed table row, a cell is undefined.'; } if (data === null) { // transform to object data = ''; } if (!data._span) { data = rowData[col] = this.styleStack.auto(data, measureCb(this, data)); if (data.colSpan && data.colSpan > 1) { markSpans(rowData, col, data.colSpan); colSpans.push({col: col, span: data.colSpan, minWidth: data._minWidth, maxWidth: data._maxWidth}); } else { c._minWidth = Math.max(c._minWidth, data._minWidth); c._maxWidth = Math.max(c._maxWidth, data._maxWidth); } } if (data.rowSpan && data.rowSpan > 1) { markVSpans(node.table, row, col, data.rowSpan); } } } extendWidthsForColSpans(); var measures = ColumnCalculator.measureMinMax(node.table.widths); node._minWidth = measures.min + node._offsets.total; node._maxWidth = measures.max + node._offsets.total; return node; function measureCb(_this, data) { return function () { if (data !== null && typeof data === 'object') { data.fillColor = _this.styleStack.getProperty('fillColor'); } return _this.measureNode(data); }; } function getLayout(tableLayouts) { var layout = node.layout; if (typeof node.layout === 'string' || node instanceof String) { layout = tableLayouts[layout]; } /*jshint unused: false */ var defaultLayout = { hLineWidth: function (i, node) { return 1; }, vLineWidth: function (i, node) { return 1; }, hLineColor: function (i, node) { return 'black'; }, vLineColor: function (i, node) { return 'black'; }, paddingLeft: function (i, node) { return 4; }, paddingRight: function (i, node) { return 4; }, paddingTop: function (i, node) { return 2; }, paddingBottom: function (i, node) { return 2; }, fillColor: function (i, node) { return null; }, defaultBorder: true }; return pack(defaultLayout, layout); } function getOffsets(layout) { var offsets = []; var totalOffset = 0; var prevRightPadding = 0; for (var i = 0, l = node.table.widths.length; i < l; i++) { var lOffset = prevRightPadding + layout.vLineWidth(i, node) + layout.paddingLeft(i, node); offsets.push(lOffset); totalOffset += lOffset; prevRightPadding = layout.paddingRight(i, node); } totalOffset += prevRightPadding + layout.vLineWidth(node.table.widths.length, node); return { total: totalOffset, offsets: offsets }; } function extendWidthsForColSpans() { var q, j; for (var i = 0, l = colSpans.length; i < l; i++) { var span = colSpans[i]; var currentMinMax = getMinMax(span.col, span.span, node._offsets); var minDifference = span.minWidth - currentMinMax.minWidth; var maxDifference = span.maxWidth - currentMinMax.maxWidth; if (minDifference > 0) { q = minDifference / span.span; for (j = 0; j < span.span; j++) { node.table.widths[span.col + j]._minWidth += q; } } if (maxDifference > 0) { q = maxDifference / span.span; for (j = 0; j < span.span; j++) { node.table.widths[span.col + j]._maxWidth += q; } } } } function getMinMax(col, span, offsets) { var result = {minWidth: 0, maxWidth: 0}; for (var i = 0; i < span; i++) { result.minWidth += node.table.widths[col + i]._minWidth + (i ? offsets.offsets[col + i] : 0); result.maxWidth += node.table.widths[col + ...
...
} else if (node.stack) {
return extendMargins(self.measureVerticalContainer(node));
} else if (node.ul) {
return extendMargins(self.measureUnorderedList(node));
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
return extendMargins(self.measureImage(node));
} else if (node.canvas) {
return extendMargins(self.measureCanvas(node));
} else if (node.qr) {
...
measureUnorderedList = function (node) { var style = this.styleStack.clone(); var items = node.ul; node.type = node.type || 'disc'; node._gapSize = this.gapSizeForList(); node._minWidth = 0; node._maxWidth = 0; for (var i = 0, l = items.length; i < l; i++) { var item = items[i] = this.measureNode(items[i]); if (!item.ol && !item.ul) { item.listMarker = this.buildUnorderedMarker(style, node._gapSize, node.type); } node._minWidth = Math.max(node._minWidth, items[i]._minWidth + node._gapSize.width); node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth + node._gapSize.width); } return node; }
...
node._margin = getNodeMargin(node);
if (node.columns) {
return extendMargins(self.measureColumns(node));
} else if (node.stack) {
return extendMargins(self.measureVerticalContainer(node));
} else if (node.ul) {
return extendMargins(self.measureUnorderedList(node));
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
...
measureVerticalContainer = function (node) { var items = node.stack; node._minWidth = 0; node._maxWidth = 0; for (var i = 0, l = items.length; i < l; i++) { items[i] = this.measureNode(items[i]); node._minWidth = Math.max(node._minWidth, items[i]._minWidth); node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth); } return node; }
...
return this.styleStack.auto(node, function () {
// TODO: refactor + rethink whether this is the proper way to handle margins
node._margin = getNodeMargin(node);
if (node.columns) {
return extendMargins(self.measureColumns(node));
} else if (node.stack) {
return extendMargins(self.measureVerticalContainer(node));
} else if (node.ul) {
return extendMargins(self.measureUnorderedList(node));
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
...
function DocumentContext(pageSize, pageMargins) { this.pages = []; this.pageMargins = pageMargins; this.x = pageMargins.left; this.availableWidth = pageSize.width - pageMargins.left - pageMargins.right; this.availableHeight = 0; this.page = -1; this.snapshots = []; this.endingCell = null; this.tracker = new TraversalTracker(); this.addPage(pageSize); }
n/a
addMargin = function (left, right) { this.x += left; this.availableWidth -= left + (right || 0); }
...
if (node.pageBreak === 'before') {
self.writer.moveToNextPage(node.pageOrientation);
}
if (margin) {
self.writer.context().moveDown(margin[1]);
self.writer.context().addMargin(margin[0], margin[2]);
}
callback();
if (margin) {
self.writer.context().addMargin(-margin[0], -margin[2]);
self.writer.context().moveDown(margin[3]);
...
addPage = function (pageSize) { var page = {items: [], pageSize: pageSize}; this.pages.push(page); this.page = this.pages.length - 1; this.initializePage(); this.tracker.emit('pageAdded'); return page; }
...
this.snapshots = [];
this.endingCell = null;
this.tracker = new TraversalTracker();
this.addPage(pageSize);
}
DocumentContext.prototype.beginColumnGroup = function () {
this.snapshots.push({
x: this.x,
y: this.y,
availableHeight: this.availableHeight,
...
beginColumn = function (width, offset, endingCell) { var saved = this.snapshots[this.snapshots.length - 1]; this.calculateBottomMost(saved); this.endingCell = endingCell; this.page = saved.page; this.x = this.x + this.lastColumnWidth + (offset || 0); this.y = saved.y; this.availableWidth = width; //saved.availableWidth - offset; this.availableHeight = saved.availableHeight; this.lastColumnWidth = width; }
...
if (column.colSpan && column.colSpan > 1) {
for (var j = 1; j < column.colSpan; j++) {
width += widths[++i]._calcWidth + gaps[i];
}
}
self.writer.context().beginColumn(width, leftOffset, getEndingCell(column, i));
if (!column._span) {
self.processNode(column);
addAll(positions, column.positions);
} else if (column._columnEndingContext) {
// row-span ending
self.writer.context().markEnding(column);
}
...
beginColumnGroup = function () { this.snapshots.push({ x: this.x, y: this.y, availableHeight: this.availableHeight, availableWidth: this.availableWidth, page: this.page, bottomMost: {y: this.y, page: this.page}, endingCell: this.endingCell, lastColumnWidth: this.lastColumnWidth }); this.lastColumnWidth = 0; }
...
LayoutBuilder.prototype.processRow = function (columns, widths, gaps, tableBody, tableRow) {
var self = this;
var pageBreaks = [], positions = [];
this.tracker.auto('pageChanged', storePageBreakData, function () {
widths = widths || columns;
self.writer.context().beginColumnGroup();
for (var i = 0, l = columns.length; i < l; i++) {
var column = columns[i];
var width = widths[i]._calcWidth;
var leftOffset = colLeftOffset(i);
if (column.colSpan && column.colSpan > 1) {
...
beginDetachedBlock = function () { this.snapshots.push({ x: this.x, y: this.y, availableHeight: this.availableHeight, availableWidth: this.availableWidth, page: this.page, endingCell: this.endingCell, lastColumnWidth: this.lastColumnWidth }); }
...
this.linearNodeList.push(node);
decorateNode(node);
applyMargins(function () {
var absPosition = node.absolutePosition;
if (absPosition) {
self.writer.context().beginDetachedBlock();
self.writer.context().moveTo(absPosition.x || 0, absPosition.y || 0);
}
var relPosition = node.relativePosition;
if (relPosition) {
self.writer.context().beginDetachedBlock();
self.writer.context().moveTo((relPosition.x || 0) + self.writer.context().x, (relPosition.y || 0) + self.writer.context().y);
...
calculateBottomMost = function (destContext) { if (this.endingCell) { this.saveContextInEndingCell(this.endingCell); this.endingCell = null; } else { destContext.bottomMost = bottomMostContext(this, destContext.bottomMost); } }
...
this.lastColumnWidth = 0;
};
DocumentContext.prototype.beginColumn = function (width, offset, endingCell) {
var saved = this.snapshots[this.snapshots.length - 1];
this.calculateBottomMost(saved);
this.endingCell = endingCell;
this.page = saved.page;
this.x = this.x + this.lastColumnWidth + (offset || 0);
this.y = saved.y;
this.availableWidth = width; //saved.availableWidth - offset;
this.availableHeight = saved.availableHeight;
...
completeColumnGroup = function () { var saved = this.snapshots.pop(); this.calculateBottomMost(saved); this.endingCell = null; this.x = saved.x; this.y = saved.bottomMost.y; this.page = saved.bottomMost.page; this.availableWidth = saved.availableWidth; this.availableHeight = saved.bottomMost.availableHeight; this.lastColumnWidth = saved.lastColumnWidth; }
...
addAll(positions, column.positions);
} else if (column._columnEndingContext) {
// row-span ending
self.writer.context().markEnding(column);
}
}
self.writer.context().completeColumnGroup();
});
return {pageBreaks: pageBreaks, positions: positions};
function storePageBreakData(data) {
var pageDesc;
...
endDetachedBlock = function () { var saved = this.snapshots.pop(); this.x = saved.x; this.y = saved.y; this.availableWidth = saved.availableWidth; this.availableHeight = saved.availableHeight; this.page = saved.page; this.endingCell = saved.endingCell; this.lastColumnWidth = saved.lastColumnWidth; }
...
} else if (node.qr) {
self.processQr(node);
} else if (!node._span) {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
if (absPosition || relPosition) {
self.writer.context().endDetachedBlock();
}
});
function applyMargins(callback) {
var margin = node._margin;
if (node.pageBreak === 'before') {
...
getCurrentPage = function () { if (this.page < 0 || this.page >= this.pages.length) { return null; } return this.pages[this.page]; }
...
this.availableHeight -= offset;
return this.availableHeight > 0;
};
DocumentContext.prototype.initializePage = function () {
this.y = this.pageMargins.top;
this.availableHeight = this.getCurrentPage().pageSize.height - this.pageMargins.top -
this.pageMargins.bottom;
this.pageSnapshot().availableWidth = this.getCurrentPage().pageSize.width - this.pageMargins.left - this.pageMargins.right;
};
DocumentContext.prototype.pageSnapshot = function () {
if (this.snapshots[0]) {
return this.snapshots[0];
} else {
...
getCurrentPosition = function () { var pageSize = this.getCurrentPage().pageSize; var innerHeight = pageSize.height - this.pageMargins.top - this.pageMargins.bottom; var innerWidth = pageSize.width - this.pageMargins.left - this.pageMargins.right; return { pageNumber: this.page + 1, pageOrientation: pageSize.orientation, pageInnerHeight: innerHeight, pageInnerWidth: innerWidth, left: this.x, top: this.y, verticalRatio: ((this.y - this.pageMargins.top) / innerHeight), horizontalRatio: ((this.x - this.pageMargins.left) / innerWidth) }; }
n/a
initializePage = function () { this.y = this.pageMargins.top; this.availableHeight = this.getCurrentPage().pageSize.height - this.pageMargins.top - this.pageMargins.bottom; this.pageSnapshot().availableWidth = this.getCurrentPage().pageSize.width - this.pageMargins.left - this.pageMargins.right; }
...
this.addPage(pageSize);
if (currentPageOrientation === pageSize.orientation) {
this.availableWidth = currentAvailableWidth;
}
} else {
this.page = nextPageIndex;
this.initializePage();
}
return {
newPageCreated: createNewPage,
prevPage: prevPage,
prevY: prevY,
y: this.y
...
markEnding = function (endingCell) { this.page = endingCell._columnEndingContext.page; this.x = endingCell._columnEndingContext.x; this.y = endingCell._columnEndingContext.y; this.availableWidth = endingCell._columnEndingContext.availableWidth; this.availableHeight = endingCell._columnEndingContext.availableHeight; this.lastColumnWidth = endingCell._columnEndingContext.lastColumnWidth; }
...
self.writer.context().beginColumn(width, leftOffset, getEndingCell(column, i));
if (!column._span) {
self.processNode(column);
addAll(positions, column.positions);
} else if (column._columnEndingContext) {
// row-span ending
self.writer.context().markEnding(column);
}
}
self.writer.context().completeColumnGroup();
});
return {pageBreaks: pageBreaks, positions: positions};
...
moveDown = function (offset) { this.y += offset; this.availableHeight -= offset; return this.availableHeight > 0; }
...
addPageItem(page, {
type: 'line',
item: line
}, index);
this.tracker.emit('lineAdded', line);
if (!dontUpdateContextPosition) {
context.moveDown(height);
}
return position;
};
ElementWriter.prototype.alignLine = function (line) {
var width = this.context.availableWidth;
...
moveTo = function (x, y) { if (x !== undefined && x !== null) { this.x = x; this.availableWidth = this.getCurrentPage().pageSize.width - this.x - this.pageMargins.right; } if (y !== undefined && y !== null) { this.y = y; this.availableHeight = this.getCurrentPage().pageSize.height - this.y - this.pageMargins.bottom; } }
...
this.linearNodeList.push(node);
decorateNode(node);
applyMargins(function () {
var absPosition = node.absolutePosition;
if (absPosition) {
self.writer.context().beginDetachedBlock();
self.writer.context().moveTo(absPosition.x || 0, absPosition.y || 0);
}
var relPosition = node.relativePosition;
if (relPosition) {
self.writer.context().beginDetachedBlock();
self.writer.context().moveTo((relPosition.x || 0) + self.writer.context().x, (relPosition.y || 0) + self.writer.context().y);
}
...
moveToNextPage = function (pageOrientation) { var nextPageIndex = this.page + 1; var prevPage = this.page; var prevY = this.y; var createNewPage = nextPageIndex >= this.pages.length; if (createNewPage) { var currentAvailableWidth = this.availableWidth; var currentPageOrientation = this.getCurrentPage().pageSize.orientation; var pageSize = getPageSize(this.getCurrentPage(), pageOrientation); this.addPage(pageSize); if (currentPageOrientation === pageSize.orientation) { this.availableWidth = currentAvailableWidth; } } else { this.page = nextPageIndex; this.initializePage(); } return { newPageCreated: createNewPage, prevPage: prevPage, prevY: prevY, y: this.y }; }
...
}
});
function applyMargins(callback) {
var margin = node._margin;
if (node.pageBreak === 'before') {
self.writer.moveToNextPage(node.pageOrientation);
}
if (margin) {
self.writer.context().moveDown(margin[1]);
self.writer.context().addMargin(margin[0], margin[2]);
}
...
pageSnapshot = function () { if (this.snapshots[0]) { return this.snapshots[0]; } else { return this; } }
...
return this.availableHeight > 0;
};
DocumentContext.prototype.initializePage = function () {
this.y = this.pageMargins.top;
this.availableHeight = this.getCurrentPage().pageSize.height - this.pageMargins.top - this.pageMargins.bottom;
this.pageSnapshot().availableWidth = this.getCurrentPage().pageSize.width - this.pageMargins
.left - this.pageMargins.right;
};
DocumentContext.prototype.pageSnapshot = function () {
if (this.snapshots[0]) {
return this.snapshots[0];
} else {
return this;
...
saveContextInEndingCell = function (endingCell) { endingCell._columnEndingContext = { page: this.page, x: this.x, y: this.y, availableHeight: this.availableHeight, availableWidth: this.availableWidth, lastColumnWidth: this.lastColumnWidth }; }
...
this.availableHeight = saved.availableHeight;
this.lastColumnWidth = width;
};
DocumentContext.prototype.calculateBottomMost = function (destContext) {
if (this.endingCell) {
this.saveContextInEndingCell(this.endingCell);
this.endingCell = null;
} else {
destContext.bottomMost = bottomMostContext(this, destContext.bottomMost);
}
};
DocumentContext.prototype.markEnding = function (endingCell) {
...
function ElementWriter(context, tracker) { this.context = context; this.contextStack = []; this.tracker = tracker; }
n/a
addFragment = function (block, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition) { var ctx = this.context; var page = ctx.getCurrentPage(); if (!useBlockXOffset && block.height > ctx.availableHeight) { return false; } block.items.forEach(function (item) { switch (item.type) { case 'line': var l = cloneLine(item.item); l.x = (l.x || 0) + (useBlockXOffset ? (block.xOffset || 0) : ctx.x); l.y = (l.y || 0) + (useBlockYOffset ? (block.yOffset || 0) : ctx.y); page.items.push({ type: 'line', item: l }); break; case 'vector': var v = pack(item.item); offsetVector(v, useBlockXOffset ? (block.xOffset || 0) : ctx.x, useBlockYOffset ? (block.yOffset || 0) : ctx.y); page.items.push({ type: 'vector', item: v }); break; case 'image': var img = pack(item.item); img.x = (img.x || 0) + (useBlockXOffset ? (block.xOffset || 0) : ctx.x); img.y = (img.y || 0) + (useBlockYOffset ? (block.yOffset || 0) : ctx.y); page.items.push({ type: 'image', item: img }); break; } }); if (!dontUpdateContextPosition) { ctx.moveDown(block.height); } return true; }
...
};
PageElementWriter.prototype.addVector = function (vector, ignoreContextX, ignoreContextY, index) {
return this.writer.addVector(vector, ignoreContextX, ignoreContextY, index);
};
PageElementWriter.prototype.addFragment = function (fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition) {
if (!this.writer.addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition
)) {
this.moveToNextPage();
this.writer.addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition);
}
};
PageElementWriter.prototype.moveToNextPage = function (pageOrientation) {
...
addImage = function (image, index) { var context = this.context; var page = context.getCurrentPage(), position = this.getCurrentPositionOnPage(); if (context.availableHeight < image._height || !page) { return false; } if (image._x === undefined) { image._x = image.x || 0; } image.x = context.x + image._x; image.y = context.y; this.alignImage(image); addPageItem(page, { type: 'image', item: image }, index); context.moveDown(image._height); return position; }
...
line.lastLineInParagraph = textNode._inlines.length === 0;
return line;
};
// images
LayoutBuilder.prototype.processImage = function (node) {
var position = this.writer.addImage(node);
node.positions.push(position);
};
LayoutBuilder.prototype.processCanvas = function (node) {
var height = node._minHeight;
if (this.writer.context().availableHeight < height) {
...
addLine = function (line, dontUpdateContextPosition, index) { var height = line.getHeight(); var context = this.context; var page = context.getCurrentPage(), position = this.getCurrentPositionOnPage(); if (context.availableHeight < height || !page) { return false; } line.x = context.x + (line.x || 0); line.y = context.y + (line.y || 0); this.alignLine(line); addPageItem(page, { type: 'line', item: line }, index); this.tracker.emit('lineAdded', line); if (!dontUpdateContextPosition) { context.moveDown(height); } return position; }
...
offsetVector(vector, -marker._minWidth, 0);
self.writer.addVector(vector);
} else if (marker._inlines) {
var markerLine = new Line(self.pageSize.width);
markerLine.addInline(marker._inlines[0]);
markerLine.x = -marker._minWidth;
markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight();
self.writer.addLine(markerLine, true);
}
}
}
};
// tables
LayoutBuilder.prototype.processTable = function (tableNode) {
...
addQr = function (qr, index) { var context = this.context; var page = context.getCurrentPage(), position = this.getCurrentPositionOnPage(); if (context.availableHeight < qr._height || !page) { return false; } if (qr._x === undefined) { qr._x = qr.x || 0; } qr.x = context.x + qr._x; qr.y = context.y; this.alignImage(qr); for (var i = 0, l = qr._canvas.length; i < l; i++) { var vector = qr._canvas[i]; vector.x += qr.x; vector.y += qr.y; this.addVector(vector, true, true, index); } context.moveDown(qr._height); return position; }
...
return fitOnPage(this, function (self) {
return self.writer.addImage(image, index);
});
};
PageElementWriter.prototype.addQr = function (qr, index) {
return fitOnPage(this, function (self) {
return self.writer.addQr(qr, index);
});
};
PageElementWriter.prototype.addVector = function (vector, ignoreContextX, ignoreContextY, index) {
return this.writer.addVector(vector, ignoreContextX, ignoreContextY, index);
};
...
addVector = function (vector, ignoreContextX, ignoreContextY, index) { var context = this.context; var page = context.getCurrentPage(), position = this.getCurrentPositionOnPage(); if (page) { offsetVector(vector, ignoreContextX ? 0 : context.x, ignoreContextY ? 0 : context.y); addPageItem(page, { type: 'vector', item: vector }, index); return position; } }
...
this.alignImage(qr);
for (var i = 0, l = qr._canvas.length; i < l; i++) {
var vector = qr._canvas[i];
vector.x += qr.x;
vector.y += qr.y;
this.addVector(vector, true, true, index);
}
context.moveDown(qr._height);
return position;
};
...
alignImage = function (image) { var width = this.context.availableWidth; var imageWidth = image._minWidth; var offset = 0; switch (image._alignment) { case 'right': offset = width - imageWidth; break; case 'center': offset = (width - imageWidth) / 2; break; } if (offset) { image.x = (image.x || 0) + offset; } }
...
if (image._x === undefined) {
image._x = image.x || 0;
}
image.x = context.x + image._x;
image.y = context.y;
this.alignImage(image);
addPageItem(page, {
type: 'image',
item: image
}, index);
context.moveDown(image._height);
...
alignLine = function (line) { var width = this.context.availableWidth; var lineWidth = line.getWidth(); var alignment = line.inlines && line.inlines.length > 0 && line.inlines[0].alignment; var offset = 0; switch (alignment) { case 'right': offset = width - lineWidth; break; case 'center': offset = (width - lineWidth) / 2; break; } if (offset) { line.x = (line.x || 0) + offset; } if (alignment === 'justify' && !line.newLineForced && !line.lastLineInParagraph && line.inlines.length > 1) { var additionalSpacing = (width - lineWidth) / (line.inlines.length - 1); for (var i = 1, l = line.inlines.length; i < l; i++) { offset = i * additionalSpacing; line.inlines[i].x += offset; } } }
...
if (context.availableHeight < height || !page) {
return false;
}
line.x = context.x + (line.x || 0);
line.y = context.y + (line.y || 0);
this.alignLine(line);
addPageItem(page, {
type: 'line',
item: line
}, index);
this.tracker.emit('lineAdded', line);
...
getCurrentPositionOnPage = function () { return (this.contextStack[0] || this.context).getCurrentPosition(); }
...
}
}
ElementWriter.prototype.addLine = function (line, dontUpdateContextPosition, index) {
var height = line.getHeight();
var context = this.context;
var page = context.getCurrentPage(),
position = this.getCurrentPositionOnPage();
if (context.availableHeight < height || !page) {
return false;
}
line.x = context.x + (line.x || 0);
line.y = context.y + (line.y || 0);
...
popContext = function () { this.context = this.contextStack.pop(); }
...
this.writer.pushContext(width, height);
}
};
PageElementWriter.prototype.commitUnbreakableBlock = function (forcedX, forcedY) {
if (--this.transactionLevel === 0) {
var unbreakableContext = this.writer.context;
this.writer.popContext();
var nbPages = unbreakableContext.pages.length;
if (nbPages > 0) {
// no support for multi-page unbreakableBlocks
var fragment = unbreakableContext.pages[0];
fragment.xOffset = forcedX;
fragment.yOffset = forcedY;
...
pushContext = function (contextOrWidth, height) { if (contextOrWidth === undefined) { height = this.context.getCurrentPage().height - this.context.pageMargins.top - this.context.pageMargins.bottom; contextOrWidth = this.context.availableWidth; } if (typeof contextOrWidth === 'number' || contextOrWidth instanceof Number) { contextOrWidth = new DocumentContext({width: contextOrWidth, height: height}, {left: 0, right: 0, top: 0, bottom: 0}); } this.contextStack.push(this.context); this.context = contextOrWidth; }
...
y: nextPage.y
});
};
PageElementWriter.prototype.beginUnbreakableBlock = function (width, height) {
if (this.transactionLevel++ === 0) {
this.originalX = this.writer.context.x;
this.writer.pushContext(width, height);
}
};
PageElementWriter.prototype.commitUnbreakableBlock = function (forcedX, forcedY) {
if (--this.transactionLevel === 0) {
var unbreakableContext = this.writer.context;
this.writer.popContext();
...
function FontProvider(fontDescriptors, pdfKitDoc) { this.fonts = {}; this.pdfKitDoc = pdfKitDoc; this.fontCache = {}; for (var font in fontDescriptors) { if (fontDescriptors.hasOwnProperty(font)) { var fontDef = fontDescriptors[font]; this.fonts[font] = { normal: fontDef.normal, bold: fontDef.bold, italics: fontDef.italics, bolditalics: fontDef.bolditalics }; } } }
n/a
provideFont = function (familyName, bold, italics) { var type = typeName(bold, italics); if (!this.fonts[familyName] || !this.fonts[familyName][type]) { throw new Error('Font \'' + familyName + '\' in style \'' + type + '\' is not defined in the font section of the document definition .'); } this.fontCache[familyName] = this.fontCache[familyName] || {}; if (!this.fontCache[familyName][type]) { var def = this.fonts[familyName][type]; if (!Array.isArray(def)) { def = [def]; } this.fontCache[familyName][type] = this.pdfKitDoc.font.apply(this.pdfKitDoc, def)._font; } return this.fontCache[familyName][type]; }
...
watermark.color = watermark.color || 'black';
watermark.opacity = watermark.opacity || 0.6;
watermark.bold = watermark.bold || false;
watermark.italics = watermark.italics || false;
var watermarkObject = {
text: watermark.text,
font: fontProvider.provideFont(watermark.font, watermark.bold, watermark.italics),
size: getSize(this.pageSize, watermark, fontProvider),
color: watermark.color,
opacity: watermark.opacity
};
var pages = this.writer.context().pages;
for (var i = 0, l = pages.length; i < l; i++) {
...
function fontStringify(key, val) { if (key === 'font') { return 'font'; } return val; }
n/a
function isFunction(functionToCheck) { var getType = {}; return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; }
n/a
function offsetVector(vector, x, y) { switch (vector.type) { case 'ellipse': case 'rect': vector.x += x; vector.y += y; break; case 'line': vector.x1 += x; vector.x2 += x; vector.y1 += y; vector.y2 += y; break; case 'polyline': for (var i = 0, l = vector.points.length; i < l; i++) { vector.points[i].x += x; vector.points[i].y += y; } break; } }
n/a
function pack() { var result = {}; for (var i = 0, l = arguments.length; i < l; i++) { var obj = arguments[i]; if (obj) { for (var key in obj) { if (obj.hasOwnProperty(key)) { result[key] = obj[key]; } } } } return result; }
n/a
function ImageMeasure(pdfKitDoc, imageDictionary) { this.pdfKitDoc = pdfKitDoc; this.imageDictionary = imageDictionary || {}; }
n/a
measureImage = function (src) { var image, label; var that = this; if (!this.pdfKitDoc._imageRegistry[src]) { label = 'I' + (++this.pdfKitDoc._imageCount); try { image = PDFImage.open(realImageSrc(src), label); } catch (error) { image = null; } if (image === null || image === undefined) { throw 'invalid image, images dictionary should contain dataURL entries (or local file paths in node.js)'; } image.embed(this.pdfKitDoc); this.pdfKitDoc._imageRegistry[src] = image; } else { image = this.pdfKitDoc._imageRegistry[src]; } return {width: image.width, height: image.height}; function realImageSrc(src) { var img = that.imageDictionary[src]; if (!img) { return src; } var index = img.indexOf('base64,'); if (index < 0) { return that.imageDictionary[src]; } return new Buffer(img.substring(index + 7), 'base64'); } }
...
} else if (node.ol) {
return extendMargins(self.measureOrderedList(node));
} else if (node.table) {
return extendMargins(self.measureTable(node));
} else if (node.text !== undefined) {
return extendMargins(self.measureLeaf(node));
} else if (node.image) {
return extendMargins(self.measureImage(node));
} else if (node.canvas) {
return extendMargins(self.measureCanvas(node));
} else if (node.qr) {
return extendMargins(self.measureQr(node));
} else {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
...
function LayoutBuilder(pageSize, pageMargins, imageMeasure) { this.pageSize = pageSize; this.pageMargins = pageMargins; this.tracker = new TraversalTracker(); this.imageMeasure = imageMeasure; this.tableLayouts = {}; }
n/a
addBackground = function (background) { var backgroundGetter = isFunction(background) ? background : function () { return background; }; var pageBackground = backgroundGetter(this.writer.context().page + 1); if (pageBackground) { var pageSize = this.writer.context().getCurrentPage().pageSize; this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height); this.processNode(this.docMeasure.measureDocument(pageBackground)); this.writer.commitUnbreakableBlock(0, 0); } }
...
docStructure = this.docMeasure.measureDocument(docStructure);
this.writer = new PageElementWriter(
new DocumentContext(this.pageSize, this.pageMargins), this.tracker);
var _this = this;
this.writer.context().tracker.startTracking('pageAdded', function () {
_this.addBackground(background);
});
this.addBackground(background);
this.processNode(docStructure);
this.addHeadersAndFooters(header, footer);
/* jshint eqnull:true */
if (watermark != null) {
...
addDynamicRepeatable = function (nodeGetter, sizeFunction) { var pages = this.writer.context().pages; for (var pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) { this.writer.context().page = pageIndex; var node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize); if (node) { var sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins); this.writer.beginUnbreakableBlock(sizes.width, sizes.height); this.processNode(this.docMeasure.measureDocument(node)); this.writer.commitUnbreakableBlock(sizes.x, sizes.y); } } }
...
this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
this.processNode(this.docMeasure.measureDocument(pageBackground));
this.writer.commitUnbreakableBlock(0, 0);
}
};
LayoutBuilder.prototype.addStaticRepeatable = function (headerOrFooter, sizeFunction) {
this.addDynamicRepeatable(function () {
return headerOrFooter;
}, sizeFunction);
};
LayoutBuilder.prototype.addDynamicRepeatable = function (nodeGetter, sizeFunction) {
var pages = this.writer.context().pages;
...
addHeadersAndFooters = function (header, footer) { var headerSizeFct = function (pageSize, pageMargins) { return { x: 0, y: 0, width: pageSize.width, height: pageMargins.top }; }; var footerSizeFct = function (pageSize, pageMargins) { return { x: 0, y: pageSize.height - pageMargins.bottom, width: pageSize.width, height: pageMargins.bottom }; }; if (isFunction(header)) { this.addDynamicRepeatable(header, headerSizeFct); } else if (header) { this.addStaticRepeatable(header, headerSizeFct); } if (isFunction(footer)) { this.addDynamicRepeatable(footer, footerSizeFct); } else if (footer) { this.addStaticRepeatable(footer, footerSizeFct); } }
...
var _this = this;
this.writer.context().tracker.startTracking('pageAdded', function () {
_this.addBackground(background);
});
this.addBackground(background);
this.processNode(docStructure);
this.addHeadersAndFooters(header, footer);
/* jshint eqnull:true */
if (watermark != null) {
this.addWatermark(watermark, fontProvider, defaultStyle);
}
return {pages: this.writer.context().pages, linearNodeList: this.linearNodeList};
};
...
addStaticRepeatable = function (headerOrFooter, sizeFunction) { this.addDynamicRepeatable(function () { return headerOrFooter; }, sizeFunction); }
...
height: pageMargins.bottom
};
};
if (isFunction(header)) {
this.addDynamicRepeatable(header, headerSizeFct);
} else if (header) {
this.addStaticRepeatable(header, headerSizeFct);
}
if (isFunction(footer)) {
this.addDynamicRepeatable(footer, footerSizeFct);
} else if (footer) {
this.addStaticRepeatable(footer, footerSizeFct);
}
...
addWatermark = function (watermark, fontProvider, defaultStyle) { if (typeof watermark === 'string') { watermark = {'text': watermark}; } if (!watermark.text) { // empty watermark text return; } watermark.font = watermark.font || defaultStyle.font || 'Roboto'; watermark.color = watermark.color || 'black'; watermark.opacity = watermark.opacity || 0.6; watermark.bold = watermark.bold || false; watermark.italics = watermark.italics || false; var watermarkObject = { text: watermark.text, font: fontProvider.provideFont(watermark.font, watermark.bold, watermark.italics), size: getSize(this.pageSize, watermark, fontProvider), color: watermark.color, opacity: watermark.opacity }; var pages = this.writer.context().pages; for (var i = 0, l = pages.length; i < l; i++) { pages[i].watermark = watermarkObject; } function getSize(pageSize, watermark, fontProvider) { var width = pageSize.width; var height = pageSize.height; var targetWidth = Math.sqrt(width * width + height * height) * 0.8; /* page diagonal * sample factor */ var textTools = new TextTools(fontProvider); var styleContextStack = new StyleContextStack(null, {font: watermark.font, bold: watermark.bold, italics: watermark.italics}); var size; /** * Binary search the best font size. * Initial bounds [0, 1000] * Break when range < 1 */ var a = 0; var b = 1000; var c = (a + b) / 2; while (Math.abs(a - b) > 1) { styleContextStack.push({ fontSize: c }); size = textTools.sizeOfString(watermark.text, styleContextStack); if (size.width > targetWidth) { b = c; c = (a + b) / 2; } else if (size.width < targetWidth) { a = c; c = (a + b) / 2; } styleContextStack.pop(); } /* End binary search */ return {size: size, fontSize: c}; } }
...
});
this.addBackground(background);
this.processNode(docStructure);
this.addHeadersAndFooters(header, footer);
/* jshint eqnull:true */
if (watermark != null) {
this.addWatermark(watermark, fontProvider, defaultStyle);
}
return {pages: this.writer.context().pages, linearNodeList: this.linearNodeList};
};
LayoutBuilder.prototype.addBackground = function (background) {
...
buildNextLine = function (textNode) { if (!textNode._inlines || textNode._inlines.length === 0) { return null; } var line = new Line(this.writer.context().availableWidth); while (textNode._inlines && textNode._inlines.length > 0 && line.hasEnoughSpaceForInline(textNode._inlines[0])) { line.addInline(textNode._inlines.shift()); } line.lastLineInParagraph = textNode._inlines.length === 0; return line; }
...
}
processor.endTable(this.writer);
};
// leafs (texts)
LayoutBuilder.prototype.processLeaf = function (node) {
var line = this.buildNextLine(node);
var currentHeight = (line) ? line.getHeight() : 0;
var maxHeight = node.maxHeight || -1;
while (line && (maxHeight === -1 || currentHeight < maxHeight)) {
var positions = this.writer.addLine(line);
node.positions.push(positions);
line = this.buildNextLine(node);
...
layoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark, pageBreakBeforeFct ) { function addPageBreaksIfNecessary(linearNodeList, pages) { if (!isFunction(pageBreakBeforeFct)) { return false; } linearNodeList = _.reject(linearNodeList, function (node) { return _.isEmpty(node.positions); }); _.each(linearNodeList, function (node) { var nodeInfo = _.pick(node, [ 'id', 'text', 'ul', 'ol', 'table', 'image', 'qr', 'canvas', 'columns', 'headlineLevel', 'style', 'pageBreak', 'pageOrientation', 'width', 'height' ]); nodeInfo.startPosition = _.first(node.positions); nodeInfo.pageNumbers = _.chain(node.positions).map('pageNumber').uniq().value(); nodeInfo.pages = pages.length; nodeInfo.stack = _.isArray(node.stack); node.nodeInfo = nodeInfo; }); return _.some(linearNodeList, function (node, index, followingNodeList) { if (node.pageBreak !== 'before' && !node.pageBreakCalculated) { node.pageBreakCalculated = true; var pageNumber = _.first(node.nodeInfo.pageNumbers); var followingNodesOnPage = _.chain(followingNodeList).drop(index + 1).filter(function (node0) { return _.includes(node0.nodeInfo.pageNumbers, pageNumber); }).value(); var nodesOnNextPage = _.chain(followingNodeList).drop(index + 1).filter(function (node0) { return _.includes(node0.nodeInfo.pageNumbers, pageNumber + 1); }).value(); var previousNodesOnPage = _.chain(followingNodeList).take(index).filter(function (node0) { return _.includes(node0.nodeInfo.pageNumbers, pageNumber); }).value(); if (pageBreakBeforeFct(node.nodeInfo, _.map(followingNodesOnPage, 'nodeInfo'), _.map(nodesOnNextPage, 'nodeInfo'), _.map(previousNodesOnPage, 'nodeInfo'))) { node.pageBreak = 'before'; return true; } } }); } this.docMeasure = new DocMeasure(fontProvider, styleDictionary, defaultStyle, this.imageMeasure, this.tableLayouts, images); function resetXYs(result) { _.each(result.linearNodeList, function (node) { node.resetXY(); }); } var result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images , watermark); while (addPageBreaksIfNecessary(result.linearNodeList, result.pages)) { resetXYs(result); result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark); } return result.pages; }
n/a
processCanvas = function (node) { var height = node._minHeight; if (this.writer.context().availableHeight < height) { // TODO: support for canvas larger than a page // TODO: support for other overflow methods this.writer.moveToNextPage(); } node.canvas.forEach(function (vector) { var position = this.writer.addVector(vector); node.positions.push(position); }, this); this.writer.context().moveDown(height); }
...
} else if (node.table) {
self.processTable(node);
} else if (node.text !== undefined) {
self.processLeaf(node);
} else if (node.image) {
self.processImage(node);
} else if (node.canvas) {
self.processCanvas(node);
} else if (node.qr) {
self.processQr(node);
} else if (!node._span) {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
if (absPosition || relPosition) {
...
processColumns = function (columnNode) { var columns = columnNode.columns; var availableWidth = this.writer.context().availableWidth; var gaps = gapArray(columnNode._gap); if (gaps) { availableWidth -= (gaps.length - 1) * columnNode._gap; } ColumnCalculator.buildColumnWidths(columns, availableWidth); var result = this.processRow(columns, columns, gaps); addAll(columnNode.positions, result.positions); function gapArray(gap) { if (!gap) { return null; } var gaps = []; gaps.push(0); for (var i = columns.length - 1; i > 0; i--) { gaps.push(gap); } return gaps; } }
...
self.writer.context().beginDetachedBlock();
self.writer.context().moveTo((relPosition.x || 0) + self.writer.context().x, (relPosition.y || 0) + self.writer.context().y);
}
if (node.stack) {
self.processVerticalContainer(node);
} else if (node.columns) {
self.processColumns(node);
} else if (node.ul) {
self.processList(false, node);
} else if (node.ol) {
self.processList(true, node);
} else if (node.table) {
self.processTable(node);
} else if (node.text !== undefined) {
...
processImage = function (node) { var position = this.writer.addImage(node); node.positions.push(position); }
...
} else if (node.ol) {
self.processList(true, node);
} else if (node.table) {
self.processTable(node);
} else if (node.text !== undefined) {
self.processLeaf(node);
} else if (node.image) {
self.processImage(node);
} else if (node.canvas) {
self.processCanvas(node);
} else if (node.qr) {
self.processQr(node);
} else if (!node._span) {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
...
processLeaf = function (node) { var line = this.buildNextLine(node); var currentHeight = (line) ? line.getHeight() : 0; var maxHeight = node.maxHeight || -1; while (line && (maxHeight === -1 || currentHeight < maxHeight)) { var positions = this.writer.addLine(line); node.positions.push(positions); line = this.buildNextLine(node); if (line) { currentHeight += line.getHeight(); } } }
...
} else if (node.ul) {
self.processList(false, node);
} else if (node.ol) {
self.processList(true, node);
} else if (node.table) {
self.processTable(node);
} else if (node.text !== undefined) {
self.processLeaf(node);
} else if (node.image) {
self.processImage(node);
} else if (node.canvas) {
self.processCanvas(node);
} else if (node.qr) {
self.processQr(node);
} else if (!node._span) {
...
processList = function (orderedList, node) { var self = this, items = orderedList ? node.ol : node.ul, gapSize = node._gapSize; this.writer.context().addMargin(gapSize.width); var nextMarker; this.tracker.auto('lineAdded', addMarkerToFirstLeaf, function () { items.forEach(function (item) { nextMarker = item.listMarker; self.processNode(item); addAll(node.positions, item.positions); }); }); this.writer.context().addMargin(-gapSize.width); function addMarkerToFirstLeaf(line) { // I'm not very happy with the way list processing is implemented // (both code and algorithm should be rethinked) if (nextMarker) { var marker = nextMarker; nextMarker = null; if (marker.canvas) { var vector = marker.canvas[0]; offsetVector(vector, -marker._minWidth, 0); self.writer.addVector(vector); } else if (marker._inlines) { var markerLine = new Line(self.pageSize.width); markerLine.addInline(marker._inlines[0]); markerLine.x = -marker._minWidth; markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight(); self.writer.addLine(markerLine, true); } } } }
...
}
if (node.stack) {
self.processVerticalContainer(node);
} else if (node.columns) {
self.processColumns(node);
} else if (node.ul) {
self.processList(false, node);
} else if (node.ol) {
self.processList(true, node);
} else if (node.table) {
self.processTable(node);
} else if (node.text !== undefined) {
self.processLeaf(node);
} else if (node.image) {
...
processNode = function (node) { var self = this; this.linearNodeList.push(node); decorateNode(node); applyMargins(function () { var absPosition = node.absolutePosition; if (absPosition) { self.writer.context().beginDetachedBlock(); self.writer.context().moveTo(absPosition.x || 0, absPosition.y || 0); } var relPosition = node.relativePosition; if (relPosition) { self.writer.context().beginDetachedBlock(); self.writer.context().moveTo((relPosition.x || 0) + self.writer.context().x, (relPosition.y || 0) + self.writer.context().y); } if (node.stack) { self.processVerticalContainer(node); } else if (node.columns) { self.processColumns(node); } else if (node.ul) { self.processList(false, node); } else if (node.ol) { self.processList(true, node); } else if (node.table) { self.processTable(node); } else if (node.text !== undefined) { self.processLeaf(node); } else if (node.image) { self.processImage(node); } else if (node.canvas) { self.processCanvas(node); } else if (node.qr) { self.processQr(node); } else if (!node._span) { throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify); } if (absPosition || relPosition) { self.writer.context().endDetachedBlock(); } }); function applyMargins(callback) { var margin = node._margin; if (node.pageBreak === 'before') { self.writer.moveToNextPage(node.pageOrientation); } if (margin) { self.writer.context().moveDown(margin[1]); self.writer.context().addMargin(margin[0], margin[2]); } callback(); if (margin) { self.writer.context().addMargin(-margin[0], -margin[2]); self.writer.context().moveDown(margin[3]); } if (node.pageBreak === 'after') { self.writer.moveToNextPage(node.pageOrientation); } } }
...
var _this = this;
this.writer.context().tracker.startTracking('pageAdded', function () {
_this.addBackground(background);
});
this.addBackground(background);
this.processNode(docStructure);
this.addHeadersAndFooters(header, footer);
/* jshint eqnull:true */
if (watermark != null) {
this.addWatermark(watermark, fontProvider, defaultStyle);
}
return {pages: this.writer.context().pages, linearNodeList: this.linearNodeList};
...
processQr = function (node) { var position = this.writer.addQr(node); node.positions.push(position); }
...
} else if (node.text !== undefined) {
self.processLeaf(node);
} else if (node.image) {
self.processImage(node);
} else if (node.canvas) {
self.processCanvas(node);
} else if (node.qr) {
self.processQr(node);
} else if (!node._span) {
throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify);
}
if (absPosition || relPosition) {
self.writer.context().endDetachedBlock();
}
...
processRow = function (columns, widths, gaps, tableBody, tableRow) { var self = this; var pageBreaks = [], positions = []; this.tracker.auto('pageChanged', storePageBreakData, function () { widths = widths || columns; self.writer.context().beginColumnGroup(); for (var i = 0, l = columns.length; i < l; i++) { var column = columns[i]; var width = widths[i]._calcWidth; var leftOffset = colLeftOffset(i); if (column.colSpan && column.colSpan > 1) { for (var j = 1; j < column.colSpan; j++) { width += widths[++i]._calcWidth + gaps[i]; } } self.writer.context().beginColumn(width, leftOffset, getEndingCell(column, i)); if (!column._span) { self.processNode(column); addAll(positions, column.positions); } else if (column._columnEndingContext) { // row-span ending self.writer.context().markEnding(column); } } self.writer.context().completeColumnGroup(); }); return {pageBreaks: pageBreaks, positions: positions}; function storePageBreakData(data) { var pageDesc; for (var i = 0, l = pageBreaks.length; i < l; i++) { var desc = pageBreaks[i]; if (desc.prevPage === data.prevPage) { pageDesc = desc; break; } } if (!pageDesc) { pageDesc = data; pageBreaks.push(pageDesc); } pageDesc.prevY = Math.max(pageDesc.prevY, data.prevY); pageDesc.y = Math.min(pageDesc.y, data.y); } function colLeftOffset(i) { if (gaps && gaps.length > i) { return gaps[i]; } return 0; } function getEndingCell(column, columnIndex) { if (column.rowSpan && column.rowSpan > 1) { var endingRow = tableRow + column.rowSpan - 1; if (endingRow >= tableBody.length) { throw 'Row span for column ' + columnIndex + ' (with indexes starting from 0) exceeded row count'; } return tableBody[endingRow][columnIndex]; } return null; } }
...
var gaps = gapArray(columnNode._gap);
if (gaps) {
availableWidth -= (gaps.length - 1) * columnNode._gap;
}
ColumnCalculator.buildColumnWidths(columns, availableWidth);
var result = this.processRow(columns, columns, gaps);
addAll(columnNode.positions, result.positions);
function gapArray(gap) {
if (!gap) {
return null;
}
...
processTable = function (tableNode) { var processor = new TableProcessor(tableNode); processor.beginTable(this.writer); for (var i = 0, l = tableNode.table.body.length; i < l; i++) { processor.beginRow(i, this.writer); var result = this.processRow(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body , i); addAll(tableNode.positions, result.positions); processor.endRow(i, this.writer, result.pageBreaks); } processor.endTable(this.writer); }
...
} else if (node.columns) {
self.processColumns(node);
} else if (node.ul) {
self.processList(false, node);
} else if (node.ol) {
self.processList(true, node);
} else if (node.table) {
self.processTable(node);
} else if (node.text !== undefined) {
self.processLeaf(node);
} else if (node.image) {
self.processImage(node);
} else if (node.canvas) {
self.processCanvas(node);
} else if (node.qr) {
...
processVerticalContainer = function (node) { var self = this; node.stack.forEach(function (item) { self.processNode(item); addAll(node.positions, item.positions); //TODO: paragraph gap }); }
...
var relPosition = node.relativePosition;
if (relPosition) {
self.writer.context().beginDetachedBlock();
self.writer.context().moveTo((relPosition.x || 0) + self.writer.context().x, (relPosition.y || 0) + self.writer.context().y);
}
if (node.stack) {
self.processVerticalContainer(node);
} else if (node.columns) {
self.processColumns(node);
} else if (node.ul) {
self.processList(false, node);
} else if (node.ol) {
self.processList(true, node);
} else if (node.table) {
...
registerTableLayouts = function (tableLayouts) { this.tableLayouts = pack(this.tableLayouts, tableLayouts); }
n/a
tryLayoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark, pageBreakBeforeFct ) { this.linearNodeList = []; docStructure = this.docMeasure.measureDocument(docStructure); this.writer = new PageElementWriter( new DocumentContext(this.pageSize, this.pageMargins), this.tracker); var _this = this; this.writer.context().tracker.startTracking('pageAdded', function () { _this.addBackground(background); }); this.addBackground(background); this.processNode(docStructure); this.addHeadersAndFooters(header, footer); /* jshint eqnull:true */ if (watermark != null) { this.addWatermark(watermark, fontProvider, defaultStyle); } return {pages: this.writer.context().pages, linearNodeList: this.linearNodeList}; }
...
function resetXYs(result) {
_.each(result.linearNodeList, function (node) {
node.resetXY();
});
}
var result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle
, background, header, footer, images, watermark);
while (addPageBreaksIfNecessary(result.linearNodeList, result.pages)) {
resetXYs(result);
result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images,
watermark);
}
return result.pages;
};
...
function Line(maxWidth) { this.maxWidth = maxWidth; this.leadingCut = 0; this.trailingCut = 0; this.inlineWidths = 0; this.inlines = []; }
n/a
addInline = function (inline) { if (this.inlines.length === 0) { this.leadingCut = inline.leadingCut || 0; } this.trailingCut = inline.trailingCut || 0; inline.x = this.inlineWidths - this.leadingCut; this.inlines.push(inline); this.inlineWidths += inline.width; if (inline.lineEnd) { this.newLineForced = true; } }
...
if (marker.canvas) {
var vector = marker.canvas[0];
offsetVector(vector, -marker._minWidth, 0);
self.writer.addVector(vector);
} else if (marker._inlines) {
var markerLine = new Line(self.pageSize.width);
markerLine.addInline(marker._inlines[0]);
markerLine.x = -marker._minWidth;
markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight();
self.writer.addLine(markerLine, true);
}
}
}
};
...
getAscenderHeight = function () { var y = 0; this.inlines.forEach(function (inline) { y = Math.max(y, inline.font.ascender / 1000 * inline.fontSize); }); return y; }
...
offsetVector(vector, -marker._minWidth, 0);
self.writer.addVector(vector);
} else if (marker._inlines) {
var markerLine = new Line(self.pageSize.width);
markerLine.addInline(marker._inlines[0]);
markerLine.x = -marker._minWidth;
markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight();
self.writer.addLine(markerLine, true);
}
}
}
};
// tables
...
getHeight = function () { var max = 0; this.inlines.forEach(function (item) { max = Math.max(max, item.height || 0); }); return max; }
...
page.items.push(item);
} else {
page.items.splice(index, 0, item);
}
}
ElementWriter.prototype.addLine = function (line, dontUpdateContextPosition, index) {
var height = line.getHeight();
var context = this.context;
var page = context.getCurrentPage(),
position = this.getCurrentPositionOnPage();
if (context.availableHeight < height || !page) {
return false;
}
...
getWidth = function () { return this.inlineWidths - this.leadingCut - this.trailingCut; }
...
}
return position;
};
ElementWriter.prototype.alignLine = function (line) {
var width = this.context.availableWidth;
var lineWidth = line.getWidth();
var alignment = line.inlines && line.inlines.length > 0 && line.inlines[0].alignment;
var offset = 0;
switch (alignment) {
case 'right':
offset = width - lineWidth;
...
hasEnoughSpaceForInline = function (inline) { if (this.inlines.length === 0) { return true; } if (this.newLineForced) { return false; } return this.inlineWidths + inline.width - this.leadingCut - (inline.trailingCut || 0) <= this.maxWidth; }
...
LayoutBuilder.prototype.buildNextLine = function (textNode) {
if (!textNode._inlines || textNode._inlines.length === 0) {
return null;
}
var line = new Line(this.writer.context().availableWidth);
while (textNode._inlines && textNode._inlines.length > 0 && line.hasEnoughSpaceForInline
(textNode._inlines[0])) {
line.addInline(textNode._inlines.shift());
}
line.lastLineInParagraph = textNode._inlines.length === 0;
return line;
};
...
function PageElementWriter(context, tracker) { this.transactionLevel = 0; this.repeatables = []; this.tracker = tracker; this.writer = new ElementWriter(context, tracker); }
n/a
addFragment = function (fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition) { if (!this.writer.addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition)) { this.moveToNextPage(); this.writer.addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition); } }
...
};
PageElementWriter.prototype.addVector = function (vector, ignoreContextX, ignoreContextY, index) {
return this.writer.addVector(vector, ignoreContextX, ignoreContextY, index);
};
PageElementWriter.prototype.addFragment = function (fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition) {
if (!this.writer.addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition
)) {
this.moveToNextPage();
this.writer.addFragment(fragment, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition);
}
};
PageElementWriter.prototype.moveToNextPage = function (pageOrientation) {
...
addImage = function (image, index) { return fitOnPage(this, function (self) { return self.writer.addImage(image, index); }); }
...
line.lastLineInParagraph = textNode._inlines.length === 0;
return line;
};
// images
LayoutBuilder.prototype.processImage = function (node) {
var position = this.writer.addImage(node);
node.positions.push(position);
};
LayoutBuilder.prototype.processCanvas = function (node) {
var height = node._minHeight;
if (this.writer.context().availableHeight < height) {
...
addLine = function (line, dontUpdateContextPosition, index) { return fitOnPage(this, function (self) { return self.writer.addLine(line, dontUpdateContextPosition, index); }); }
...
offsetVector(vector, -marker._minWidth, 0);
self.writer.addVector(vector);
} else if (marker._inlines) {
var markerLine = new Line(self.pageSize.width);
markerLine.addInline(marker._inlines[0]);
markerLine.x = -marker._minWidth;
markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight();
self.writer.addLine(markerLine, true);
}
}
}
};
// tables
LayoutBuilder.prototype.processTable = function (tableNode) {
...
addQr = function (qr, index) { return fitOnPage(this, function (self) { return self.writer.addQr(qr, index); }); }
...
return fitOnPage(this, function (self) {
return self.writer.addImage(image, index);
});
};
PageElementWriter.prototype.addQr = function (qr, index) {
return fitOnPage(this, function (self) {
return self.writer.addQr(qr, index);
});
};
PageElementWriter.prototype.addVector = function (vector, ignoreContextX, ignoreContextY, index) {
return this.writer.addVector(vector, ignoreContextX, ignoreContextY, index);
};
...
addVector = function (vector, ignoreContextX, ignoreContextY, index) { return this.writer.addVector(vector, ignoreContextX, ignoreContextY, index); }
...
this.alignImage(qr);
for (var i = 0, l = qr._canvas.length; i < l; i++) {
var vector = qr._canvas[i];
vector.x += qr.x;
vector.y += qr.y;
this.addVector(vector, true, true, index);
}
context.moveDown(qr._height);
return position;
};
...
beginUnbreakableBlock = function (width, height) { if (this.transactionLevel++ === 0) { this.originalX = this.writer.context.x; this.writer.pushContext(width, height); } }
...
return background;
};
var pageBackground = backgroundGetter(this.writer.context().page + 1);
if (pageBackground) {
var pageSize = this.writer.context().getCurrentPage().pageSize;
this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
this.processNode(this.docMeasure.measureDocument(pageBackground));
this.writer.commitUnbreakableBlock(0, 0);
}
};
LayoutBuilder.prototype.addStaticRepeatable = function (headerOrFooter, sizeFunction) {
this.addDynamicRepeatable(function () {
...
commitUnbreakableBlock = function (forcedX, forcedY) { if (--this.transactionLevel === 0) { var unbreakableContext = this.writer.context; this.writer.popContext(); var nbPages = unbreakableContext.pages.length; if (nbPages > 0) { // no support for multi-page unbreakableBlocks var fragment = unbreakableContext.pages[0]; fragment.xOffset = forcedX; fragment.yOffset = forcedY; //TODO: vectors can influence height in some situations if (nbPages > 1) { // on out-of-context blocs (headers, footers, background) height should be the whole DocumentContext height if (forcedX !== undefined || forcedY !== undefined) { fragment.height = unbreakableContext.getCurrentPage().pageSize.height - unbreakableContext.pageMargins.top - unbreakableContext .pageMargins.bottom; } else { fragment.height = this.writer.context.getCurrentPage().pageSize.height - this.writer.context.pageMargins.top - this.writer. context.pageMargins.bottom; for (var i = 0, l = this.repeatables.length; i < l; i++) { fragment.height -= this.repeatables[i].height; } } } else { fragment.height = unbreakableContext.y; } if (forcedX !== undefined || forcedY !== undefined) { this.writer.addFragment(fragment, true, true, true); } else { this.addFragment(fragment); } } } }
...
var pageBackground = backgroundGetter(this.writer.context().page + 1);
if (pageBackground) {
var pageSize = this.writer.context().getCurrentPage().pageSize;
this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
this.processNode(this.docMeasure.measureDocument(pageBackground));
this.writer.commitUnbreakableBlock(0, 0);
}
};
LayoutBuilder.prototype.addStaticRepeatable = function (headerOrFooter, sizeFunction) {
this.addDynamicRepeatable(function () {
return headerOrFooter;
}, sizeFunction);
...
context = function () { return this.writer.context; }
...
this.linearNodeList = [];
docStructure = this.docMeasure.measureDocument(docStructure);
this.writer = new PageElementWriter(
new DocumentContext(this.pageSize, this.pageMargins), this.tracker);
var _this = this;
this.writer.context().tracker.startTracking('pageAdded', function () {
_this.addBackground(background);
});
this.addBackground(background);
this.processNode(docStructure);
this.addHeadersAndFooters(header, footer);
/* jshint eqnull:true */
...
currentBlockToRepeatable = function () { var unbreakableContext = this.writer.context; var rep = {items: []}; unbreakableContext.pages[0].items.forEach(function (item) { rep.items.push(item); }); rep.xOffset = this.originalX; //TODO: vectors can influence height in some situations rep.height = unbreakableContext.y; return rep; }
...
this.rowSpanData[i].rowSpan--;
}
}
this.drawHorizontalLine(rowIndex + 1, writer);
if (this.headerRows && rowIndex === this.headerRows - 1) {
this.headerRepeatable = writer.currentBlockToRepeatable();
}
if (this.dontBreakRows) {
writer.tracker.auto('pageChanged',
function () {
if (!self.headerRows && self.layout.hLineWhenBroken !== false) {
self.drawHorizontalLine(rowIndex, writer);
...
moveToNextPage = function (pageOrientation) { var nextPage = this.writer.context.moveToNextPage(pageOrientation); if (nextPage.newPageCreated) { this.repeatables.forEach(function (rep) { this.writer.addFragment(rep, true); }, this); } else { this.repeatables.forEach(function (rep) { this.writer.context.moveDown(rep.height); }, this); } this.writer.tracker.emit('pageChanged', { prevPage: nextPage.prevPage, prevY: nextPage.prevY, y: nextPage.y }); }
...
}
});
function applyMargins(callback) {
var margin = node._margin;
if (node.pageBreak === 'before') {
self.writer.moveToNextPage(node.pageOrientation);
}
if (margin) {
self.writer.context().moveDown(margin[1]);
self.writer.context().addMargin(margin[0], margin[2]);
}
...
popFromRepeatables = function () { this.repeatables.pop(); }
...
lineWidth: width,
lineColor: typeof this.layout.vLineColor === 'function' ? this.layout.vLineColor(vLineIndex, this.tableNode) : this.
layout.vLineColor
}, false, true);
};
TableProcessor.prototype.endTable = function (writer) {
if (this.cleanUpRepeatables) {
writer.popFromRepeatables();
}
};
TableProcessor.prototype.endRow = function (rowIndex, writer, pageBreaks) {
var l, i;
var self = this;
writer.tracker.stopTracking('pageChanged', this.rowCallback);
...
pushToRepeatables = function (rep) { this.repeatables.push(rep); }
...
writer.commitUnbreakableBlock();
}
);
}
if (this.headerRepeatable && (rowIndex === (this.rowsWithoutPageBreak - 1) || rowIndex === this.tableNode.table.body.
length - 1)) {
writer.commitUnbreakableBlock();
writer.pushToRepeatables(this.headerRepeatable);
this.cleanUpRepeatables = true;
this.headerRepeatable = null;
}
function getLineXs() {
var result = [];
var cols = 0;
...
createPdf = function (docDefinition) { if (!canCreatePdf()) { throw 'Your browser does not provide the level of support needed'; } return new Document(docDefinition, window.pdfMake.tableLayouts, window.pdfMake.fonts, window.pdfMake.vfs); }
...
or become pretty complex (having multi-level tables, images, lists, paragraphs, margins, styles etc...).
As soon as you have the document-definition-object, you're ready to create and open/print/download the PDF:
```js
// open the PDF in a new window
pdfMake.createPdf(docDefinition).open();
// print the PDF
pdfMake.createPdf(docDefinition).print();
// download the PDF
pdfMake.createPdf(docDefinition).download();
...
function measure(node) { var cd = buildCanvas(node.qr, node); node._canvas = cd.canvas; node._width = node._height = node._minWidth = node._maxWidth = node._minHeight = node._maxHeight = cd.size; return node; }
n/a
function StyleContextStack(styleDictionary, defaultStyle) { this.defaultStyle = defaultStyle || {}; this.styleDictionary = styleDictionary; this.styleOverrides = []; }
n/a
auto = function (item, callback) { var pushedItems = this.autopush(item); var result = callback(); if (pushedItems > 0) { this.pop(pushedItems); } return result; }
...
if (Object.keys(node).length === 0) {
// A warning could be logged: console.warn('pdfmake: Empty node, ignoring it');
node = {text: ''};
}
var self = this;
return this.styleStack.auto(node, function () {
// TODO: refactor + rethink whether this is the proper way to handle margins
node._margin = getNodeMargin(node);
if (node.columns) {
return extendMargins(self.measureColumns(node));
} else if (node.stack) {
return extendMargins(self.measureVerticalContainer(node));
...
autopush = function (item) { if (typeof item === 'string' || item instanceof String) { return 0; } var styleNames = []; if (item.style) { if (Array.isArray(item.style)) { styleNames = item.style; } else { styleNames = [item.style]; } } for (var i = 0, l = styleNames.length; i < l; i++) { this.push(styleNames[i]); } var styleOverrideObject = {}; var pushSOO = false; [ 'font', 'fontSize', 'bold', 'italics', 'alignment', 'color', 'columnGap', 'fillColor', 'decoration', 'decorationStyle', 'decorationColor', 'background', 'lineHeight', 'noWrap', 'markerColor' //'tableCellPadding' // 'cellBorder', // 'headerCellBorder', // 'oddRowCellBorder', // 'evenRowCellBorder', // 'tableBorder' ].forEach(function (key) { if (item[key] !== undefined && item[key] !== null) { styleOverrideObject[key] = item[key]; pushSOO = true; } }); if (pushSOO) { this.push(styleOverrideObject); } return styleNames.length + (pushSOO ? 1 : 0); }
...
* executes callback and then pops elements back. Returns value returned by callback
*
* @param {Object} item - an object with optional style property and/or style overrides
* @param {Function} function to be called between autopush and pop
* @return {Object} value returned by callback
*/
StyleContextStack.prototype.auto = function (item, callback) {
var pushedItems = this.autopush(item);
var result = callback();
if (pushedItems > 0) {
this.pop(pushedItems);
}
return result;
...
clone = function () { var stack = new StyleContextStack(this.styleDictionary, this.defaultStyle); this.styleOverrides.forEach(function (item) { stack.styleOverrides.push(item); }); return stack; }
...
return node;
};
DocMeasure.prototype.measureLeaf = function (node) {
// Make sure style properties of the node itself are considered when building inlines.
// We could also just pass [node] to buildInlines, but that fails for bullet points.
var styleStack = this.styleStack.clone();
styleStack.push(node);
var data = this.textTools.buildInlines(node.text, styleStack);
node._inlines = data.items;
node._minWidth = data.minWidth;
node._maxWidth = data.maxWidth;
...
getProperty = function (property) { if (this.styleOverrides) { for (var i = this.styleOverrides.length - 1; i >= 0; i--) { var item = this.styleOverrides[i]; if (typeof item === 'string' || item instanceof String) { // named-style-override var style = this.styleDictionary[item]; if (style && style[property] !== null && style[property] !== undefined) { return style[property]; } } else { // style-overrides-object if (item[property] !== undefined && item[property] !== null) { return item[property]; } } } } return this.defaultStyle && this.defaultStyle[property]; }
...
node._width = node._minWidth = node._maxWidth = imageSize.width * factor;
node._height = imageSize.height * factor;
} else {
node._width = node._minWidth = node._maxWidth = node.width || imageSize.width;
node._height = node.height || (imageSize.height * node._width / imageSize.width);
}
node._alignment = this.styleStack.getProperty('alignment');
return node;
};
DocMeasure.prototype.measureLeaf = function (node) {
// Make sure style properties of the node itself are considered when building inlines.
// We could also just pass [node] to buildInlines, but that fails for bullet points.
...
pop = function (howMany) { howMany = howMany || 1; while (howMany-- > 0) { this.styleOverrides.pop(); } }
...
availableHeight: this.availableHeight,
availableWidth: this.availableWidth,
lastColumnWidth: this.lastColumnWidth
};
};
DocumentContext.prototype.completeColumnGroup = function () {
var saved = this.snapshots.pop();
this.calculateBottomMost(saved);
this.endingCell = null;
this.x = saved.x;
this.y = saved.bottomMost.y;
this.page = saved.bottomMost.page;
...
push = function (styleNameOrOverride) { this.styleOverrides.push(styleNameOrOverride); }
...
starMaxMin = 0,
starMaxMax = 0,
fixedColumns = [],
initial_availableWidth = availableWidth;
columns.forEach(function (column) {
if (isAutoColumn(column)) {
autoColumns.push(column);
autoMin += column._minWidth;
autoMax += column._maxWidth;
} else if (isStarColumn(column)) {
starColumns.push(column);
starMaxMin = Math.max(starMaxMin, column._minWidth);
starMaxMax = Math.max(starMaxMax, column._maxWidth);
} else {
...
function TableProcessor(tableNode) { this.tableNode = tableNode; }
n/a
beginRow = function (rowIndex, writer) { this.topLineWidth = this.layout.hLineWidth(rowIndex, this.tableNode); this.rowPaddingTop = this.layout.paddingTop(rowIndex, this.tableNode); this.bottomLineWidth = this.layout.hLineWidth(rowIndex + 1, this.tableNode); this.rowPaddingBottom = this.layout.paddingBottom(rowIndex, this.tableNode); this.rowCallback = this.onRowBreak(rowIndex, writer); writer.tracker.startTracking('pageChanged', this.rowCallback); if (this.dontBreakRows) { writer.beginUnbreakableBlock(); } this.rowTopY = writer.context().y; this.reservedAtBottom = this.bottomLineWidth + this.rowPaddingBottom; writer.context().availableHeight -= this.reservedAtBottom; writer.context().moveDown(this.rowPaddingTop); }
...
// tables
LayoutBuilder.prototype.processTable = function (tableNode) {
var processor = new TableProcessor(tableNode);
processor.beginTable(this.writer);
for (var i = 0, l = tableNode.table.body.length; i < l; i++) {
processor.beginRow(i, this.writer);
var result = this.processRow(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body
, i);
addAll(tableNode.positions, result.positions);
processor.endRow(i, this.writer, result.pageBreaks);
}
...
beginTable = function (writer) { var tableNode; var availableWidth; var self = this; tableNode = this.tableNode; this.offsets = tableNode._offsets; this.layout = tableNode._layout; availableWidth = writer.context().availableWidth - this.offsets.total; ColumnCalculator.buildColumnWidths(tableNode.table.widths, availableWidth); this.tableWidth = tableNode._offsets.total + getTableInnerContentWidth(); this.rowSpanData = prepareRowSpanData(); this.cleanUpRepeatables = false; this.headerRows = tableNode.table.headerRows || 0; this.rowsWithoutPageBreak = this.headerRows + (tableNode.table.keepWithHeaderRows || 0); this.dontBreakRows = tableNode.table.dontBreakRows || false; if (this.rowsWithoutPageBreak) { writer.beginUnbreakableBlock(); } // update the border properties of all cells before drawing any lines prepareCellBorders(this.tableNode.table.body); this.drawHorizontalLine(0, writer); function getTableInnerContentWidth() { var width = 0; tableNode.table.widths.forEach(function (w) { width += w._calcWidth; }); return width; } function prepareRowSpanData() { var rsd = []; var x = 0; var lastWidth = 0; rsd.push({left: 0, rowSpan: 0}); for (var i = 0, l = self.tableNode.table.body[0].length; i < l; i++) { var paddings = self.layout.paddingLeft(i, self.tableNode) + self.layout.paddingRight(i, self.tableNode); var lBorder = self.layout.vLineWidth(i, self.tableNode); lastWidth = paddings + lBorder + self.tableNode.table.widths[i]._calcWidth; rsd[rsd.length - 1].width = lastWidth; x += lastWidth; rsd.push({left: x, rowSpan: 0, width: 0}); } return rsd; } // Iterate through all cells. If the current cell is the start of a // rowSpan/colSpan, update the border property of the cells on its // bottom/right accordingly. This is needed since each iteration of the // line-drawing loops draws lines for a single cell, not for an entire // rowSpan/colSpan. function prepareCellBorders(body) { for (var rowIndex = 0; rowIndex < body.length; rowIndex++) { var row = body[rowIndex]; for (var colIndex = 0; colIndex < row.length; colIndex++) { var cell = row[colIndex]; if (cell.border) { var rowSpan = cell.rowSpan || 1; var colSpan = cell.colSpan || 1; for (var rowOffset = 0; rowOffset < rowSpan; rowOffset++) { // set left border if (cell.border[0] !== undefined && rowOffset > 0) { setBorder(rowIndex + rowOffset, colIndex, 0, cell.border[0]); } // set right border if (cell.border[2] !== undefined) { setBorder(rowIndex + rowOffset, colIndex + colSpan - 1, 2, cell.border[2]); } } for (var colOffset = 0; colOffset < colSpan; colOffset++) { // set top border if (cell.border[1] !== undefined && colOffset > 0) { setBorder(rowIndex, colIndex + colOffset, 1, cell.border[1]); } // set bottom border if (cell.border[3] !== undefined) { setBorder(rowIndex + rowSpan - 1, colIndex + colOffset, 3, cell.border[3]); } } } } } // helper function to set the border for a given cell function setBorder(rowIndex, colIndex, borderIndex, borderValue) { var cell = body[rowIndex][colIndex]; cell.border = cell.border || {}; cell.border[borderIndex] = borderValue; } } }
...
}
};
// tables
LayoutBuilder.prototype.processTable = function (tableNode) {
var processor = new TableProcessor(tableNode);
processor.beginTable(this.writer);
for (var i = 0, l = tableNode.table.body.length; i < l; i++) {
processor.beginRow(i, this.writer);
var result = this.processRow(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body
, i);
addAll(tableNode.positions, result.positions);
...
drawHorizontalLine = function (lineIndex, writer, overrideY) { var lineWidth = this.layout.hLineWidth(lineIndex, this.tableNode); if (lineWidth) { var offset = lineWidth / 2; var currentLine = null; var body = this.tableNode.table.body; for (var i = 0, l = this.rowSpanData.length; i < l; i++) { var data = this.rowSpanData[i]; var shouldDrawLine = !data.rowSpan; // draw only if the current cell requires a top border or the cell in the // row above requires a bottom border if (shouldDrawLine && i < l - 1) { var topBorder = false, bottomBorder = false; // the current cell if (lineIndex < body.length) { var cell = body[lineIndex][i]; topBorder = cell.border ? cell.border[1] : this.layout.defaultBorder; } // the cell in the row above if (lineIndex > 0) { var cellAbove = body[lineIndex - 1][i]; bottomBorder = cellAbove.border ? cellAbove.border[3] : this.layout.defaultBorder; } shouldDrawLine = topBorder || bottomBorder; } if (!currentLine && shouldDrawLine) { currentLine = {left: data.left, width: 0}; } if (shouldDrawLine) { currentLine.width += (data.width || 0); } var y = (overrideY || 0) + offset; if (!shouldDrawLine || i === l - 1) { if (currentLine && currentLine.width) { writer.addVector({ type: 'line', x1: currentLine.left, x2: currentLine.left + currentLine.width, y1: y, y2: y, lineWidth: lineWidth, lineColor: typeof this.layout.hLineColor === 'function' ? this.layout.hLineColor(lineIndex, this.tableNode) : this.layout. hLineColor }, false, overrideY); currentLine = null; } } } writer.context().moveDown(lineWidth); } }
...
if (this.rowsWithoutPageBreak) {
writer.beginUnbreakableBlock();
}
// update the border properties of all cells before drawing any lines
prepareCellBorders(this.tableNode.table.body);
this.drawHorizontalLine(0, writer);
function getTableInnerContentWidth() {
var width = 0;
tableNode.table.widths.forEach(function (w) {
width += w._calcWidth;
});
...
drawVerticalLine = function (x, y0, y1, vLineIndex, writer) { var width = this.layout.vLineWidth(vLineIndex, this.tableNode); if (width === 0) { return; } writer.addVector({ type: 'line', x1: x + width / 2, x2: x + width / 2, y1: y0, y2: y1, lineWidth: width, lineColor: typeof this.layout.vLineColor === 'function' ? this.layout.vLineColor(vLineIndex, this.tableNode) : this.layout.vLineColor }, false, true); }
...
// the cell from before column
if (colIndex > 0) {
var cell = body[rowIndex][colIndex - 1];
rightBorder = cell.border ? cell.border[2] : this.layout.defaultBorder;
}
if (leftBorder || rightBorder) {
this.drawVerticalLine(xs[i].x, y1 - hzLineOffset, y2 + this.bottomLineWidth, xs[i
].index, writer);
}
if (i < l - 1) {
var fillColor = body[rowIndex][colIndex].fillColor;
if (!fillColor) {
fillColor = typeof this.layout.fillColor === 'function' ? this.layout.fillColor(rowIndex, this.tableNode) : this.
layout.fillColor;
}
...
endRow = function (rowIndex, writer, pageBreaks) { var l, i; var self = this; writer.tracker.stopTracking('pageChanged', this.rowCallback); writer.context().moveDown(this.layout.paddingBottom(rowIndex, this.tableNode)); writer.context().availableHeight += this.reservedAtBottom; var endingPage = writer.context().page; var endingY = writer.context().y; var xs = getLineXs(); var ys = []; var hasBreaks = pageBreaks && pageBreaks.length > 0; var body = this.tableNode.table.body; ys.push({ y0: this.rowTopY, page: hasBreaks ? pageBreaks[0].prevPage : endingPage }); if (hasBreaks) { for (i = 0, l = pageBreaks.length; i < l; i++) { var pageBreak = pageBreaks[i]; ys[ys.length - 1].y1 = pageBreak.prevY; ys.push({y0: pageBreak.y, page: pageBreak.prevPage + 1}); } } ys[ys.length - 1].y1 = endingY; var skipOrphanePadding = (ys[0].y1 - ys[0].y0 === this.rowPaddingTop); for (var yi = (skipOrphanePadding ? 1 : 0), yl = ys.length; yi < yl; yi++) { var willBreak = yi < ys.length - 1; var rowBreakWithoutHeader = (yi > 0 && !this.headerRows); var hzLineOffset = rowBreakWithoutHeader ? 0 : this.topLineWidth; var y1 = ys[yi].y0; var y2 = ys[yi].y1; if (willBreak) { y2 = y2 + this.rowPaddingBottom; } if (writer.context().page != ys[yi].page) { writer.context().page = ys[yi].page; //TODO: buggy, availableHeight should be updated on every pageChanged event // TableProcessor should be pageChanged listener, instead of processRow this.reservedAtBottom = 0; } for (i = 0, l = xs.length; i < l; i++) { var leftBorder = false, rightBorder = false; var colIndex = xs[i].index; // the current cell if (colIndex < body[rowIndex].length) { var cell = body[rowIndex][colIndex]; leftBorder = cell.border ? cell.border[0] : this.layout.defaultBorder; } // the cell from before column if (colIndex > 0) { var cell = body[rowIndex][colIndex - 1]; rightBorder = cell.border ? cell.border[2] : this.layout.defaultBorder; } if (leftBorder || rightBorder) { this.drawVerticalLine(xs[i].x, y1 - hzLineOffset, y2 + this.bottomLineWidth, xs[i].index, writer); } if (i < l - 1) { var fillColor = body[rowIndex][colIndex].fillColor; if (!fillColor) { fillColor = typeof this.layout.fillColor === 'function' ? this.layout.fillColor(rowIndex, this.tableNode) : this.layout.fillColor ; } if (fillColor) { var wBorder = (leftBorder || rightBorder) ? this.layout.vLineWidth(colIndex, this.tableNode) : 0; var xf = xs[i].x + wBorder; var yf = this.dontBreakRows ? y1 : y1 - hzLineOffset; writer.addVector({ type: 'rect', x: xf, y: yf, w: xs[i + 1].x - xf, h: y2 + this.bottomLineWidth - yf, lineWidth: 0, color: fillColor }, false, true, 0); } } } if (willBreak && this.layout.hLineWhenBroken !== false) { this.drawHorizontalLine(rowIndex + 1, writer, y2); } if (rowBreakWithoutHeader && this.layout.hLineWhenBroken !== false) { this.drawHorizontalLine(rowIndex, writer, y1); } } writer.context().page = endingPage; writer.context().y = endingY; var row = this.tableNode.table.body[rowIndex]; for (i = 0, l = row.length; i < l; i++) { if (row[i].rowSpan) { this.rowSpanData[i].rowSpan = row[i].rowSpan; // fix colSpans if (row[i].colSpan && row[i].colSpan > 1) { for (var j = 1; j < row[i].rowSpan; j++) { this.tableNode.table.body[rowIndex + j][i]._colSpan = row[i].colSpan; } } } if (this.rowSpanData[i].rowSpan > 0) { this.rowSpanData[i].rowSpan--; } } this.drawHorizontalLine(rowIndex + 1, writer); if (this.headerRows && rowIndex === this.headerRows - 1) { this.headerRepeatable = writer.currentBlockToRepeatable(); } if (this.dontBreakRows) { writer.tracker.auto('pageChanged', function () { if (!self.headerRows && self.layout.hLineWhenBroken !== false) { self.drawHorizontalL ...
...
for (var i = 0, l = tableNode.table.body.length; i < l; i++) {
processor.beginRow(i, this.writer);
var result = this.processRow(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body
, i);
addAll(tableNode.positions, result.positions);
processor.endRow(i, this.writer, result.pageBreaks);
}
processor.endTable(this.writer);
};
// leafs (texts)
LayoutBuilder.prototype.processLeaf = function (node) {
...
endTable = function (writer) { if (this.cleanUpRepeatables) { writer.popFromRepeatables(); } }
...
var result = this.processRow(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body
, i);
addAll(tableNode.positions, result.positions);
processor.endRow(i, this.writer, result.pageBreaks);
}
processor.endTable(this.writer);
};
// leafs (texts)
LayoutBuilder.prototype.processLeaf = function (node) {
var line = this.buildNextLine(node);
var currentHeight = (line) ? line.getHeight() : 0;
var maxHeight = node.maxHeight || -1;
...
onRowBreak = function (rowIndex, writer) { var self = this; return function () { //console.log('moving by : ', topLineWidth, rowPaddingTop); var offset = self.rowPaddingTop + (!self.headerRows ? self.topLineWidth : 0); writer.context().moveDown(offset); }; }
...
TableProcessor.prototype.beginRow = function (rowIndex, writer) {
this.topLineWidth = this.layout.hLineWidth(rowIndex, this.tableNode);
this.rowPaddingTop = this.layout.paddingTop(rowIndex, this.tableNode);
this.bottomLineWidth = this.layout.hLineWidth(rowIndex + 1, this.tableNode);
this.rowPaddingBottom = this.layout.paddingBottom(rowIndex, this.tableNode);
this.rowCallback = this.onRowBreak(rowIndex, writer);
writer.tracker.startTracking('pageChanged', this.rowCallback);
if (this.dontBreakRows) {
writer.beginUnbreakableBlock();
}
this.rowTopY = writer.context().y;
this.reservedAtBottom = this.bottomLineWidth + this.rowPaddingBottom;
...
function drawBackground(line, x, y, pdfKitDoc) { var height = line.getHeight(); for (var i = 0, l = line.inlines.length; i < l; i++) { var inline = line.inlines[i]; if (inline.background) { pdfKitDoc.fillColor(inline.background) .rect(x + inline.x, y, inline.width, height) .fill(); } } }
n/a
function drawDecorations(line, x, y, pdfKitDoc) { var groups = groupDecorations(line); for (var i = 0, l = groups.length; i < l; i++) { drawDecoration(groups[i], x, y, pdfKitDoc); } }
n/a
function TextTools(fontProvider) { this.fontProvider = fontProvider; }
n/a
buildInlines = function (textArray, styleContextStack) { var measured = measure(this.fontProvider, textArray, styleContextStack); var minWidth = 0, maxWidth = 0, currentLineWidth; measured.forEach(function (inline) { minWidth = Math.max(minWidth, inline.width - inline.leadingCut - inline.trailingCut); if (!currentLineWidth) { currentLineWidth = {width: 0, leadingCut: inline.leadingCut, trailingCut: 0}; } currentLineWidth.width += inline.width; currentLineWidth.trailingCut = inline.trailingCut; maxWidth = Math.max(maxWidth, getTrimmedWidth(currentLineWidth)); if (inline.lineEnd) { currentLineWidth = null; } }); if (getStyleProperty({}, styleContextStack, 'noWrap', false)) { minWidth = maxWidth; } return { items: measured, minWidth: minWidth, maxWidth: maxWidth }; function getTrimmedWidth(item) { return Math.max(0, item.width - item.leadingCut - item.trailingCut); } }
...
DocMeasure.prototype.measureLeaf = function (node) {
// Make sure style properties of the node itself are considered when building inlines.
// We could also just pass [node] to buildInlines, but that fails for bullet points.
var styleStack = this.styleStack.clone();
styleStack.push(node);
var data = this.textTools.buildInlines(node.text, styleStack);
node._inlines = data.items;
node._minWidth = data.minWidth;
node._maxWidth = data.maxWidth;
return node;
};
...
sizeOfString = function (text, styleContextStack) { text = text ? text.toString().replace('\t', ' ') : ''; //TODO: refactor - extract from measure var fontName = getStyleProperty({}, styleContextStack, 'font', 'Roboto'); var fontSize = getStyleProperty({}, styleContextStack, 'fontSize', 12); var bold = getStyleProperty({}, styleContextStack, 'bold', false); var italics = getStyleProperty({}, styleContextStack, 'italics', false); var lineHeight = getStyleProperty({}, styleContextStack, 'lineHeight', 1); var font = this.fontProvider.provideFont(fontName, bold, italics); return { width: font.widthOfString(text, fontSize), height: font.lineHeight(fontSize) * lineHeight, fontSize: fontSize, lineHeight: lineHeight, ascender: font.ascender / 1000 * fontSize, descender: font.descender / 1000 * fontSize }; }
...
node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth);
}
return node;
};
DocMeasure.prototype.gapSizeForList = function () {
return this.textTools.sizeOfString('9. ', this.styleStack);
};
DocMeasure.prototype.buildUnorderedMarker = function (styleStack, gapSize, type) {
function buildDisc(gapSize, color) {
// TODO: ascender-based calculations
var radius = gapSize.fontSize / 6;
return {
...
function TraversalTracker() { this.events = {}; }
n/a
auto = function (event, cb, innerBlock) { this.startTracking(event, cb); innerBlock(); this.stopTracking(event, cb); }
...
if (Object.keys(node).length === 0) {
// A warning could be logged: console.warn('pdfmake: Empty node, ignoring it');
node = {text: ''};
}
var self = this;
return this.styleStack.auto(node, function () {
// TODO: refactor + rethink whether this is the proper way to handle margins
node._margin = getNodeMargin(node);
if (node.columns) {
return extendMargins(self.measureColumns(node));
} else if (node.stack) {
return extendMargins(self.measureVerticalContainer(node));
...
emit = function (event) { var args = Array.prototype.slice.call(arguments, 1); var callbacks = this.events[event]; if (callbacks) { callbacks.forEach(function (cb) { cb.apply(this, args); }); } }
...
DocumentContext.prototype.addPage = function (pageSize) {
var page = {items: [], pageSize: pageSize};
this.pages.push(page);
this.page = this.pages.length - 1;
this.initializePage();
this.tracker.emit('pageAdded');
return page;
};
DocumentContext.prototype.getCurrentPage = function () {
if (this.page < 0 || this.page >= this.pages.length) {
return null;
...
startTracking = function (event, cb) { var callbacks = (this.events[event] || (this.events[event] = [])); if (callbacks.indexOf(cb) < 0) { callbacks.push(cb); } }
...
this.linearNodeList = [];
docStructure = this.docMeasure.measureDocument(docStructure);
this.writer = new PageElementWriter(
new DocumentContext(this.pageSize, this.pageMargins), this.tracker);
var _this = this;
this.writer.context().tracker.startTracking('pageAdded', function () {
_this.addBackground(background);
});
this.addBackground(background);
this.processNode(docStructure);
this.addHeadersAndFooters(header, footer);
/* jshint eqnull:true */
...
stopTracking = function (event, cb) { var callbacks = this.events[event]; if (callbacks) { var index = callbacks.indexOf(cb); if (index >= 0) { callbacks.splice(index, 1); } } }
...
writer.popFromRepeatables();
}
};
TableProcessor.prototype.endRow = function (rowIndex, writer, pageBreaks) {
var l, i;
var self = this;
writer.tracker.stopTracking('pageChanged', this.rowCallback);
writer.context().moveDown(this.layout.paddingBottom(rowIndex, this.tableNode));
writer.context().availableHeight += this.reservedAtBottom;
var endingPage = writer.context().page;
var endingY = writer.context().y;
var xs = getLineXs();
...