Skip to content

Commit

Permalink
Fix horizontalPageBreak infinite loop + refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
mmghv committed Oct 1, 2023
1 parent 90a2c87 commit 8daeb84
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 99 deletions.
5 changes: 5 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
13 changes: 5 additions & 8 deletions src/tableDrawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
}
)
Expand Down
142 changes: 54 additions & 88 deletions src/tablePrinter.ts
Original file line number Diff line number Diff line change
@@ -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<number, Column>();
let repeatColumn = null
const cols: number[] = []
const repeatColumnsMap = new Map<number, boolean>()
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,
}
5 changes: 2 additions & 3 deletions src/widthCalculator.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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]
Expand Down

0 comments on commit 8daeb84

Please sign in to comment.