2.2 KiB
2.2 KiB
PDF Generation — Pagination & Layout
Applies to: src/lib/pdf.ts in spq-v2.
Page Break Logic
The correct page-break pattern for jsPDF 4.x:
const CONTENT_BOTTOM = PAGE_H - MARGIN; // 772
const FOOTER_HEIGHT = 35; // reserved for footer separator + text + page number
// Before rendering each service, check if it fits:
let svcH = 0;
svcH += 5; // decision badge (if present)
svcH += 6; // name + price
svcH += 16; // priority (if present)
svcH += 8; // recommendation (if present)
svcH += doc.splitTextToSize(svc.explanation, CW).length * 5 + 2; // explanation
svcH += 8; // parts flags (each, if present)
svcH += 6; // separator + spacing
if (y + svcH > CONTENT_BOTTOM - FOOTER_HEIGHT) {
y = newPage(doc); // doc.addPage() + y = MARGIN
// Redraw section header on new page
doc.setFont(...); doc.text(currentSectionLabel, MARGIN, y); y += 8;
}
Do NOT use yPos > PAGE_HEIGHT * 0.7 as a guard — it blocks breaks in the top 70% of the page, forcing content past the page edge.
Footer Rendering
function drawFooter(doc, pageNum, totalPages) {
doc.setPage(pageNum);
const fy = PAGE_H - MARGIN - FOOTER_HEIGHT + 5;
doc.line(MARGIN, fy, PAGE_W - MARGIN, fy);
doc.text('message', PAGE_W / 2, fy + 15, { align: 'center' });
doc.text(`Page ${pageNum} of ${totalPages}`, PAGE_W - MARGIN, PAGE_H - 10, { align: 'right' });
}
// After all content: loop over pages to draw footer on EVERY page
for (let i = 1; i <= doc.getNumberOfPages(); i++) drawFooter(doc, i, totalPages);
Text Rendering (jspdf 4.x)
Never pass arrays to doc.text(). Render splitTextToSize results one line at a time:
// ❌ Causes overlap in jspdf 4.x:
doc.text(lines, MARGIN, yPos);
yPos += lines.length * 6;
// ✅ Correct:
lines.forEach((line) => { doc.text(line, MARGIN, yPos); yPos += 6; });
Height Estimator Constants
The height estimator must match actual yPos increments exactly:
| Element | yPos Advance |
|---|---|
| Decision badge | 5 |
| Name + price | 6 |
| Priority label | 6 |
| Priority reason | 10 |
| Recommendation | 8 |
| Explanation line | 5 |
| Explanation gap | 2 |
| Parts flag | 8 |
| Aftermarket flag | 8 |
| Separator + spacing | 6 |