Files
2026-07-12 10:17:17 -04:00

1.6 KiB

PDF Generation Pagination

Source: src/lib/pdf.ts

Page-break logic

Each service calculates its height before rendering. Page break triggers when y + serviceHeight > CONTENT_BOTTOM - FOOTER_HEIGHT.

Key constants:

  • CONTENT_BOTTOM = PAGE_H - MARGIN (772)
  • FOOTER_HEIGHT = 35 (reserved for separator + footer text + page number)

Height calculation (must match rendering yPos increments)

svcH = 0
if hasDecision:     svcH += 5   // badge
svcH += 6                       // name + price
if priority:        svcH += 16  // priority label + reason
if recommendation:  svcH += 8
if explanation:     svcH += lines * 5 + 2
if partsNotInStock: svcH += 8 + (deliveryTime ? 8 : 0)
if aftermarket:     svcH += 8 + (partsList ? 8 : 0)
svcH += 6                       // separator + spacing

After page break

  • Call doc.addPage() then set yPos = MARGIN
  • Redraw current section header (SERVICES APPROVED / POSTPONED SERVICES) so new page has context
  • Summary box uses same FOOTER_HEIGHT check before rendering

Drawn on EVERY page via post-render loop:

const totalPages = doc.getNumberOfPages();
for (let i = 1; i <= totalPages; i++) {
    drawFooter(doc, i, totalPages, settings, shopChargeAmount);
}

Footer includes: separator line, shop message, validity note, "Page N of M"

String rendering (jspdf 4.x)

NEVER pass arrays to doc.text() — jspdf 4.x uses different internal line spacing than manual yPos tracking, causing cascading desync and text overlap. Always render splitTextToSize() results per-line:

doc.splitTextToSize(text, CW).forEach((line) => {
    doc.text(line, MARGIN, y);
    y += 5;
});