diff --git a/src/common.ts b/src/common.ts index 83b6b9fb..1d60131e 100644 --- a/src/common.ts +++ b/src/common.ts @@ -118,3 +118,8 @@ export function parseSpacing( return { top: value, right: value, bottom: value, left: value } } + +export function getPageAvailableWidth(doc: DocHandler, table: Table) { + const margins = parseSpacing(table.settings.margin, 0) + return doc.pageSize().width - (margins.left + margins.right) +} diff --git a/src/tableDrawer.ts b/src/tableDrawer.ts index 873cf4b6..adc981ea 100644 --- a/src/tableDrawer.ts +++ b/src/tableDrawer.ts @@ -4,7 +4,7 @@ import { Cell, Column, Pos, Row, Table } from './models' import { DocHandler, jsPDFDocument } from './documentHandler' import { assign } from './polyfills' import autoTableText from './autoTableText' -import tablePrinter, { ColumnFitInPageResult } from './tablePrinter' +import { calculateAllColumnsCanFitInPage } from './tablePrinter' export function drawTable(jsPDFDoc: jsPDFDocument, table: Table): void { const settings = table.settings @@ -40,7 +40,7 @@ export function drawTable(jsPDFDoc: jsPDFDocument, table: Table): void { table.startPageNumber = doc.pageNumber() - if (settings.horizontalPageBreak === true) { + if (settings.horizontalPageBreak) { // managed flow for split columns printTableWithHorizontalPageBreak(doc, table, startPos, cursor) } else { @@ -89,11 +89,10 @@ function printTableWithHorizontalPageBreak( cursor: { x: number; y: number } ) { // calculate width of columns and render only those which can fit into page - const allColumnsCanFitResult: ColumnFitInPageResult[] = - tablePrinter.calculateAllColumnsCanFitInPage(doc, table) + const allColumnsCanFitResult = calculateAllColumnsCanFitInPage(doc, table) allColumnsCanFitResult.map( - (colsAndIndexes: ColumnFitInPageResult, index: number) => { + (colsAndIndexes, index: number) => { doc.applyStyles(doc.userStyles) // add page to print next columns in new page if (index > 0) { @@ -102,10 +101,8 @@ function printTableWithHorizontalPageBreak( // print head for selected columns printHead(doc, table, cursor, colsAndIndexes.columns) } - // print body for selected columns + // print body & footer for selected columns printBody(doc, table, startPos, cursor, colsAndIndexes.columns) - - // print foot for selected columns printFoot(doc, table, cursor, colsAndIndexes.columns) } ) diff --git a/src/tablePrinter.ts b/src/tablePrinter.ts index 2ab18f3b..9fa844eb 100644 --- a/src/tablePrinter.ts +++ b/src/tablePrinter.ts @@ -1,121 +1,87 @@ -import { parseSpacing } from './common' +import { getPageAvailableWidth } from './common' import { DocHandler } from './documentHandler' import { Column, Table } from './models' -export interface ColumnFitInPageResult { +interface ColumnFitInPageResult { colIndexes: number[] - columns: Column[], + columns: Column[] lastIndex: number } -const getPageAvailableWidth = (doc: DocHandler, table: Table) => { - const margins = parseSpacing(table.settings.margin, 0) - const availablePageWidth = - doc.pageSize().width - (margins.left + margins.right) - return availablePageWidth -} - // get columns can be fit into page -const getColumnsCanFitInPage = ( +function getColumnsCanFitInPage( doc: DocHandler, table: Table, - config: any = {} -): ColumnFitInPageResult => { + config: any = {}, +): ColumnFitInPageResult { // Get page width - const availablePageWidth = getPageAvailableWidth(doc, table) - let remainingWidth = availablePageWidth + let remainingWidth = getPageAvailableWidth(doc, table) // Get column data key to repeat - const repeatColumnsMap = new Map(); - let repeatColumn = null - const cols: number[] = [] + const repeatColumnsMap = new Map() + const colIndexes: number[] = [] const columns: Column[] = [] - const len = table.columns.length - let i = config?.start ?? 0; + let horizontalPageBreakRepeat: (number | string)[] = [] + table.settings.horizontalPageBreakRepeat - const horizontalPageBreakRepeat = table.settings.horizontalPageBreakRepeat - // Code to repeat the given column in split pages - if (horizontalPageBreakRepeat !== null && horizontalPageBreakRepeat !== undefined && Array.isArray(horizontalPageBreakRepeat)) { - for (const field of horizontalPageBreakRepeat) { - const col = table.columns.find( - (item) => - item.dataKey === field || - item.index === field - ) - if (col) { - repeatColumnsMap.set(col.index, col); - cols.push(col.index) - columns.push(table.columns[col.index]) - remainingWidth = remainingWidth - col.wrappedWidth - } - } + if (Array.isArray(table.settings.horizontalPageBreakRepeat)) { + horizontalPageBreakRepeat = table.settings.horizontalPageBreakRepeat // It can be a single value of type string or number (even number: 0) - } else if (horizontalPageBreakRepeat !== null && horizontalPageBreakRepeat !== undefined) { - repeatColumn = table.columns.find( - (item) => - item.dataKey === horizontalPageBreakRepeat || - item.index === horizontalPageBreakRepeat - ) - if (repeatColumn) { - cols.push(repeatColumn.index) - columns.push(table.columns[repeatColumn.index]) - remainingWidth = remainingWidth - repeatColumn.wrappedWidth - } + } else if ( + typeof table.settings.horizontalPageBreakRepeat === 'string' || + typeof table.settings.horizontalPageBreakRepeat === 'number' + ) { + horizontalPageBreakRepeat = [table.settings.horizontalPageBreakRepeat] } - while (i < len) { - // Prevent columnDataKeyToRepeat from being pushed twice on a page - if ((Array.isArray(horizontalPageBreakRepeat) && repeatColumnsMap.get(i)) - || repeatColumn?.index === i) { - i++; - continue; - } + // Code to repeat the given column in split pages + horizontalPageBreakRepeat.forEach((field) => { + const col = table.columns.find( + (item) => item.dataKey === field || item.index === field, + ) - const colWidth = table.columns[i].wrappedWidth; - if (remainingWidth < colWidth) { - if (i === 0 || i === config.start) { - cols.push(i); - columns.push(table.columns[i]); - } - break; + if (col && !repeatColumnsMap.has(col.index)) { + repeatColumnsMap.set(col.index, true) + colIndexes.push(col.index) + columns.push(table.columns[col.index]) + remainingWidth -= col.wrappedWidth } + }) - cols.push(i); - columns.push(table.columns[i]); - remainingWidth -= colWidth; - i++; + let first = true + let i = config?.start ?? 0 // make sure couter is initiated outside the loop + for (i = i; i < table.columns.length; i++) { + // Prevent duplicates + if (repeatColumnsMap.has(i)) continue + + const colWidth = table.columns[i].wrappedWidth + + // Take at least one column even if it doesn't fit + if (first || remainingWidth >= colWidth) { + first = false + colIndexes.push(i) + columns.push(table.columns[i]) + remainingWidth -= colWidth + } else { + break + } } - return { colIndexes: cols, columns, lastIndex: i } + return { colIndexes, columns, lastIndex: i - 1 } } -const calculateAllColumnsCanFitInPage = ( +export function calculateAllColumnsCanFitInPage( doc: DocHandler, - table: Table -): ColumnFitInPageResult[] => { - // const margins = table.settings.margin; - // const availablePageWidth = doc.pageSize().width - (margins.left + margins.right); - + table: Table, +): ColumnFitInPageResult[] { const allResults: ColumnFitInPageResult[] = [] - let index = 0 - const len = table.columns.length - while (index < len) { - const result = getColumnsCanFitInPage(doc, table, { - start: index === 0 ? 0 : index, - }) - if (result && result.columns && result.columns.length) { - index = result.lastIndex + for (let i = 0; i < table.columns.length; i++) { + const result = getColumnsCanFitInPage(doc, table, { start: i }) + if (result.columns.length) { allResults.push(result) - } else { - index++ + i = result.lastIndex } } return allResults } - -export default { - getColumnsCanFitInPage, - calculateAllColumnsCanFitInPage, - getPageAvailableWidth, -} diff --git a/src/widthCalculator.ts b/src/widthCalculator.ts index 092ab77b..1032a127 100644 --- a/src/widthCalculator.ts +++ b/src/widthCalculator.ts @@ -1,8 +1,7 @@ -import { getStringWidth } from './common' +import { getStringWidth, getPageAvailableWidth } from './common' import { Table, Cell, Column, Row } from './models' import { DocHandler } from './documentHandler' import { Styles } from './config' -import TablePrinter from './tablePrinter' /** * Calculate the column widths @@ -68,7 +67,7 @@ export function calculateWidths(doc: DocHandler, table: Table) { function calculate(doc: DocHandler, table: Table) { const sf = doc.scaleFactor() const horizontalPageBreak = table.settings.horizontalPageBreak - const availablePageWidth = TablePrinter.getPageAvailableWidth(doc, table) + const availablePageWidth = getPageAvailableWidth(doc, table) table.allRows().forEach((row) => { for (const column of table.columns) { const cell = row.cells[column.index]