# Tasks — @suaveplan/pdf migration ## Chunk A — Foundation (THIS CHANGE) Status: **SHIPPED** in this change. - [x] Scaffold `packages/devtools/docs/pdf/` (package.json, vite, vitest, tsconfig.lib.json, README, biome). - [x] `src/types/` — page-size enum, dimensions table, margins, document / page / element types, colour types, unit type. One `.md` per `.ts`. - [x] `src/error-codes/` — `PDF_CODES` via `defineCodes`, `PdfError` class, `asPdfError` helper. - [x] `src/schemas/` — Zod schemas (`UnitSchema`, `HexColorSchema`, `RgbColorSchema`, `MarginsSchema`, `PageConfigSchema`, `DocumentMetadataSchema`) + `parseOrThrow` helper raising `PdfError`. - [x] `src/telemetry/` — six OTel instruments + `createPdfObservability` factory + recording helpers. - [x] `src/units/` — `toPoints`, `fromPoints`, `convert`, `parseUnit`, `formatUnit` raising typed errors. - [x] `src/colors/` — `hexToRgb`, `rgbToPdfColor`, `hexToPdfColor`, `rgbToHex`, `lighten`, `darken`, `COLORS`, `isTransparent`. - [x] Pre-publish gate: `bunx turbo lint typecheck test build --filter=@suaveplan/pdf` green, `bunx turbo test:coverage --filter=@suaveplan/pdf` 100 / 100 / 100 / 100. - [x] Refresh `MANIFEST.md`. ## Chunk B — Low-level writer Status: **SHIPPED** in this change. - [x] `src/pdf-writer/` — hand-rolled PDF 1.4 writer with: indirect-object table, font registry for the 14 standard fonts, stream-object assembly (raw + FlateDecode via `node:zlib`), xref + trailer, deterministic ordering, injectable `Clock` for `/CreationDate`, AbortSignal-aware `generate`, `dispose()` guard. - [x] `src/content-stream/` — encoder for `BT/ET`, `Td`/`T*`/`TL`/`Tm`, `Tj`/`Tf`, `q/Q`, `rg/RG`, `m/l/h/S/s/f/B`, `re`, `cm`, `w`, and image XObject `Do` placement with text-mode invariants. - [x] Real PDF parse verification via `pdf-parse@^2.4` round-trip (text content + page count + metadata + ProducerInfo). ## Chunk C — Elements Status: **SHIPPED** in this change. - [x] `src/text-element/` — `TextElement` class + `wrapText` + `resolveStandardFont` + `measureLineWidth` with alignment, max-lines overflow flag, kerning, `AbortSignal`. - [x] `src/table-element/` — `TableElement` with column-width auto-distribution, header repeat across pages via `renderPage(context, rowStart)`, alternating row backgrounds, border + column dividers, `LAYOUT_OVERFLOW` typed error. - [x] `src/image-element/` — `ImageElement` with PNG decode via `pngjs` (RGB + optional SMask alpha plane), JPEG pass-through via SOF-marker walk for width/height, `detectImageFormat`, `scaleToFit`, `validateImageProps`, declared-format mismatch detection. - [x] `src/divider-element/` — `DividerElement` with horizontal / vertical orientation, dash pattern, spacing above / below, `computeDividerSpacing` flow helper. - [x] Zod input schema per element type. - [x] Real pdf-parse round-trip per text + table element. - [x] Pre-publish gate: lint + typecheck + test + build green, coverage 100 / 100 / 100 / 100. ## Chunk D — Layout, styles, document builder, renderer Status: **SHIPPED** in this change. - [x] `src/style-resolver/` — `StyleResolver` cascade (`DEFAULT_STYLE` → theme → base → overrides), `mergeStyles`, `mergeTheme` deep merge incl. `headingSizes`, `normalizeSpacing` with `PDF.INVALID_ELEMENT` on non-finite input, `DEFAULT_THEME` palette. - [x] `src/layout-engine/` — `LayoutEngine` with cursor + content-area + header/footer slot rectangles, landscape orientation swap, `advance(cursor, height)` returning `{ fit, remaining }`, `nextPage`, `computeTextHeight`, `applyMargin`, AbortSignal propagation, typed `PDF.LAYOUT_OVERFLOW` / `PDF.INVALID_PAGE_CONFIG` errors. - [x] `src/document-builder/` — `DocumentBuilder` fluent API (`title` / `author` / `subject` / `keywords` / `creator` / `creationDate` / `pageConfig` / `withData` / `addText` / `addTable` / `addImage` / `addDivider` / `newPage` / `setHeader` / `setFooter` / `build`) + `createDocument()` factory. - [x] `src/image-registry/` — `ImageRegistry` + `registerImageXObject` that allocate per-element PDF XObjects (DCTDecode for JPEG, FlateDecode for PNG RGB plane) and share them across logical pages. - [x] `src/render-pipeline/` — `placeNode` / `renderTableSlice` / `renderSlot` / `pickFontResource` helpers operating on a `PipelineState` bundle. - [x] `src/renderer/` — top-level `renderDocument(doc, opts)` returning `{ bytes, pageCount, durationMs }`. AbortSignal-aware, emits six OTel instruments (documents-rendered counter, pages-emitted counter, render-errors counter tagged by code, validation-failures counter, render-duration histogram, document-size histogram). Page-break + table-continuation loop; raises `PDF.LAYOUT_OVERFLOW` for elements that cannot fit on a fresh page. - [x] Real pdf-parse round-trip tests covering text, multi-page flow, oversize-table pagination with header repeat, header / footer stamping across emitted pages, image XObject embedding. - [x] Pre-publish gate: `bunx turbo lint typecheck test build --filter=@suaveplan/pdf` green AND `bunx turbo test:coverage --filter=@suaveplan/pdf` 100 / 100 / 100 / 100. ## Chunk E — Templates Status: **SHIPPED** in this change. - [x] `src/templates/invoice-template/` — `createInvoiceTemplate` + `InvoiceDataSchema` + `computeInvoiceTotals` + `formatInvoiceAmount`. - [x] `src/templates/receipt-template/` — `createReceiptTemplate` + `ReceiptDataSchema` + `computeReceiptTotals` + `formatReceiptAmount`. - [x] `src/templates/report-template/` — `createReportTemplate` + `ReportDataSchema`. - [x] Zod input schema per template with `PDF.INVALID_TEMPLATE_INPUT` error path attaching the failing issue paths. - [x] Real pdf-parse round-trip per template. - [x] Pre-publish gate: `bunx turbo lint typecheck test build --filter=@suaveplan/pdf` green AND `bunx turbo test:coverage --filter=@suaveplan/pdf` 100 / 100 / 100 / 100. React PDF preview component — deferred to a follow-up change (`enterprise-react-pdf-preview` stub left under `openspec/changes/`). ## Cross-chunk invariants (preserved at every step) - One `.md` per `.ts` ≥ 200 words covering Purpose, Features, Usage: Basic, Usage: Advanced, API Reference, Implementation Notes. - 100 / 100 / 100 / 100 Istanbul coverage on every touched module — no `istanbul ignore` comments, no `// biome-ignore`. - Every `it` / `test` carries `{ timeout: 5000 }` (30 000 for async). - Tests import from `@suaveplan/testing/runner` per rule 11. - No `export *` in non-root barrels (rule 10).