Use when building complete PDF documents from scratch with pdf-lib — invoices, reports, certificates, or multi-page layouts. Prevents content overflow by providing page-break detection and content flow patterns. Covers multi-page generation, headers/footers, page numbering, table layouts, templates. Keywords: invoice, report, template, header, footer, page number, table, multi-page, generate, create PDF, generate invoice, build report, make PDF from scratch, PDF with tables.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Use when building complete PDF documents from scratch with pdf-lib — invoices, reports, certificates, or multi-page layouts. Prevents content overflow by providing page-break detection and content flow patterns. Covers multi-page generation, headers/footers, page numbering, table layouts, templates. Keywords: invoice, report, template, header, footer, page number, table, multi-page, generate, create PDF, generate invoice, build report, make PDF from scratch, PDF with tables.
license
MIT
compatibility
Designed for Claude Code. Requires pdf-lib 1.x with TypeScript/JavaScript.
ALWAYS use height - offset to position content from the top
NEVER assume y=0 is the top of the page
(0, height) -------- (width, height) <- top of page
| |
| CONTENT AREA |
| |
(0, 0) ----------- (width, 0) <- bottom of page
Decision Trees
Which Page Size?
Need a page size?
+-- Standard paper size?
| +-- International -> PageSizes.A4 (595.28 x 841.89 pt)
| +-- North America -> PageSizes.Letter (612 x 792 pt)
| +-- Legal paper -> PageSizes.Legal (612 x 1008 pt)
+-- Custom dimensions?
| +-- pdfDoc.addPage([widthPt, heightPt])
+-- No preference?
+-- pdfDoc.addPage() (default dimensions)
Which Output Format?
Saving the PDF?
+-- Write to file (Node.js) -> pdfDoc.save() -> fs.writeFileSync('out.pdf', pdfBytes)
+-- Send as HTTP response -> pdfDoc.save() -> Buffer.from(pdfBytes)
+-- Display in browser -> pdfDoc.saveAsBase64({ dataUri: true }) -> set as iframe src
+-- Store in database -> pdfDoc.save() -> store Uint8Array as blob
+-- Email attachment -> pdfDoc.saveAsBase64() -> attach as base64
Content Overflow?
Content might not fit on page?
+-- Track current Y position manually
+-- When Y < bottomMargin -> add new page, reset Y to top
+-- NEVER rely on pdf-lib to auto-paginate (it does NOT)
Core Patterns
Pattern 1: Multi-Page Document with Content Overflow
ALWAYS track the current Y position manually. pdf-lib has NO automatic pagination.
// Save as Uint8Array (Node.js file write, HTTP response)constpdfBytes: Uint8Array = await pdfDoc.save();
// Node.js: write to fileimport { writeFileSync } from'fs';
writeFileSync('output.pdf', pdfBytes);
// Save as Base64 (email attachment, database storage)constbase64String: string = await pdfDoc.saveAsBase64();
// Save as data URI (browser display in iframe)constdataUri: string = await pdfDoc.saveAsBase64({ dataUri: true });
// In browser: document.getElementById('iframe').src = dataUri;
Critical Rules
ALWAYS await async methods: create(), embedFont(), embedPng(), embedJpg(), save(), saveAsBase64() are ALL async. Forgetting await on embedFont() passes a Promise instead of a PDFFont to drawText().
ALWAYS embed fonts and images ONCE before the page loop: Embedding is expensive. Embed at document level, then reuse the returned PDFFont/PDFImage on every page.
ALWAYS track Y position manually for multi-page content: pdf-lib does NOT auto-paginate. When currentY < bottomMargin, call pdfDoc.addPage() and reset currentY.
ALWAYS use height - offset for top-down positioning: The PDF coordinate origin is bottom-left. To place content 50pt from the top: y = height - 50.
NEVER assume pdf-lib wraps text across pages: maxWidth wraps text within a single drawText() call on one page. It does NOT continue onto the next page.
ALWAYS add headers/footers AFTER generating all content pages: Iterate pdfDoc.getPages() to stamp headers, footers, and page numbers on every page, so totalPages is accurate.
NEVER use rgb values 0-255: pdf-lib rgb() takes values 0.0 to 1.0. rgb(255, 0, 0) does NOT create red.