123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- /**
- * jsPDF AutoTable plugin
- * Copyright (c) 2014 Simon Bengtsson, https://github.com/someatoms/jsPDF-AutoTable
- *
- * Licensed under the MIT License.
- * http://opensource.org/licenses/mit-license
- */
- (function (API) {
- 'use strict';
- // On every new jsPDF object, clear variables
- API.events.push(['initialized', function () {
- doc = undefined;
- cellPos = undefined;
- pageCount = 1;
- settings = undefined;
- }], false);
- var MIN_COLUMN_WIDTH = 25;
- var doc, cellPos, pageCount = 1, settings;
- // See README.md or examples for documentation of the options
- // return a new instance every time to avoid references issues
- var defaultOptions = function () {
- return {
- padding: 5,
- fontSize: 10,
- lineHeight: 20,
- renderHeader: function (doc, pageNumber, settings) {
- },
- renderFooter: function (doc, lastCellPos, pageNumber, settings) {
- },
- renderHeaderCell: function (x, y, width, height, key, value, settings) {
- doc.setFillColor(52, 73, 94); // Asphalt
- doc.setTextColor(255, 255, 255);
- doc.setFontStyle('bold');
- doc.rect(x, y, width, height, 'F');
- y += settings.lineHeight / 2 + doc.internal.getLineHeight() / 2;
- doc.text(value, x + settings.padding, y);
- },
- renderCell: function (x, y, width, height, key, value, row, settings) {
- doc.setFillColor(row % 2 === 0 ? 245 : 255);
- doc.setTextColor(50);
- doc.rect(x, y, width, height, 'F');
- y += settings.lineHeight / 2 + doc.internal.getLineHeight() / 2 - 2.5;
- doc.text(value, x + settings.padding, y);
- },
- margins: {right: 40, left: 40, top: 50, bottom: 40},
- startY: false,
- overflow: 'ellipsize', // false, ellipsize or linebreak (false passes the raw text to renderCell)
- overflowColumns: false, // Specify which colums that gets subjected to the overflow method chosen. false indicates all
- avoidPageSplit: false,
- extendWidth: true
- }
- };
- /**
- * Create a table from a set of rows and columns.
- *
- * @param {Object[]|String[]} columns Either as an array of objects or array of strings
- * @param {Object[][]|String[][]} data Either as an array of objects or array of strings
- * @param {Object} [options={}] Options that will override the default ones (above)
- */
- API.autoTable = function (columns, data, options) {
- options = options || {};
- columns = columns || [];
- doc = this;
- var userFontSize = doc.internal.getFontSize();
- initData({columns: columns, data: data});
- initOptions(options);
- cellPos = {
- x: settings.margins.left,
- y: settings.startY === false ? settings.margins.top : settings.startY
- };
- var tableHeight = settings.margins.bottom + settings.margins.top + settings.lineHeight * (data.length + 1) + 5 + settings.startY;
- if (settings.startY !== false && settings.avoidPageSplit && tableHeight > doc.internal.pageSize.height) {
- pageCount++;
- doc.addPage();
- cellPos.y = settings.margins.top;
- }
- settings.renderHeader(doc, pageCount, settings);
- var columnWidths = calculateColumnWidths(data, columns);
- printHeader(columns, columnWidths);
- printRows(columns, data, columnWidths);
- settings.renderFooter(doc, cellPos, pageCount, settings);
- doc.setFontSize(userFontSize);
- return this;
- };
- /**
- * Returns the Y position of the last drawn cell
- * @returns int
- */
- API.autoTableEndPosY = function () {
- // If cellPos is not set, autoTable() has probably not been called
- return cellPos ? cellPos.y : false;
- };
- /**
- * @deprecated Use autoTableEndPosY()
- */
- API.autoTableEndPos = function () {
- return cellPos;
- };
- /**
- * Parses an html table. To draw a table, use it like this:
- * `doc.autoTable(false, doc.autoTableHtmlToJson(tableDomElem))`
- *
- * @param table Html table element
- * @param indexBased Boolean flag if result should be returned as seperate cols and data
- * @returns []|{} Array of objects with object keys as headers or based on indexes if indexBased is set to true
- */
- API.autoTableHtmlToJson = function (table, indexBased) {
- var data = [], headers = {}, header = table.rows[0], i, tableRow, rowData, j;
- if (indexBased) {
- headers = [];
- for (i = 0; i < header.cells.length; i++) {
- headers.push(header.cells[i] ? header.cells[i].textContent : '');
- }
- for (i = 1; i < table.rows.length; i++) {
- tableRow = table.rows[i];
- rowData = [];
- for (j = 0; j < header.cells.length; j++) {
- rowData.push(tableRow.cells[j] ? tableRow.cells[j].textContent : '');
- }
- data.push(rowData);
- }
- return {columns: headers, data: data};
- } else {
- for (i = 0; i < header.cells.length; i++) {
- headers[i] = header.cells[i] ? header.cells[i].textContent : '';
- }
- for (i = 1; i < table.rows.length; i++) {
- tableRow = table.rows[i];
- rowData = {};
- for (j = 0; j < header.cells.length; j++) {
- rowData[headers[j]] = tableRow.cells[j] ? tableRow.cells[j].textContent : '';
- }
- data.push(rowData);
- }
- return data;
- }
- };
- /**
- * Transform all to the object initialization form
- * @param params
- */
- function initData(params) {
- // Object only initial
- if (!params.columns || params.columns.length === 0) {
- var keys = Object.keys(params.data[0]);
- Array.prototype.push.apply(params.columns, keys);
- params.columns.forEach(function (title, i) {
- params.columns[i] = {title: title, key: keys[i]};
- });
- }
- // Array initialization form
- else if (typeof params.columns[0] === 'string') {
- params.data.forEach(function (row, i) {
- var obj = {};
- for (var j = 0; j < row.length; j++) {
- obj[j] = params.data[i][j];
- }
- params.data[i] = obj;
- });
- params.columns.forEach(function (title, i) {
- params.columns[i] = {title: title, key: i};
- });
- } else {
- // Use options as is
- }
- }
- function initOptions(raw) {
- settings = defaultOptions();
- Object.keys(raw).forEach(function (key) {
- settings[key] = raw[key];
- });
- doc.setFontSize(settings.fontSize);
- // Backwards compatibility
- if(settings.margins.horizontal !== undefined) {
- settings.margins.left = settings.margins.horizontal;
- settings.margins.right = settings.margins.horizontal;
- } else {
- settings.margins.horizontal = settings.margins.left;
- }
- }
- function calculateColumnWidths(rows, columns) {
- var widths = {};
- // Optimal widths
- var optimalTableWidth = 0;
- columns.forEach(function (header) {
- var widest = getStringWidth(header.title || '', true);
- if(typeof header.width == "number") {
- widest = header.width;
- } else {
- rows.forEach(function (row) {
- if (!header.hasOwnProperty('key'))
- throw new Error("The key attribute is required in every header");
- var w = getStringWidth(stringify(row, header.key));
- if (w > widest) {
- widest = w;
- }
- });
- }
- widths[header.key] = widest;
- optimalTableWidth += widest;
- });
- var paddingAndMargin = settings.padding * 2 * columns.length + settings.margins.left + settings.margins.right;
- var spaceDiff = doc.internal.pageSize.width - optimalTableWidth - paddingAndMargin;
- var keys = Object.keys(widths);
- if (spaceDiff < 0) {
- // Shrink columns
- var shrinkableColumns = [];
- var shrinkableColumnWidths = 0;
- if (settings.overflowColumns === false) {
- keys.forEach(function (key) {
- if (widths[key] > MIN_COLUMN_WIDTH) {
- shrinkableColumns.push(key);
- shrinkableColumnWidths += widths[key];
- }
- });
- } else {
- shrinkableColumns = settings.overflowColumns;
- shrinkableColumns.forEach(function (col) {
- shrinkableColumnWidths += widths[col];
- });
- }
- shrinkableColumns.forEach(function (key) {
- widths[key] += spaceDiff * (widths[key] / shrinkableColumnWidths);
- });
- } else if (spaceDiff > 0 && settings.extendWidth) {
- // Fill page horizontally
- keys.forEach(function (key) {
- widths[key] += spaceDiff / keys.length;
- });
- }
- return widths;
- }
- function printHeader(headers, columnWidths) {
- if (!headers) return;
- // First calculate the height of the row
- // (to do that the maxium amount of rows first need to be found)
- var maxRows = 1;
- if (settings.overflow === 'linebreak') {
- headers.forEach(function (header) {
- if (isOverflowColumn(header)) {
- var value = header.title || '';
- var arr = doc.splitTextToSize(value, columnWidths[header.key]);
- if (arr.length > maxRows) {
- maxRows = arr.length;
- }
- }
- });
- }
- var rowHeight = settings.lineHeight + (maxRows - 1) * doc.internal.getLineHeight() + 5;
- headers.forEach(function (header) {
- var width = columnWidths[header.key] + settings.padding * 2;
- var value = header.title || '';
- if (settings.overflow === 'linebreak') {
- if (isOverflowColumn(header)) {
- value = doc.splitTextToSize(value, columnWidths[header.key]);
- }
- } else if (settings.overflow === 'ellipsize') {
- value = ellipsize(columnWidths[header.key], value);
- }
- settings.renderHeaderCell(cellPos.x, cellPos.y, width, rowHeight, header.key, value, settings);
- cellPos.x += width;
- });
- doc.setTextColor(70, 70, 70);
- doc.setFontStyle('normal');
- cellPos.y += rowHeight;
- cellPos.x = settings.margins.left;
- }
- function printRows(headers, rows, columnWidths) {
- for (var i = 0; i < rows.length; i++) {
- var row = rows[i];
- // First calculate the height of the row
- // (to do that the maxium amount of rows first need to be found)
- var maxRows = 1;
- if (settings.overflow === 'linebreak') {
- headers.forEach(function (header) {
- if (isOverflowColumn(header)) {
- var value = stringify(row, header.key);
- var arr = doc.splitTextToSize(value, columnWidths[header.key]);
- if (arr.length > maxRows) {
- maxRows = arr.length;
- }
- }
- });
- }
- var rowHeight = settings.lineHeight + (maxRows - 1) * doc.internal.getLineHeight();
- // Render the cell
- headers.forEach(function (header) {
- var value = stringify(row, header.key);
- if (settings.overflow === 'linebreak') {
- if (isOverflowColumn(header)) {
- value = doc.splitTextToSize(value, columnWidths[header.key]);
- }
- } else if (settings.overflow === 'ellipsize') {
- value = ellipsize(columnWidths[header.key], value);
- }
- var width = columnWidths[header.key] + settings.padding * 2;
- settings.renderCell(cellPos.x, cellPos.y, width, rowHeight, header.key, value, i, settings);
- cellPos.x = cellPos.x + columnWidths[header.key] + settings.padding * 2;
- });
- // Add a new page if cellpos is at the end of page
- var newPage = (cellPos.y + settings.margins.bottom + settings.lineHeight * 2) >= doc.internal.pageSize.height;
- if (newPage) {
- settings.renderFooter(doc, cellPos, pageCount, settings);
- doc.addPage();
- cellPos = {x: settings.margins.left, y: settings.margins.top};
- pageCount++;
- settings.renderHeader(doc, pageCount, settings);
- printHeader(headers, columnWidths);
- } else {
- cellPos.y += rowHeight;
- cellPos.x = settings.margins.left;
- }
- }
- }
- function isOverflowColumn(header) {
- return settings.overflowColumns === false || settings.overflowColumns.indexOf(header.key) !== -1;
- }
- /**
- * Ellipsize the text to fit in the width
- * @param width
- * @param text
- */
- function ellipsize(width, text) {
- if (width >= getStringWidth(text)) {
- return text;
- }
- while (width < getStringWidth(text + "...")) {
- if (text.length < 2) {
- break;
- }
- text = text.substring(0, text.length - 1);
- }
- text += "...";
- return text;
- }
- function stringify(row, key) {
- return row.hasOwnProperty(key) ? '' + row[key] : '';
- }
- function getStringWidth(txt, isBold) {
- if(isBold) {
- doc.setFontStyle('bold');
- }
- var strWidth = doc.getStringUnitWidth(txt) * doc.internal.getFontSize();
- if(isBold) {
- doc.setFontStyle('normal');
- }
- return strWidth;
- }
- })(jsPDF.API);
|