# 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: ```typescript 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 ```typescript 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: ```typescript // ❌ 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 |