main.js 27 KB

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