@lichens-innovation/ts-common 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pdf.cjs CHANGED
@@ -126,13 +126,13 @@ var PdfGenerator = class {
126
126
  head,
127
127
  body,
128
128
  startY = this._currentY,
129
- columnWidths
129
+ columnWidths,
130
+ cellPadding = 0.05
130
131
  } = addTableArgs;
131
132
  if (body.length === 0) {
132
133
  this._currentY = startY;
133
134
  return startY;
134
135
  }
135
- const cellPadding = 0.05;
136
136
  const fontSize = this.theme.fontSize.small;
137
137
  const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);
138
138
  const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);
package/dist/pdf.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":["format","jsPDF","autoTable"],"mappings":"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","file":"pdf.cjs","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from \"date-fns\";\nimport type { jsPDF, jsPDFOptions } from \"jspdf\";\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF[\"internal\"][\"pageSize\"];\nexport type PageOrientation = jsPDFOptions[\"orientation\"];\nexport type PaperFormat = jsPDFOptions[\"format\"];\n\nexport const DEFAULT_REPORT_FILENAME = \"report.pdf\";\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === \"left\") return format(new Date(), \"yyyy-MM-dd HH:mm\");\n if (align === \"center\") return \"\";\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: \"portrait\",\n paperFormat: \"letter\",\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = \"left\" | \"center\" | \"right\";\nexport type TableCellValign = \"top\" | \"middle\" | \"bottom\";\nexport type FontStyle = \"normal\" | \"bold\";\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { isNullish, type Dimensions } from \"../utils\";\nimport { jsPDF, type ImageCompression, type ImageFormat } from \"jspdf\";\nimport { autoTable } from \"jspdf-autotable\";\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type PageSize,\n type PdfOptions,\n type ThemeConfig,\n} from \"./pdf-generator.types\";\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: \"in\" });\n this.doc.setFont(\"helvetica\");\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn(\"[checkTableOverflow] table overflow\", infos);\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const cellPadding = 0.05;\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: \"grid\",\n tableLineWidth,\n tableLineColor: lineColor,\n margin: { left: this.margin, right: this.margin },\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = \"PNG\";\n const imageCompression: ImageCompression = \"NONE\";\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = \"PNG\";\n const imageCompression: ImageCompression = \"NONE\";\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: \"left\" as const },\n 1: { cellWidth, halign: \"center\" as const },\n 2: { cellWidth, halign: \"right\" as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn(\"[renderFooters] Footer is disabled\");\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = ([\"left\", \"center\", \"right\"] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: \"plain\",\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n \"S\" // Stroke only, no fill\n );\n } catch (error) {\n console.error(\"[drawAvailableAreaRectangle] Error drawing available area rect\", error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":["format","jsPDF","autoTable"],"mappings":"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","file":"pdf.cjs","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from 'date-fns';\nimport type { jsPDF, jsPDFOptions } from 'jspdf';\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF['internal']['pageSize'];\nexport type PageOrientation = jsPDFOptions['orientation'];\nexport type PaperFormat = jsPDFOptions['format'];\n\nexport const DEFAULT_REPORT_FILENAME = 'report.pdf';\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === 'left') return format(new Date(), 'yyyy-MM-dd HH:mm');\n if (align === 'center') return '';\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: 'portrait',\n paperFormat: 'letter',\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = 'left' | 'center' | 'right';\nexport type TableCellValign = 'top' | 'middle' | 'bottom';\nexport type FontStyle = 'normal' | 'bold';\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n /** Cell padding in doc units (inches) (default 0.05). */\n cellPadding?: number;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { isNullish, type Dimensions } from '../utils';\nimport { jsPDF, type ImageCompression, type ImageFormat } from 'jspdf';\nimport { autoTable } from 'jspdf-autotable';\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type PageSize,\n type PdfOptions,\n type ThemeConfig,\n} from './pdf-generator.types';\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: 'in' });\n this.doc.setFont('helvetica');\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn('[checkTableOverflow] table overflow', infos);\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n cellPadding = 0.05,\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: 'grid',\n tableLineWidth,\n tableLineColor: lineColor,\n margin: { left: this.margin, right: this.margin },\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: 'left' as const },\n 1: { cellWidth, halign: 'center' as const },\n 2: { cellWidth, halign: 'right' as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn('[renderFooters] Footer is disabled');\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = (['left', 'center', 'right'] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: 'plain',\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n 'S' // Stroke only, no fill\n );\n } catch (error) {\n console.error('[drawAvailableAreaRectangle] Error drawing available area rect', error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
package/dist/pdf.d.cts CHANGED
@@ -16,9 +16,9 @@ interface ThemeConfig {
16
16
  primaryColor: RgbColor;
17
17
  fontSize: FontSize;
18
18
  }
19
- type PageSize = jsPDF["internal"]["pageSize"];
20
- type PageOrientation = jsPDFOptions["orientation"];
21
- type PaperFormat = jsPDFOptions["format"];
19
+ type PageSize = jsPDF['internal']['pageSize'];
20
+ type PageOrientation = jsPDFOptions['orientation'];
21
+ type PaperFormat = jsPDFOptions['format'];
22
22
  declare const DEFAULT_REPORT_FILENAME = "report.pdf";
23
23
  declare const IMAGE_GAP = 0.5;
24
24
  declare const DEFAULT_THEME: ThemeConfig;
@@ -48,9 +48,9 @@ declare const Gaps: {
48
48
  X_LARGE: number;
49
49
  };
50
50
  declare const PdfColors: Record<string, FillColorRgbTuple>;
51
- type TableCellHalign = "left" | "center" | "right";
52
- type TableCellValign = "top" | "middle" | "bottom";
53
- type FontStyle = "normal" | "bold";
51
+ type TableCellHalign = 'left' | 'center' | 'right';
52
+ type TableCellValign = 'top' | 'middle' | 'bottom';
53
+ type FontStyle = 'normal' | 'bold';
54
54
  type FillColorRgbTuple = [number, number, number];
55
55
  type CellStyle = {
56
56
  cellWidth?: number;
@@ -87,6 +87,8 @@ interface AddTableArgs {
87
87
  lineWidth?: number;
88
88
  /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */
89
89
  lineColor?: number;
90
+ /** Cell padding in doc units (inches) (default 0.05). */
91
+ cellPadding?: number;
90
92
  }
91
93
  interface AddImageArgs {
92
94
  dataUri: string;
package/dist/pdf.d.ts CHANGED
@@ -16,9 +16,9 @@ interface ThemeConfig {
16
16
  primaryColor: RgbColor;
17
17
  fontSize: FontSize;
18
18
  }
19
- type PageSize = jsPDF["internal"]["pageSize"];
20
- type PageOrientation = jsPDFOptions["orientation"];
21
- type PaperFormat = jsPDFOptions["format"];
19
+ type PageSize = jsPDF['internal']['pageSize'];
20
+ type PageOrientation = jsPDFOptions['orientation'];
21
+ type PaperFormat = jsPDFOptions['format'];
22
22
  declare const DEFAULT_REPORT_FILENAME = "report.pdf";
23
23
  declare const IMAGE_GAP = 0.5;
24
24
  declare const DEFAULT_THEME: ThemeConfig;
@@ -48,9 +48,9 @@ declare const Gaps: {
48
48
  X_LARGE: number;
49
49
  };
50
50
  declare const PdfColors: Record<string, FillColorRgbTuple>;
51
- type TableCellHalign = "left" | "center" | "right";
52
- type TableCellValign = "top" | "middle" | "bottom";
53
- type FontStyle = "normal" | "bold";
51
+ type TableCellHalign = 'left' | 'center' | 'right';
52
+ type TableCellValign = 'top' | 'middle' | 'bottom';
53
+ type FontStyle = 'normal' | 'bold';
54
54
  type FillColorRgbTuple = [number, number, number];
55
55
  type CellStyle = {
56
56
  cellWidth?: number;
@@ -87,6 +87,8 @@ interface AddTableArgs {
87
87
  lineWidth?: number;
88
88
  /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */
89
89
  lineColor?: number;
90
+ /** Cell padding in doc units (inches) (default 0.05). */
91
+ cellPadding?: number;
90
92
  }
91
93
  interface AddImageArgs {
92
94
  dataUri: string;
package/dist/pdf.js CHANGED
@@ -124,13 +124,13 @@ var PdfGenerator = class {
124
124
  head,
125
125
  body,
126
126
  startY = this._currentY,
127
- columnWidths
127
+ columnWidths,
128
+ cellPadding = 0.05
128
129
  } = addTableArgs;
129
130
  if (body.length === 0) {
130
131
  this._currentY = startY;
131
132
  return startY;
132
133
  }
133
- const cellPadding = 0.05;
134
134
  const fontSize = this.theme.fontSize.small;
135
135
  const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);
136
136
  const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);
package/dist/pdf.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":[],"mappings":"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","file":"pdf.js","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from \"date-fns\";\nimport type { jsPDF, jsPDFOptions } from \"jspdf\";\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF[\"internal\"][\"pageSize\"];\nexport type PageOrientation = jsPDFOptions[\"orientation\"];\nexport type PaperFormat = jsPDFOptions[\"format\"];\n\nexport const DEFAULT_REPORT_FILENAME = \"report.pdf\";\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === \"left\") return format(new Date(), \"yyyy-MM-dd HH:mm\");\n if (align === \"center\") return \"\";\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: \"portrait\",\n paperFormat: \"letter\",\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = \"left\" | \"center\" | \"right\";\nexport type TableCellValign = \"top\" | \"middle\" | \"bottom\";\nexport type FontStyle = \"normal\" | \"bold\";\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { isNullish, type Dimensions } from \"../utils\";\nimport { jsPDF, type ImageCompression, type ImageFormat } from \"jspdf\";\nimport { autoTable } from \"jspdf-autotable\";\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type PageSize,\n type PdfOptions,\n type ThemeConfig,\n} from \"./pdf-generator.types\";\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: \"in\" });\n this.doc.setFont(\"helvetica\");\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn(\"[checkTableOverflow] table overflow\", infos);\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const cellPadding = 0.05;\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: \"grid\",\n tableLineWidth,\n tableLineColor: lineColor,\n margin: { left: this.margin, right: this.margin },\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = \"PNG\";\n const imageCompression: ImageCompression = \"NONE\";\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = \"PNG\";\n const imageCompression: ImageCompression = \"NONE\";\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: \"left\" as const },\n 1: { cellWidth, halign: \"center\" as const },\n 2: { cellWidth, halign: \"right\" as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn(\"[renderFooters] Footer is disabled\");\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = ([\"left\", \"center\", \"right\"] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: \"plain\",\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n \"S\" // Stroke only, no fill\n );\n } catch (error) {\n console.error(\"[drawAvailableAreaRectangle] Error drawing available area rect\", error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/types.utils.ts","../src/pdf/pdf-generator.types.ts","../src/pdf/pdf-generator.ts"],"names":[],"mappings":"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","file":"pdf.js","sourcesContent":["export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import { format } from 'date-fns';\nimport type { jsPDF, jsPDFOptions } from 'jspdf';\n\ninterface FontSize {\n title: number;\n header: number;\n normal: number;\n small: number;\n tiny: number;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ThemeConfig {\n primaryColor: RgbColor;\n fontSize: FontSize;\n}\n\nexport type PageSize = jsPDF['internal']['pageSize'];\nexport type PageOrientation = jsPDFOptions['orientation'];\nexport type PaperFormat = jsPDFOptions['format'];\n\nexport const DEFAULT_REPORT_FILENAME = 'report.pdf';\nexport const IMAGE_GAP = 0.5; // space in inches between image and footer\n\nexport const DEFAULT_THEME: ThemeConfig = {\n primaryColor: { r: 0, g: 0, b: 0 }, // black\n fontSize: {\n title: 16,\n header: 10,\n normal: 9,\n small: 8,\n tiny: 7,\n },\n};\n\nexport interface FooterCellArgs {\n align: TableCellHalign;\n currentPage: number;\n totalPages: number;\n}\nexport type FooterCellBuilder = (footerCellArgs: FooterCellArgs) => string;\n\nexport interface PdfOptions {\n orientation?: PageOrientation;\n paperFormat?: PaperFormat;\n hasFooter?: boolean;\n filename?: string;\n theme?: ThemeConfig;\n margin?: number;\n displayAvailableAreaRectangle?: boolean;\n footerCellBuilder?: FooterCellBuilder;\n}\n\nexport const DEFAULT_FOOTER_CELL_BUILDER = ({ align, currentPage, totalPages }: FooterCellArgs): string => {\n if (align === 'left') return format(new Date(), 'yyyy-MM-dd HH:mm');\n if (align === 'center') return '';\n return `Page ${currentPage}/${totalPages}`;\n};\n\nexport const DEFAULT_OPTIONS: Readonly<Required<PdfOptions>> = {\n orientation: 'portrait',\n paperFormat: 'letter',\n filename: DEFAULT_REPORT_FILENAME,\n theme: DEFAULT_THEME,\n margin: 0.5,\n displayAvailableAreaRectangle: false,\n hasFooter: true,\n footerCellBuilder: DEFAULT_FOOTER_CELL_BUILDER,\n};\n\nexport const Gaps = {\n TINY: 0.02,\n SMALL: 0.04,\n MEDIUM: 0.08,\n LARGE: 0.16,\n X_LARGE: 0.32,\n};\n\nexport const PdfColors: Record<string, FillColorRgbTuple> = {\n LIGHT_GRAY: [240, 240, 240],\n};\n\nexport type TableCellHalign = 'left' | 'center' | 'right';\nexport type TableCellValign = 'top' | 'middle' | 'bottom';\nexport type FontStyle = 'normal' | 'bold';\nexport type FillColorRgbTuple = [number, number, number];\n\nexport type CellStyle = {\n cellWidth?: number;\n halign?: TableCellHalign;\n valign?: TableCellValign;\n fontStyle?: FontStyle;\n fillColor?: FillColorRgbTuple;\n};\n\nexport type CellValue = string | number;\n\n/** Cell with rowSpan/colSpan for merged cells (passed through to autoTable). */\nexport interface TableCellDef {\n content: string | string[] | number;\n rowSpan?: number;\n colSpan?: number;\n}\n\nexport type TableCellInput = CellValue | TableCellDef;\n\n/** Parameters for adding a table (generic API, no jspdf-autotable leak). */\nexport interface AddTableArgs {\n /** Each row is an array of cell values or cell defs (with rowSpan/colSpan). */\n body: TableCellInput[][];\n /** Optional header row (single row of cell values). */\n head?: CellValue[];\n /** Y position to start the table (uses generator currentY if omitted). */\n startY?: number;\n\n /** Optional column widths (in same units as doc, e.g. inches). Sum should match available width. */\n columnWidths?: number[];\n /** Optional per-column styles (e.g. halign, fontStyle). Merged with auto-generated cellWidth. */\n columnStyles?: Record<number, CellStyle>;\n\n /** Whether to draw the table outer border (default true). */\n drawLineForTable?: boolean;\n /** Whether to draw cell borders (default true). */\n drawLineForCells?: boolean;\n /** Table border line width (default 0.005). */\n lineWidth?: number;\n /** Table border color as a grayscale value: 0 = black, 200 = light gray, 255 = white (default 0). */\n lineColor?: number;\n /** Cell padding in doc units (inches) (default 0.05). */\n cellPadding?: number;\n}\n\nexport interface AddImageArgs {\n dataUri: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n","import { isNullish, type Dimensions } from '../utils';\nimport { jsPDF, type ImageCompression, type ImageFormat } from 'jspdf';\nimport { autoTable } from 'jspdf-autotable';\nimport {\n DEFAULT_FOOTER_CELL_BUILDER,\n DEFAULT_OPTIONS,\n IMAGE_GAP,\n type AddImageArgs,\n type AddTableArgs,\n type CellStyle,\n type PageSize,\n type PdfOptions,\n type ThemeConfig,\n} from './pdf-generator.types';\n\nexport class PdfGenerator {\n private doc: jsPDF;\n private options: Required<PdfOptions>;\n private _currentY: number;\n\n constructor(options: PdfOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const { orientation, paperFormat } = this.options;\n this.doc = new jsPDF({ orientation, format: paperFormat, unit: 'in' });\n this.doc.setFont('helvetica');\n\n this._currentY = this.margin;\n }\n\n get font(): string {\n return this.doc.getFont().fontName;\n }\n\n set font(font: string) {\n this.doc.setFont(font);\n }\n\n get theme(): ThemeConfig {\n return this.options.theme;\n }\n\n get filename(): string {\n return this.options.filename;\n }\n\n get margin(): number {\n return this.options.margin;\n }\n\n get pageSize(): PageSize {\n return this.doc.internal.pageSize;\n }\n\n get pageWidth(): number {\n return this.pageSize.width;\n }\n\n get pageHeight(): number {\n return this.pageSize.height;\n }\n\n get fullImageHeight(): number {\n return this.availableHeight - this.footerHeight - IMAGE_GAP;\n }\n\n get availableWidth(): number {\n return this.pageWidth - 2 * this.margin;\n }\n\n get availableHeight(): number {\n return this.pageHeight - 2 * this.margin;\n }\n\n get footerFontSizePoints(): number {\n return this.theme.fontSize.tiny;\n }\n\n /** Current Y position (in doc units) for layout. */\n get currentY(): number {\n return this._currentY;\n }\n\n /** Set current Y position (e.g. after custom content like header or chart). */\n public setCurrentY(y: number): void {\n this._currentY = y;\n }\n\n public addVerticalGap(delta: number): void {\n this._currentY += delta;\n }\n\n public checkTableOverflow(columnWidths: number[]): void {\n const computedSum = columnWidths.reduce((acc, width) => acc + width, 0);\n if (computedSum <= this.availableWidth) return;\n\n const infos = {\n availableWidth: this.availableWidth,\n computedSum,\n columnWidths: JSON.stringify(columnWidths),\n };\n console.warn('[checkTableOverflow] table overflow', infos);\n }\n\n public addTable(addTableArgs: AddTableArgs) {\n const {\n lineWidth = 0.005,\n lineColor = 0,\n drawLineForTable = true,\n drawLineForCells = true,\n head,\n body,\n startY = this._currentY,\n columnWidths,\n cellPadding = 0.05,\n } = addTableArgs;\n\n if (body.length === 0) {\n this._currentY = startY;\n return startY;\n }\n\n const fontSize = this.theme.fontSize.small;\n const colCount = columnWidths?.length ?? Math.max(...body.map((row) => row.length), 0);\n const widths = columnWidths ?? Array(colCount).fill(this.availableWidth / colCount);\n const columnStyles: Record<number, CellStyle> = {};\n widths.forEach((cellWidth, i) => {\n columnStyles[i] = { cellWidth, ...addTableArgs.columnStyles?.[i] };\n });\n\n const tableLineWidth = drawLineForTable ? lineWidth : 0;\n const cellLineWidth = drawLineForCells ? lineWidth : 0;\n\n autoTable(this.doc, {\n startY,\n head: !isNullish(head) ? [head] : undefined,\n body,\n theme: 'grid',\n tableLineWidth,\n tableLineColor: lineColor,\n margin: { left: this.margin, right: this.margin },\n bodyStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n headStyles: { fontSize, font: this.font, cellPadding, lineWidth: cellLineWidth, lineColor },\n columnStyles,\n });\n\n const finalY = (this.doc as unknown as { lastAutoTable: { finalY: number } }).lastAutoTable?.finalY ?? startY;\n this._currentY = finalY;\n }\n\n /**\n * Add an image at the given position and size. Does not update currentY.\n */\n public addImage({ dataUri, x, y, width, height }: AddImageArgs): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n this.doc.addImage(dataUri, imageFormat, x, y, width, height, undefined, imageCompression);\n }\n\n /** Exposes the jsPDF document for custom drawing (e.g. header text, status badge, certification). */\n public getDoc(): jsPDF {\n return this.doc;\n }\n\n public addPage() {\n this.doc.addPage();\n }\n\n public addFullPagePNG(dataURI: string, aspectRatio: number): void {\n const imageFormat: ImageFormat = 'PNG';\n const imageCompression: ImageCompression = 'NONE';\n\n const alias = undefined;\n const x = this.margin;\n const y = this.margin;\n const { width, height } = this.calculateImageDimensions(aspectRatio);\n\n this.doc.addImage(dataURI, imageFormat, x, y, width, height, alias, imageCompression);\n\n this._currentY = this.margin + height + IMAGE_GAP;\n }\n\n private calculateImageDimensions(aspectRatio: number): Dimensions {\n const width = this.availableWidth;\n\n // Calculate height based on canvas aspect ratio to maintain proper proportions\n const height = width / aspectRatio;\n\n // Ensure the image doesn't exceed available space (accounting for footer)\n const maxHeight = this.availableHeight - this.footerHeight - IMAGE_GAP;\n const finalHeight = Math.min(height, maxHeight);\n\n // If image is reduced, adjust width to maintain aspect ratio\n const finalWidth = finalHeight < height ? finalHeight * aspectRatio : width;\n\n return { width: finalWidth, height: finalHeight };\n }\n\n public save(): void {\n this.doc.save(this.filename);\n }\n\n private buildFooterColumnStyles() {\n const cellWidth = this.availableWidth / 3;\n\n return {\n 0: { cellWidth, halign: 'left' as const },\n 1: { cellWidth, halign: 'center' as const },\n 2: { cellWidth, halign: 'right' as const },\n };\n }\n\n public renderFooters(): void {\n if (!this.options.hasFooter) {\n console.warn('[renderFooters] Footer is disabled');\n return;\n }\n\n const totalPages = this.doc.getNumberOfPages();\n const columnStyles = this.buildFooterColumnStyles();\n const startY = this.pageHeight - this.margin - this.footerHeight;\n\n const footerCellBuilder = this.options.footerCellBuilder ?? DEFAULT_FOOTER_CELL_BUILDER;\n\n for (let currentPage = 1; currentPage <= totalPages; currentPage++) {\n this.doc.setPage(currentPage);\n this.drawAvailableAreaRectangle();\n const footerCells = (['left', 'center', 'right'] as const).map((align) => ({\n content: footerCellBuilder({ align, currentPage, totalPages }),\n }));\n\n autoTable(this.doc, {\n startY,\n body: [footerCells],\n theme: 'plain',\n bodyStyles: { fontSize: this.footerFontSizePoints, font: this.font },\n margin: { left: this.margin, right: this.margin },\n columnStyles,\n });\n }\n }\n\n /** Footer is a single line; height derived from font size and table padding. */\n get footerHeight(): number {\n if (!this.options.hasFooter) {\n return 0;\n }\n\n const fontSizeInches = this.footerFontSizePoints / 72; // 1 inch = 72 points\n const lineHeight = fontSizeInches * 1.5; // autoTable line height\n return lineHeight + 0.2; // table borders and minimal padding\n }\n\n private drawAvailableAreaRectangle(): void {\n if (!this.options.displayAvailableAreaRectangle) {\n return;\n }\n\n // Save current style state\n const currentDrawColor = this.doc.getDrawColor();\n const currentLineWidth = this.doc.getLineWidth();\n\n try {\n this.doc.setDrawColor(200, 200, 200); // Light gray\n this.doc.setLineWidth(0.01);\n this.doc.rect(\n this.margin,\n this.margin,\n this.availableWidth,\n this.availableHeight,\n 'S' // Stroke only, no fill\n );\n } catch (error) {\n console.error('[drawAvailableAreaRectangle] Error drawing available area rect', error);\n } finally {\n // Restore previous state\n this.doc.setDrawColor(currentDrawColor);\n this.doc.setLineWidth(currentLineWidth);\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lichens-innovation/ts-common",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "Reusable generic typescript utilities, types, constants, helpers",
5
5
  "keywords": [
6
6
  "typescript",
@@ -149,53 +149,43 @@
149
149
  "bun": ">=1.2.0"
150
150
  },
151
151
  "packageManager": "bun@1.3.14",
152
- "lint-staged": {
153
- "*.{ts,tsx}": [
154
- "eslint --fix",
155
- "prettier --write"
156
- ],
157
- "*.{json,md}": [
158
- "prettier --write"
159
- ]
160
- },
161
152
  "devDependencies": {
162
153
  "@commitlint/cli": "20.4.4",
163
- "@rjsf/utils": "6.4.1",
164
- "@rjsf/validator-ajv8": "6.4.1",
165
- "ajv-i18n": "4.2.0",
166
- "i18next": "25.8.18",
167
- "react-i18next": "16.5.8",
168
154
  "@commitlint/config-conventional": "20.4.4",
169
155
  "@eslint/js": "10.0.1",
170
- "@faker-js/faker": "10.3.0",
156
+ "@rjsf/utils": "6.4.1",
157
+ "@rjsf/validator-ajv8": "6.4.1",
158
+ "@semantic-release/changelog": "6.0.3",
159
+ "@semantic-release/exec": "6.0.3",
160
+ "@semantic-release/git": "10.0.1",
161
+ "@semantic-release/github": "10.0.0",
171
162
  "@types/node": "25.5.0",
172
163
  "@types/papaparse": "5.5.2",
173
164
  "@vitest/coverage-v8": "4.1.0",
165
+ "ajv-i18n": "4.2.0",
174
166
  "commitlint": "20.4.4",
175
167
  "cross-env": "10.1.0",
176
168
  "eslint": "10.0.3",
177
169
  "exceljs": "4.4.0",
178
170
  "globals": "17.4.0",
179
171
  "husky": "9.1.7",
172
+ "i18next": "25.8.18",
180
173
  "jspdf": "4.2.0",
181
174
  "jspdf-autotable": "5.0.7",
175
+ "knip": "6.29.0",
182
176
  "mime": "4.1.0",
183
177
  "papaparse": "5.5.3",
184
178
  "pino": "10.3.1",
185
179
  "pino-pretty": "13.1.3",
186
180
  "prettier": "3.8.1",
181
+ "react-i18next": "16.5.8",
187
182
  "rimraf": "6.1.3",
183
+ "semantic-release": "24.0.0",
188
184
  "tsup": "8.5.1",
189
185
  "typescript": "5.9.3",
190
186
  "typescript-eslint": "8.57.0",
191
187
  "vite": "8.0.0",
192
188
  "vite-tsconfig-paths": "6.1.1",
193
- "vitest": "4.1.0",
194
- "lint-staged": "15.0.0",
195
- "semantic-release": "24.0.0",
196
- "@semantic-release/changelog": "6.0.3",
197
- "@semantic-release/exec": "6.0.3",
198
- "@semantic-release/git": "10.0.1",
199
- "@semantic-release/github": "10.0.0"
189
+ "vitest": "4.1.0"
200
190
  }
201
191
  }