Generate PDF tables with JavaScript (jsPDF plugin)
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Core data model classes representing table structure and cell content with full type safety and comprehensive functionality for table manipulation and rendering.
Primary table model representing the complete table structure with settings, styling, content, and rendering state.
class Table {
/** Optional table identifier */
readonly id?: string | number;
/** Table configuration settings */
readonly settings: Settings;
/** Table styling configuration */
readonly styles: StylesProps;
/** Hook functions for customization */
readonly hooks: HookProps;
/** Column definitions */
readonly columns: Column[];
/** Header rows */
readonly head: Row[];
/** Body rows */
readonly body: Row[];
/** Footer rows */
readonly foot: Row[];
/** Current page number during rendering */
pageNumber: number;
/** Final Y position after table is drawn */
finalY?: number;
/** Starting page number */
startPageNumber?: number;
constructor(input: TableInput, content: ContentSettings);
/** Calculate total height of header rows */
getHeadHeight(columns: Column[]): number;
/** Calculate total height of footer rows */
getFootHeight(columns: Column[]): number;
/** Get all rows (head + body + foot) */
allRows(): Row[];
/** Execute cell-level hooks */
callCellHooks(
doc: DocHandler,
handlers: CellHook[],
cell: Cell,
row: Row,
column: Column,
cursor: { x: number; y: number } | null
): boolean;
/** Execute end-of-page hooks */
callEndPageHooks(doc: DocHandler, cursor: { x: number; y: number }): void;
/** Execute start-of-page hooks */
callWillDrawPageHooks(doc: DocHandler, cursor: { x: number; y: number }): void;
/** Calculate table width based on settings */
getWidth(pageWidth: number): number;
}Usage Example:
import { __createTable } from "jspdf-autotable";
const table = __createTable(doc, {
head: [['Name', 'Score']],
body: [['Alice', 95], ['Bob', 87]],
});
// Access table properties
console.log(table.columns.length); // 2
console.log(table.head.length); // 1
console.log(table.body.length); // 2
// Calculate dimensions
const headHeight = table.getHeadHeight(table.columns);
const totalWidth = table.getWidth(doc.internal.pageSize.width);Represents a single table row with cells and layout information.
class Row {
/** Original row input data */
readonly raw: HTMLTableRowElement | RowInput;
/** Reference to HTML element if created from HTML */
readonly element?: HTMLTableRowElement;
/** Row index within its section */
readonly index: number;
/** Section type: 'head', 'body', or 'foot' */
readonly section: Section;
/** Cell objects keyed by column index */
readonly cells: { [key: string]: Cell };
/** Whether this row spans multiple pages */
spansMultiplePages: boolean;
/** Calculated row height */
height: number;
constructor(
raw: RowInput | HTMLTableRowElement,
index: number,
section: Section,
cells: { [key: string]: Cell },
spansMultiplePages?: boolean
);
/** Get maximum cell height in this row */
getMaxCellHeight(columns: Column[]): number;
/** Check if row has cells with rowSpan > 1 */
hasRowSpan(columns: Column[]): boolean;
/** Check if entire row can fit in given height */
canEntireRowFit(height: number, columns: Column[]): boolean;
/** Calculate minimum height needed for this row */
getMinimumRowHeight(columns: Column[], doc: DocHandler): number;
}Usage Example:
// Row properties are typically accessed during hook execution
const didDrawCell = (data: CellHookData) => {
const row = data.row;
console.log(`Row ${row.index} in ${row.section} section`);
console.log(`Row height: ${row.height}`);
console.log(`Row spans pages: ${row.spansMultiplePages}`);
// Access specific cells
const firstCell = row.cells[0];
if (firstCell) {
console.log(`First cell content: ${firstCell.text.join(' ')}`);
}
};Represents individual table cells with content, styling, and positioning information.
class Cell {
/** Original cell input data */
raw: HTMLTableCellElement | CellInput;
/** Cell styling configuration */
styles: Styles;
/** Text content as array of lines */
text: string[];
/** Section type: 'head', 'body', or 'foot' */
section: Section;
/** Number of columns this cell spans */
colSpan: number;
/** Number of rows this cell spans */
rowSpan: number;
/** Height of cell content */
contentHeight: number;
/** Width of cell content */
contentWidth: number;
/** Width after text wrapping */
wrappedWidth: number;
/** Minimum readable width */
minReadableWidth: number;
/** Minimum width */
minWidth: number;
/** Actual cell width */
width: number;
/** Actual cell height */
height: number;
/** X position on page */
x: number;
/** Y position on page */
y: number;
constructor(raw: CellInput, styles: Styles, section: Section);
/** Get text position based on alignment */
getTextPos(): Pos;
/** Calculate content height with line spacing */
getContentHeight(scaleFactor: number, lineHeightFactor?: number): number;
/** Get padding value for specific side */
padding(name: "vertical" | "horizontal" | "top" | "bottom" | "left" | "right"): number;
}Usage Example:
// Cell manipulation in hooks
const willDrawCell = (data: CellHookData) => {
const cell = data.cell;
// Modify cell content
if (cell.text[0] === 'URGENT') {
// Change styling for urgent items
cell.styles.fillColor = [255, 0, 0];
cell.styles.textColor = [255, 255, 255];
cell.styles.fontStyle = 'bold';
}
// Position information
console.log(`Cell at (${cell.x}, ${cell.y}) size ${cell.width}x${cell.height}`);
// Content information
console.log(`Cell spans ${cell.colSpan} columns, ${cell.rowSpan} rows`);
console.log(`Cell content: ${cell.text.join('\n')}`);
};Represents table columns with width calculations and data mapping.
class Column {
/** Original column input data */
raw: ColumnInput | null;
/** Data key for mapping object properties */
dataKey: string | number;
/** Column index */
index: number;
/** Width after content wrapping */
wrappedWidth: number;
/** Minimum readable width */
minReadableWidth: number;
/** Minimum width */
minWidth: number;
/** Actual column width */
width: number;
constructor(dataKey: string | number, raw: ColumnInput | null, index: number);
/** Get maximum custom cell width in this column */
getMaxCustomCellWidth(table: Table): number;
}Usage Example:
// Column information is typically accessed during table creation
const table = __createTable(doc, {
columns: [
{ header: 'Name', dataKey: 'name' },
{ header: 'Score', dataKey: 'score' },
],
body: [
{ name: 'Alice', score: 95 },
{ name: 'Bob', score: 87 },
],
});
table.columns.forEach((column, index) => {
console.log(`Column ${index}: ${column.dataKey}`);
console.log(`Width: ${column.width}`);
const maxCustomWidth = column.getMaxCustomCellWidth(table);
console.log(`Max custom width: ${maxCustomWidth}`);
});Table-level configuration settings processed from UserOptions.
interface Settings {
includeHiddenHtml: boolean;
useCss: boolean;
theme: "striped" | "grid" | "plain";
startY: number;
margin: MarginPadding;
pageBreak: "auto" | "avoid" | "always";
rowPageBreak: "auto" | "avoid";
tableWidth: "auto" | "wrap" | number;
showHead: "everyPage" | "firstPage" | "never";
showFoot: "everyPage" | "lastPage" | "never";
tableLineWidth: number;
tableLineColor: Color;
horizontalPageBreak?: boolean;
horizontalPageBreakBehaviour?: "immediately" | "afterAllRows";
horizontalPageBreakRepeat?: string | number | string[] | number[] | null;
}Collection of style configurations for different table sections.
interface StylesProps {
styles: Partial<Styles>;
headStyles: Partial<Styles>;
bodyStyles: Partial<Styles>;
footStyles: Partial<Styles>;
alternateRowStyles: Partial<Styles>;
columnStyles: { [key: string]: Partial<Styles> };
}Processed content data for table creation.
type ContentSettings = {
body: Row[];
head: Row[];
foot: Row[];
columns: Column[];
};const didDrawPage = (data: HookData) => {
const table = data.table;
// Table metadata
console.log(`Table ID: ${table.id}`);
console.log(`Page: ${table.pageNumber}`);
// Structure information
console.log(`Columns: ${table.columns.length}`);
console.log(`Header rows: ${table.head.length}`);
console.log(`Body rows: ${table.body.length}`);
console.log(`Footer rows: ${table.foot.length}`);
// Calculated dimensions
const headHeight = table.getHeadHeight(table.columns);
const footHeight = table.getFootHeight(table.columns);
console.log(`Header height: ${headHeight}`);
console.log(`Footer height: ${footHeight}`);
};const didParseCell = (data: CellHookData) => {
const { cell, column, row } = data;
// Format currency values
if (column.dataKey === 'price') {
const price = parseFloat(cell.text[0]);
cell.text = [`$${price.toFixed(2)}`];
}
// Add status indicators
if (column.dataKey === 'status' && cell.text[0] === 'active') {
cell.text = ['✓ Active'];
cell.styles.textColor = [0, 128, 0];
}
};const willDrawCell = (data: CellHookData) => {
const { cell, row, table } = data;
// Highlight every 5th row
if (row.index % 5 === 0) {
cell.styles.fillColor = [255, 255, 200];
}
// Add borders to last row
if (row.index === table.body.length - 1) {
cell.styles.lineWidth = { bottom: 2 };
cell.styles.lineColor = [0, 0, 0];
}
};