jspdf.plugin.autotable.src.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. /**
  2. * jsPDF AutoTable plugin v2.0.17
  3. * Copyright (c) 2014 Simon Bengtsson, https://github.com/simonbengtsson/jsPDF-AutoTable
  4. *
  5. * Licensed under the MIT License.
  6. * http://opensource.org/licenses/mit-license
  7. *
  8. * @preserve
  9. */
  10. 'use strict';
  11. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  12. (function (API) {
  13. 'use strict';
  14. // Ratio between font size and font height. The number comes from jspdf's source code
  15. var FONT_ROW_RATIO = 1.15;
  16. var doc, // The current jspdf instance
  17. cursor, // An object keeping track of the x and y position of the next table cell to draw
  18. settings, // Default options merged with user options
  19. pageCount, // The page count the current table spans
  20. table; // The current Table instance
  21. // Base style for all themes
  22. var defaultStyles = {
  23. cellPadding: 5,
  24. fontSize: 10,
  25. font: "helvetica", // helvetica, times, courier
  26. lineColor: 200,
  27. lineWidth: 0.1,
  28. fontStyle: 'normal', // normal, bold, italic, bolditalic
  29. overflow: 'ellipsize', // visible, hidden, ellipsize or linebreak
  30. fillColor: 255,
  31. textColor: 20,
  32. halign: 'left', // left, center, right
  33. valign: 'top', // top, middle, bottom
  34. fillStyle: 'F', // 'S', 'F' or 'DF' (stroke, fill or fill then stroke)
  35. rowHeight: 20,
  36. columnWidth: 'auto'
  37. };
  38. // Styles for the themes
  39. var themes = {
  40. 'striped': {
  41. table: {
  42. fillColor: 255,
  43. textColor: 80,
  44. fontStyle: 'normal',
  45. fillStyle: 'F'
  46. },
  47. header: {
  48. textColor: 255,
  49. fillColor: [41, 128, 185],
  50. rowHeight: 23,
  51. fontStyle: 'bold'
  52. },
  53. body: {},
  54. alternateRow: { fillColor: 245 }
  55. },
  56. 'grid': {
  57. table: {
  58. fillColor: 255,
  59. textColor: 80,
  60. fontStyle: 'normal',
  61. lineWidth: 0.1,
  62. fillStyle: 'DF'
  63. },
  64. header: {
  65. textColor: 255,
  66. fillColor: [26, 188, 156],
  67. rowHeight: 23,
  68. fillStyle: 'F',
  69. fontStyle: 'bold'
  70. },
  71. body: {},
  72. alternateRow: {}
  73. },
  74. 'plain': { header: { fontStyle: 'bold' } }
  75. };
  76. // See README.md for documentation of the options
  77. // See examples.js for usage examples
  78. var defaultOptions = function defaultOptions() {
  79. return {
  80. // Styling
  81. theme: 'striped', // 'striped', 'grid' or 'plain'
  82. styles: {},
  83. headerStyles: {},
  84. bodyStyles: {},
  85. alternateRowStyles: {},
  86. columnStyles: {},
  87. // Properties
  88. startY: false, // false indicates the margin.top value
  89. margin: 40,
  90. pageBreak: 'auto', // 'auto', 'avoid', 'always'
  91. tableWidth: 'auto', // number, 'auto', 'wrap'
  92. // Hooks
  93. createdHeaderCell: function createdHeaderCell(cell, data) {},
  94. createdCell: function createdCell(cell, data) {},
  95. drawHeaderRow: function drawHeaderRow(row, data) {},
  96. drawRow: function drawRow(row, data) {},
  97. drawHeaderCell: function drawHeaderCell(cell, data) {},
  98. drawCell: function drawCell(cell, data) {},
  99. beforePageContent: function beforePageContent(data) {},
  100. afterPageContent: function afterPageContent(data) {}
  101. };
  102. };
  103. /**
  104. * Create a table from a set of rows and columns.
  105. *
  106. * @param {Object[]|String[]} headers Either as an array of objects or array of strings
  107. * @param {Object[][]|String[][]} data Either as an array of objects or array of strings
  108. * @param {Object} [options={}] Options that will override the default ones
  109. */
  110. API.autoTable = function (headers, data, options) {
  111. validateInput(headers, data, options);
  112. doc = this;
  113. settings = initOptions(options || {});
  114. pageCount = 1;
  115. // Need a cursor y as it needs to be reset after each page (row.y can't do that)
  116. cursor = { y: settings.startY === false ? settings.margin.top : settings.startY };
  117. var userStyles = {
  118. textColor: 30, // Setting text color to dark gray as it can't be obtained from jsPDF
  119. fontSize: doc.internal.getFontSize(),
  120. fontStyle: doc.internal.getFont().fontStyle
  121. };
  122. // Create the table model with its columns, rows and cells
  123. createModels(headers, data);
  124. calculateWidths();
  125. // Page break if there is room for only the first data row
  126. var firstRowHeight = table.rows[0] && settings.pageBreak === 'auto' ? table.rows[0].height : 0;
  127. var minTableBottomPos = settings.startY + settings.margin.bottom + table.headerRow.height + firstRowHeight;
  128. if (settings.pageBreak === 'avoid') {
  129. minTableBottomPos += table.height;
  130. }
  131. if (settings.pageBreak === 'always' && settings.startY !== false || settings.startY !== false && minTableBottomPos > doc.internal.pageSize.height) {
  132. doc.addPage();
  133. cursor.y = settings.margin.top;
  134. }
  135. applyStyles(userStyles);
  136. settings.beforePageContent(hooksData());
  137. if (settings.drawHeaderRow(table.headerRow, hooksData({ row: table.headerRow })) !== false) {
  138. printRow(table.headerRow, settings.drawHeaderCell);
  139. }
  140. applyStyles(userStyles);
  141. printRows();
  142. settings.afterPageContent(hooksData());
  143. applyStyles(userStyles);
  144. return this;
  145. };
  146. /**
  147. * Returns the Y position of the last drawn cell
  148. * @returns int
  149. */
  150. API.autoTableEndPosY = function () {
  151. if (typeof cursor === 'undefined' || typeof cursor.y === 'undefined') {
  152. return 0;
  153. }
  154. return cursor.y;
  155. };
  156. /**
  157. * Parses an html table
  158. *
  159. * @param tableElem Html table element
  160. * @param includeHiddenRows Defaults to false
  161. * @returns Object Object with two properties, columns and rows
  162. */
  163. API.autoTableHtmlToJson = function (tableElem, includeHiddenRows) {
  164. includeHiddenRows = includeHiddenRows || false;
  165. var header = tableElem.rows[0];
  166. var result = { columns: [], rows: [] };
  167. for (var k = 0; k < header.cells.length; k++) {
  168. var cell = header.cells[k];
  169. result.columns.push(typeof cell !== 'undefined' ? cell.textContent : '');
  170. }
  171. for (var i = 1; i < tableElem.rows.length; i++) {
  172. var tableRow = tableElem.rows[i];
  173. var style = window.getComputedStyle(tableRow);
  174. if (includeHiddenRows || style.display !== 'none') {
  175. var rowData = [];
  176. for (var j = 0; j < header.cells.length; j++) {
  177. rowData.push(typeof tableRow.cells[j] !== 'undefined' ? tableRow.cells[j].textContent : '');
  178. }
  179. result.rows.push(rowData);
  180. }
  181. }
  182. result.data = result.rows; // Deprecated
  183. return result;
  184. };
  185. /**
  186. * Improved text function with halign and valign support
  187. * Inspiration from: http://stackoverflow.com/questions/28327510/align-text-right-using-jspdf/28433113#28433113
  188. */
  189. API.autoTableText = function (text, x, y, styles) {
  190. if (typeof x !== 'number' || typeof y !== 'number') {
  191. console.error('The x and y parameters are required. Missing for the text: ', text);
  192. }
  193. var fontSize = doc.internal.getFontSize() / doc.internal.scaleFactor;
  194. // As defined in jsPDF source code
  195. var lineHeightProportion = FONT_ROW_RATIO;
  196. var splitRegex = /\r\n|\r|\n/g;
  197. var splittedText = null;
  198. var lineCount = 1;
  199. if (styles.valign === 'middle' || styles.valign === 'bottom' || styles.halign === 'center' || styles.halign === 'right') {
  200. splittedText = typeof text === 'string' ? text.split(splitRegex) : text;
  201. lineCount = splittedText.length || 1;
  202. }
  203. // Align the top
  204. y += fontSize * (2 - lineHeightProportion);
  205. if (styles.valign === 'middle') y -= lineCount / 2 * fontSize;else if (styles.valign === 'bottom') y -= lineCount * fontSize;
  206. if (styles.halign === 'center' || styles.halign === 'right') {
  207. var alignSize = fontSize;
  208. if (styles.halign === 'center') alignSize *= 0.5;
  209. if (lineCount >= 1) {
  210. for (var iLine = 0; iLine < splittedText.length; iLine++) {
  211. doc.text(splittedText[iLine], x - doc.getStringUnitWidth(splittedText[iLine]) * alignSize, y);
  212. y += fontSize;
  213. }
  214. return doc;
  215. }
  216. x -= doc.getStringUnitWidth(text) * alignSize;
  217. }
  218. doc.text(text, x, y);
  219. return doc;
  220. };
  221. function validateInput(headers, data, options) {
  222. if (!headers || typeof headers !== 'object') {
  223. console.error("The headers should be an object or array, is: " + typeof headers);
  224. }
  225. if (!data || typeof data !== 'object') {
  226. console.error("The data should be an object or array, is: " + typeof data);
  227. }
  228. if (!!options && typeof options !== 'object') {
  229. console.error("The data should be an object or array, is: " + typeof data);
  230. }
  231. if (!Array.prototype.forEach) {
  232. console.error("The current browser does not support Array.prototype.forEach which is required for " + "jsPDF-AutoTable. You can try polyfilling it by including this script " + "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Polyfill");
  233. }
  234. }
  235. function initOptions(userOptions) {
  236. var settings = extend(defaultOptions(), userOptions);
  237. // Options
  238. if (typeof settings.extendWidth !== 'undefined') {
  239. settings.tableWidth = settings.extendWidth ? 'auto' : 'wrap';
  240. console.error("Use of deprecated option: extendWidth, use tableWidth instead.");
  241. }
  242. if (typeof settings.margins !== 'undefined') {
  243. if (typeof settings.margin === 'undefined') settings.margin = settings.margins;
  244. console.error("Use of deprecated option: margins, use margin instead.");
  245. }
  246. [['padding', 'cellPadding'], ['lineHeight', 'rowHeight'], 'fontSize', 'overflow'].forEach(function (o) {
  247. var deprecatedOption = typeof o === 'string' ? o : o[0];
  248. var style = typeof o === 'string' ? o : o[1];
  249. if (typeof settings[deprecatedOption] !== 'undefined') {
  250. if (typeof settings.styles[style] === 'undefined') {
  251. settings.styles[style] = settings[deprecatedOption];
  252. }
  253. console.error("Use of deprecated option: " + deprecatedOption + ", use the style " + style + " instead.");
  254. }
  255. });
  256. // Unifying
  257. var marginSetting = settings.margin;
  258. settings.margin = {};
  259. if (typeof marginSetting.horizontal === 'number') {
  260. marginSetting.right = marginSetting.horizontal;
  261. marginSetting.left = marginSetting.horizontal;
  262. }
  263. if (typeof marginSetting.vertical === 'number') {
  264. marginSetting.top = marginSetting.vertical;
  265. marginSetting.bottom = marginSetting.vertical;
  266. }
  267. ['top', 'right', 'bottom', 'left'].forEach(function (side, i) {
  268. if (typeof marginSetting === 'number') {
  269. settings.margin[side] = marginSetting;
  270. } else {
  271. var key = Array.isArray(marginSetting) ? i : side;
  272. settings.margin[side] = typeof marginSetting[key] === 'number' ? marginSetting[key] : 40;
  273. }
  274. });
  275. return settings;
  276. }
  277. /**
  278. * Create models from the user input
  279. *
  280. * @param inputHeaders
  281. * @param inputData
  282. */
  283. function createModels(inputHeaders, inputData) {
  284. table = new Table();
  285. table.x = settings.margin.left;
  286. var splitRegex = /\r\n|\r|\n/g;
  287. // Header row and columns
  288. var headerRow = new Row(inputHeaders);
  289. headerRow.index = -1;
  290. var themeStyles = extend(defaultStyles, themes[settings.theme].table, themes[settings.theme].header);
  291. headerRow.styles = extend(themeStyles, settings.styles, settings.headerStyles);
  292. // Columns and header row
  293. inputHeaders.forEach(function (rawColumn, dataKey) {
  294. if (typeof rawColumn === 'object') {
  295. dataKey = typeof rawColumn.dataKey !== 'undefined' ? rawColumn.dataKey : rawColumn.key;
  296. }
  297. if (typeof rawColumn.width !== 'undefined') {
  298. console.error("Use of deprecated option: column.width, use column.styles.columnWidth instead.");
  299. }
  300. var col = new Column(dataKey);
  301. col.styles = settings.columnStyles[col.dataKey] || {};
  302. table.columns.push(col);
  303. var cell = new Cell();
  304. cell.raw = typeof rawColumn === 'object' ? rawColumn.title : rawColumn;
  305. cell.styles = extend(headerRow.styles);
  306. cell.text = '' + cell.raw;
  307. cell.contentWidth = cell.styles.cellPadding * 2 + getStringWidth(cell.text, cell.styles);
  308. cell.text = cell.text.split(splitRegex);
  309. headerRow.cells[dataKey] = cell;
  310. settings.createdHeaderCell(cell, { column: col, row: headerRow, settings: settings });
  311. });
  312. table.headerRow = headerRow;
  313. // Rows och cells
  314. inputData.forEach(function (rawRow, i) {
  315. var row = new Row(rawRow);
  316. var isAlternate = i % 2 === 0;
  317. var themeStyles = extend(defaultStyles, themes[settings.theme].table, isAlternate ? themes[settings.theme].alternateRow : {});
  318. var userStyles = extend(settings.styles, settings.bodyStyles, isAlternate ? settings.alternateRowStyles : {});
  319. row.styles = extend(themeStyles, userStyles);
  320. row.index = i;
  321. table.columns.forEach(function (column) {
  322. var cell = new Cell();
  323. cell.raw = rawRow[column.dataKey];
  324. cell.styles = extend(row.styles, column.styles);
  325. cell.text = typeof cell.raw !== 'undefined' ? '' + cell.raw : ''; // Stringify 0 and false, but not undefined
  326. row.cells[column.dataKey] = cell;
  327. settings.createdCell(cell, hooksData({ column: column, row: row }));
  328. cell.contentWidth = cell.styles.cellPadding * 2 + getStringWidth(cell.text, cell.styles);
  329. cell.text = cell.text.split(splitRegex);
  330. });
  331. table.rows.push(row);
  332. });
  333. }
  334. /**
  335. * Calculate the column widths
  336. */
  337. function calculateWidths() {
  338. // Column and table content width
  339. var tableContentWidth = 0;
  340. table.columns.forEach(function (column) {
  341. column.contentWidth = table.headerRow.cells[column.dataKey].contentWidth;
  342. table.rows.forEach(function (row) {
  343. var cellWidth = row.cells[column.dataKey].contentWidth;
  344. if (cellWidth > column.contentWidth) {
  345. column.contentWidth = cellWidth;
  346. }
  347. });
  348. column.width = column.contentWidth;
  349. tableContentWidth += column.contentWidth;
  350. });
  351. table.contentWidth = tableContentWidth;
  352. var maxTableWidth = doc.internal.pageSize.width - settings.margin.left - settings.margin.right;
  353. var preferredTableWidth = maxTableWidth; // settings.tableWidth === 'auto'
  354. if (typeof settings.tableWidth === 'number') {
  355. preferredTableWidth = settings.tableWidth;
  356. } else if (settings.tableWidth === 'wrap') {
  357. preferredTableWidth = table.contentWidth;
  358. }
  359. table.width = preferredTableWidth < maxTableWidth ? preferredTableWidth : maxTableWidth;
  360. // To avoid subjecting columns with little content with the chosen overflow method,
  361. // never shrink a column more than the table divided by column count (its "fair part")
  362. var dynamicColumns = [];
  363. var dynamicColumnsContentWidth = 0;
  364. var fairWidth = table.width / table.columns.length;
  365. var staticWidth = 0;
  366. table.columns.forEach(function (column) {
  367. var colStyles = extend(defaultStyles, themes[settings.theme].table, settings.styles, column.styles);
  368. if (colStyles.columnWidth === 'wrap') {
  369. column.width = column.contentWidth;
  370. } else if (typeof colStyles.columnWidth === 'number') {
  371. column.width = colStyles.columnWidth;
  372. } else if (colStyles.columnWidth === 'auto' || true) {
  373. if (column.contentWidth <= fairWidth && table.contentWidth > table.width) {
  374. column.width = column.contentWidth;
  375. } else {
  376. dynamicColumns.push(column);
  377. dynamicColumnsContentWidth += column.contentWidth;
  378. column.width = 0;
  379. }
  380. }
  381. staticWidth += column.width;
  382. });
  383. // Distributes extra width or trims columns down to fit
  384. distributeWidth(dynamicColumns, staticWidth, dynamicColumnsContentWidth, fairWidth);
  385. // Row height, table height and text overflow
  386. table.height = 0;
  387. var all = table.rows.concat(table.headerRow);
  388. all.forEach(function (row, i) {
  389. var lineBreakCount = 0;
  390. var cursorX = table.x;
  391. table.columns.forEach(function (col) {
  392. var cell = row.cells[col.dataKey];
  393. col.x = cursorX;
  394. applyStyles(cell.styles);
  395. var textSpace = col.width - cell.styles.cellPadding * 2;
  396. if (cell.styles.overflow === 'linebreak') {
  397. // Add one pt to textSpace to fix rounding error
  398. cell.text = doc.splitTextToSize(cell.text, textSpace + 1, { fontSize: cell.styles.fontSize });
  399. } else if (cell.styles.overflow === 'ellipsize') {
  400. cell.text = ellipsize(cell.text, textSpace, cell.styles);
  401. } else if (cell.styles.overflow === 'visible') {
  402. // Do nothing
  403. } else if (cell.styles.overflow === 'hidden') {
  404. cell.text = ellipsize(cell.text, textSpace, cell.styles, '');
  405. } else if (typeof cell.styles.overflow === 'function') {
  406. cell.text = cell.styles.overflow(cell.text, textSpace);
  407. } else {
  408. console.error("Unrecognized overflow type: " + cell.styles.overflow);
  409. }
  410. var count = Array.isArray(cell.text) ? cell.text.length - 1 : 0;
  411. if (count > lineBreakCount) {
  412. lineBreakCount = count;
  413. }
  414. cursorX += col.width;
  415. });
  416. row.heightStyle = row.styles.rowHeight;
  417. // TODO Pick the highest row based on font size as well
  418. row.height = row.heightStyle + lineBreakCount * row.styles.fontSize * FONT_ROW_RATIO;
  419. table.height += row.height;
  420. });
  421. }
  422. function distributeWidth(dynamicColumns, staticWidth, dynamicColumnsContentWidth, fairWidth) {
  423. var extraWidth = table.width - staticWidth - dynamicColumnsContentWidth;
  424. for (var i = 0; i < dynamicColumns.length; i++) {
  425. var col = dynamicColumns[i];
  426. var ratio = col.contentWidth / dynamicColumnsContentWidth;
  427. // A column turned out to be none dynamic, start over recursively
  428. var isNoneDynamic = col.contentWidth + extraWidth * ratio < fairWidth;
  429. if (extraWidth < 0 && isNoneDynamic) {
  430. dynamicColumns.splice(i, 1);
  431. dynamicColumnsContentWidth -= col.contentWidth;
  432. col.width = fairWidth;
  433. staticWidth += col.width;
  434. distributeWidth(dynamicColumns, staticWidth, dynamicColumnsContentWidth, fairWidth);
  435. break;
  436. } else {
  437. col.width = col.contentWidth + extraWidth * ratio;
  438. }
  439. }
  440. }
  441. function printRows() {
  442. table.rows.forEach(function (row, i) {
  443. if (isNewPage(row.height)) {
  444. var samePageThreshold = 3;
  445. // TODO Fix cell height > pageHeight
  446. /*if (row.height > row.heightStyle * samePageThreshold) {
  447. var remainingPageSpace = doc.internal.pageSize.height - cursor.y - settings.margin.bottom;
  448. var lineCount = Math.floor(remainingPageSpace / (row.styles.fontSize * FONT_ROW_RATIO));
  449. table.columns.forEach(function(col) {
  450. var arr = row.cells[col.dataKey].text;
  451. if (arr.length > lineCount) {
  452. arr.splice(lineCount - 1, arr.length, "...");
  453. }
  454. });
  455. row.height = remainingPageSpace;
  456. if (settings.drawRow(row, hooksData({row: row})) !== false) {
  457. printRow(row, settings.drawCell);
  458. }
  459. row = new Row(rawRow);
  460. }*/
  461. addPage();
  462. }
  463. row.y = cursor.y;
  464. if (settings.drawRow(row, hooksData({ row: row })) !== false) {
  465. printRow(row, settings.drawCell);
  466. }
  467. });
  468. }
  469. function addPage() {
  470. settings.afterPageContent(hooksData());
  471. doc.addPage();
  472. pageCount++;
  473. cursor = { x: settings.margin.left, y: settings.margin.top };
  474. settings.beforePageContent(hooksData());
  475. if (settings.drawHeaderRow(table.headerRow, hooksData({ row: table.headerRow })) !== false) {
  476. printRow(table.headerRow, settings.drawHeaderCell);
  477. }
  478. }
  479. /**
  480. * Add a new page if cursor is at the end of page
  481. * @param rowHeight
  482. * @returns {boolean}
  483. */
  484. function isNewPage(rowHeight) {
  485. var afterRowPos = cursor.y + rowHeight + settings.margin.bottom;
  486. return afterRowPos >= doc.internal.pageSize.height;
  487. }
  488. function printRow(row, hookHandler) {
  489. for (var i = 0; i < table.columns.length; i++) {
  490. var column = table.columns[i];
  491. var cell = row.cells[column.dataKey];
  492. if (!cell) {
  493. continue;
  494. }
  495. applyStyles(cell.styles);
  496. cell.x = column.x;
  497. cell.y = cursor.y;
  498. cell.height = row.height;
  499. cell.width = column.width;
  500. if (cell.styles.valign === 'top') {
  501. cell.textPos.y = cursor.y + cell.styles.cellPadding;
  502. } else if (cell.styles.valign === 'bottom') {
  503. cell.textPos.y = cursor.y + row.height - cell.styles.cellPadding;
  504. } else {
  505. cell.textPos.y = cursor.y + row.height / 2;
  506. }
  507. if (cell.styles.halign === 'right') {
  508. cell.textPos.x = cell.x + cell.width - cell.styles.cellPadding;
  509. } else if (cell.styles.halign === 'center') {
  510. cell.textPos.x = cell.x + cell.width / 2;
  511. } else {
  512. cell.textPos.x = cell.x + cell.styles.cellPadding;
  513. }
  514. var data = hooksData({ column: column, row: row });
  515. if (hookHandler(cell, data) !== false) {
  516. doc.rect(cell.x, cell.y, cell.width, cell.height, cell.styles.fillStyle);
  517. doc.autoTableText(cell.text, cell.textPos.x, cell.textPos.y, {
  518. halign: cell.styles.halign,
  519. valign: cell.styles.valign
  520. });
  521. }
  522. }
  523. cursor.y += row.height;
  524. }
  525. function applyStyles(styles) {
  526. var arr = [{ func: doc.setFillColor, value: styles.fillColor }, { func: doc.setTextColor, value: styles.textColor }, { func: doc.setFontStyle, value: styles.fontStyle }, { func: doc.setDrawColor, value: styles.lineColor }, { func: doc.setLineWidth, value: styles.lineWidth }, { func: doc.setFont, value: styles.font }, { func: doc.setFontSize, value: styles.fontSize }];
  527. arr.forEach(function (obj) {
  528. if (typeof obj.value !== 'undefined') {
  529. if (obj.value.constructor === Array) {
  530. obj.func.apply(this, obj.value);
  531. } else {
  532. obj.func(obj.value);
  533. }
  534. }
  535. });
  536. }
  537. function hooksData(additionalData) {
  538. additionalData = additionalData || {};
  539. var data = {
  540. pageCount: pageCount,
  541. settings: settings,
  542. table: table,
  543. cursor: cursor
  544. };
  545. for (var prop in additionalData) {
  546. if (additionalData.hasOwnProperty(prop)) {
  547. data[prop] = additionalData[prop];
  548. }
  549. }
  550. return data;
  551. }
  552. /**
  553. * Ellipsize the text to fit in the width
  554. */
  555. function ellipsize(text, width, styles, ellipsizeStr) {
  556. ellipsizeStr = typeof ellipsizeStr !== 'undefined' ? ellipsizeStr : '...';
  557. if (Array.isArray(text)) {
  558. text.forEach(function (str, i) {
  559. text[i] = ellipsize(str, width, styles, ellipsizeStr);
  560. });
  561. return text;
  562. }
  563. if (width >= getStringWidth(text, styles)) {
  564. return text;
  565. }
  566. while (width < getStringWidth(text + ellipsizeStr, styles)) {
  567. if (text.length < 2) {
  568. break;
  569. }
  570. text = text.substring(0, text.length - 1);
  571. }
  572. return text.trim() + ellipsizeStr;
  573. }
  574. function getStringWidth(text, styles) {
  575. applyStyles(styles);
  576. var w = doc.getStringUnitWidth(text);
  577. return w * styles.fontSize;
  578. }
  579. function extend(defaults) {
  580. var extended = {};
  581. var prop;
  582. for (prop in defaults) {
  583. if (defaults.hasOwnProperty(prop)) {
  584. extended[prop] = defaults[prop];
  585. }
  586. }
  587. for (var i = 1; i < arguments.length; i++) {
  588. var options = arguments[i];
  589. for (prop in options) {
  590. if (options.hasOwnProperty(prop)) {
  591. if (typeof options[prop] === 'object' && !Array.isArray(options[prop])) {
  592. //extended[prop] = extend(extended[prop] || {}, options[prop])
  593. extended[prop] = options[prop];
  594. } else {
  595. extended[prop] = options[prop];
  596. }
  597. }
  598. }
  599. }
  600. return extended;
  601. }
  602. })(jsPDF.API);
  603. var Table = function Table() {
  604. _classCallCheck(this, Table);
  605. this.height = 0;
  606. this.width = 0;
  607. this.x = 0;
  608. this.y = 0;
  609. this.contentWidth = 0;
  610. this.rows = [];
  611. this.columns = [];
  612. this.headerRow = null;
  613. this.settings = {};
  614. };
  615. var Row = function Row(raw) {
  616. _classCallCheck(this, Row);
  617. this.raw = raw || {};
  618. this.index = 0;
  619. this.styles = {};
  620. this.cells = {};
  621. this.height = 0;
  622. this.y = 0;
  623. };
  624. var Cell = function Cell(raw) {
  625. _classCallCheck(this, Cell);
  626. this.raw = raw;
  627. this.styles = {};
  628. this.text = '';
  629. this.contentWidth = 0;
  630. this.textPos = {};
  631. this.height = 0;
  632. this.width = 0;
  633. this.x = 0;
  634. this.y = 0;
  635. };
  636. var Column = function Column(dataKey) {
  637. _classCallCheck(this, Column);
  638. this.dataKey = dataKey;
  639. this.options = {};
  640. this.styles = {};
  641. this.contentWidth = 0;
  642. this.width = 0;
  643. this.x = 0;
  644. };