Last active
June 29, 2023 10:29
-
-
Save killan/a4f6cac70063aacd58df7ba43fefaeac to your computer and use it in GitHub Desktop.
jsPDF Goo's base
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { jsPDF } from "jspdf"; | |
export interface Margins { | |
left: number | |
right: number | |
top: number | |
bottom: number | |
} | |
export interface TableColumns { | |
title: string | |
field: string | |
width: number // Percent on 1 | |
align: "left" | "center" | "right" | "justify" | |
} | |
export interface RGBColor { | |
r: number, | |
g: number, | |
b: number, | |
a?: number | |
} | |
export const DIM_A4_POINTS = { width: 595, height: 842 } | |
export const DIM_MM_POINTS = 2.83465 | |
export const DIM_PX_POINTS = 0.75 // 1.333 | |
export type HeaderPrototype = (pdf: jsPDF) => void | |
export class KPdf { | |
doc: jsPDF | |
margins!: Margins // Defined in constructor | |
cellPaddings!: Margins | |
x: number | |
y: number | |
pageWidth!: number // Updated by constructor call | |
pageHeight!: number // Updated by constructor call | |
headerFct?: HeaderPrototype | |
footerFct?: HeaderPrototype | |
tableHeaderInitFct?: HeaderPrototype | |
tableContentInitFct?: HeaderPrototype | |
fontSize: number = 10 | |
lineHeight: number = 12 | |
constructor() { | |
this.doc = new jsPDF("p", "pt", "a4") | |
this.setMargins(20 * DIM_MM_POINTS) | |
this.setCellPaddings(0.5 * DIM_MM_POINTS) | |
this.x = this.margins.left | |
this.y = this.margins.top | |
} | |
// Setters | |
setMargins(margins: number): KPdf; | |
setMargins(left: number, right?: number, top?: number, bottom?: number): KPdf { | |
this.margins = right ? { left, right, top, bottom } as Margins | |
: { left: left, right: left, top: left, bottom: left } as Margins | |
this.updatePageDims() | |
return this | |
} | |
setCellPaddings(paddings: number): KPdf; | |
setCellPaddings(left: number, right?: number, top?: number, bottom?: number): KPdf { | |
this.cellPaddings = right ? { left, right, top, bottom } as Margins | |
: { left: left, right: left, top: left, bottom: left } as Margins | |
return this | |
} | |
setHeaderFct(fct: HeaderPrototype): KPdf { | |
this.headerFct = fct | |
return this | |
} | |
setFooterFct(fct: HeaderPrototype): KPdf { | |
this.footerFct = fct | |
return this | |
} | |
setTableHeaderInitFct(fct: HeaderPrototype): KPdf { | |
this.tableHeaderInitFct = fct | |
return this | |
} | |
setTableContentInitFct(fct: HeaderPrototype): KPdf { | |
this.tableContentInitFct = fct | |
return this | |
} | |
setX(x: number): KPdf { | |
this.x = x | |
return this | |
} | |
addX(value: number): KPdf { | |
this.x += value | |
return this | |
} | |
setY(y: number): KPdf { | |
this.y = y | |
return this | |
} | |
addY(value: number): KPdf { | |
this.y += value | |
return this | |
} | |
setXY(x: number, y: number): KPdf { | |
this.setX(x) | |
this.setY(y) | |
return this | |
} | |
setFontSize(fontSize: number): KPdf { | |
this.fontSize = fontSize | |
this.doc.setFontSize(fontSize) | |
return this | |
} | |
setLineHeight(lineHeight: number): KPdf { | |
this.lineHeight = lineHeight | |
return this | |
} | |
// Getters | |
getMargins(): Margins { | |
return this.margins | |
} | |
getCellPaddings(): Margins { | |
return this.cellPaddings | |
} | |
getX(): number { | |
return this.x | |
} | |
getY(): number { | |
return this.y | |
} | |
getXY(): { x: number, y: number } { | |
return { x: this.x, y: this.y } | |
} | |
getFontSize(): number { | |
return this.fontSize | |
} | |
getLineHeight(): number { | |
return this.lineHeight | |
} | |
updatePageDims(): void { | |
// A4 forced format on portait orientation | |
this.pageWidth = DIM_A4_POINTS.width - this.margins.left - this.margins.right | |
this.pageHeight = DIM_A4_POINTS.height - this.margins.top - this.margins.bottom | |
} | |
addHeader(): KPdf { | |
if (this.headerFct) { | |
this.headerFct(this.doc) | |
} | |
return this | |
} | |
addFooter(): KPdf { | |
if (this.footerFct) { | |
this.footerFct(this.doc) | |
} | |
return this | |
} | |
newPage(): KPdf { | |
this.doc.addPage() | |
this.addHeader().addFooter() | |
this.setXY( | |
this.margins.left, | |
this.margins.top | |
) | |
return this | |
} | |
// Table part | |
addTableHead(columns: TableColumns[]): KPdf { | |
if (this.tableHeaderInitFct) { | |
this.tableHeaderInitFct(this.doc) | |
} | |
// X, Y, fontSize and LineHeight already set by user | |
this.y += this.cellPaddings.top | |
columns.forEach((c) => { | |
const cellW = this.pageWidth * c.width | |
this.doc.text( | |
c.title, | |
this.x + (c.align === "right" ? cellW - this.cellPaddings.right : this.cellPaddings.left), | |
this.y, | |
{ | |
baseline: "top", | |
align: c.align | |
} | |
) | |
this.x += cellW | |
}) | |
this.y += this.lineHeight + this.cellPaddings.bottom | |
return this | |
} | |
addTableContent(columns: TableColumns[], data: any[], zebraColor: RGBColor = { r: 240, g: 240, b: 240 }): KPdf { | |
if (this.tableContentInitFct) { | |
this.tableContentInitFct(this.doc) | |
} | |
let zebra = true | |
data.forEach((d) => { | |
let maxCellHeight = 0 | |
// Find sizes | |
columns.forEach((c) => { | |
const cellW = this.pageWidth * c.width | |
// Cell size prep | |
const content: string[] = this.doc.splitTextToSize(d[c.field], cellW - this.cellPaddings.left - this.cellPaddings.right) | |
maxCellHeight = Math.max(maxCellHeight, content.length * this.lineHeight) | |
}) | |
if (zebra) { | |
this.doc | |
.setFillColor(zebraColor.r, zebraColor.g, zebraColor.b, zebraColor.a) | |
.rect( | |
this.x, | |
this.y, | |
this.pageWidth, | |
maxCellHeight + this.cellPaddings.top + this.cellPaddings.bottom, | |
"F" | |
) | |
} | |
zebra = !zebra | |
this.y += this.cellPaddings.top | |
// Write content | |
columns.forEach((c) => { | |
const cellW = this.pageWidth * c.width | |
const content: string[] = this.doc.splitTextToSize(d[c.field], cellW - this.cellPaddings.left - this.cellPaddings.right) | |
// Write cell content | |
content.forEach((line, i) => { | |
this.doc.text( | |
line, | |
this.x + (c.align === "right" ? cellW - this.cellPaddings.right : this.cellPaddings.left), | |
this.y + (i * this.lineHeight), | |
{ | |
baseline: "top", | |
align: c.align | |
} | |
) | |
}) | |
this.x += cellW | |
}) | |
this.x = this.margins.left | |
this.y += maxCellHeight + this.cellPaddings.bottom | |
// Page break | |
if (this.y >= this.margins.top + this.pageHeight) { | |
this.newPage() | |
this.addTableHead(columns) | |
if (this.tableContentInitFct) { | |
this.tableContentInitFct(this.doc) | |
} | |
} | |
}) | |
return this | |
} | |
save(fileName: string): KPdf { | |
this.doc.save(fileName) | |
return this | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const kpdf = new KPdf() | |
const daabooRed: RGBColor = { r: 148, g: 14, b: 14 } | |
kpdf | |
.setMargins( | |
20 * DIM_MM_POINTS, | |
20 * DIM_MM_POINTS, | |
30 * DIM_MM_POINTS, | |
30 * DIM_MM_POINTS | |
) | |
.setCellPaddings( | |
2 * DIM_MM_POINTS | |
) | |
.setHeaderFct((jsPDF) => { | |
jsPDF | |
.setTextColor("#333333") | |
.setFontSize(20) | |
.text( | |
"blah", | |
kpdf.getMargins().left + kpdf.pageWidth, | |
30, | |
{ | |
align: "right", | |
baseline: "top" | |
}) | |
.setFontSize(16) | |
.text( | |
"blah-blah", | |
kpdf.getMargins().left + kpdf.pageWidth, | |
54, | |
{ | |
align: "right", | |
baseline: "top" | |
}) | |
/*.rect( | |
kpdf.getMargins().left, | |
kpdf.getMargins().top, | |
kpdf.pageWidth, | |
kpdf.pageHeight | |
)*/ | |
}) | |
.setFooterFct((jsPDF) => { | |
jsPDF | |
.setTextColor("#616161") | |
.setFontSize(10) | |
.text([ | |
"Killan" | |
], kpdf.getMargins().left, | |
kpdf.getMargins().top + kpdf.pageHeight + 10, | |
{ | |
baseline: "top" | |
}) | |
.text([ | |
"daaboo.net" | |
], kpdf.getMargins().left + kpdf.pageWidth, | |
kpdf.getMargins().top + kpdf.pageHeight + 10, | |
{ | |
align: "right", | |
baseline: "top" | |
}) | |
.setDrawColor(daabooRed.r, daabooRed.g, daabooRed.b) | |
.line( | |
kpdf.getMargins().left, | |
kpdf.getMargins().top + kpdf.pageHeight, | |
kpdf.getMargins().left + kpdf.pageWidth, | |
kpdf.getMargins().top + kpdf.pageHeight | |
) | |
}) | |
// Manage first page | |
kpdf.addHeader().addFooter() | |
// Add address | |
const adrLeft = kpdf.getMargins().left + (kpdf.pageWidth * 0.5) | |
const adr = [ | |
"indented text like address" | |
] | |
kpdf.doc | |
.setTextColor("#333333") | |
.setFontSize(12) | |
.text( | |
adr, | |
adrLeft, | |
kpdf.getMargins().top + (10 * DIM_MM_POINTS), | |
{ | |
baseline: "top" | |
} | |
) | |
// Prep table | |
const columns: TableColumns[] = [ | |
{ | |
title: "col1", | |
width: 0.51, | |
align: "left", | |
field: "field1" | |
}, | |
{ | |
title: "col2", | |
width: 0.10, | |
align: "right", | |
field: "field2" | |
}, | |
{ | |
title: "col3", | |
width: 0.15, | |
align: "right", | |
field: "field3" | |
}, | |
{ | |
title: "col4", | |
width: 0.12, | |
align: "right", | |
field: "field4" | |
}, | |
{ | |
title: "col5", | |
width: 0.12, | |
align: "right", | |
field: "field5" | |
} | |
] | |
// Position start (first page) | |
kpdf.setXY(kpdf.getMargins().left, kpdf.getMargins().top + (30 * DIM_MM_POINTS) + (adr.length * 14)) | |
// Config table init fct | |
// Helps page break display | |
kpdf | |
.setTableHeaderInitFct((jsPDF) => { | |
kpdf.setFontSize(12) | |
.setLineHeight(12) | |
.doc.setTextColor(daabooRed.r, daabooRed.g, daabooRed.b) | |
}) | |
.setTableContentInitFct((jsPDF) => { | |
kpdf.setX(kpdf.getMargins().left) | |
.setLineHeight(14) | |
.doc.setTextColor("#333333") | |
}) | |
kpdf | |
.addTableHead(columns) | |
.addTableContent( | |
columns, | |
[] | |
) | |
kpdf.save("mydoc.pdf") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Related to https://blog.daaboo.net/2023/06/jspdf-le-nouveau-fpdf/