Orpheus Systems Architecture

4. Client-side PDF generation with pdfmake

4. Client-side PDF generation with pdfmake

Date: 2026-10-22

Status

Accepted


Problem

The timesheet is a one-page landscape A4 document (one page per 12 months) with a header of two label rows, a month × day grid (columns 1–31) whose cells show hours (8.00 / 4.00) with blue on-site, yellow off-site and grey blocked fills, per-month and grand totals, and three signature boxes. It must match the reference layout exactly. The solution has no server component.


Decision

We will generate the PDF in the browser with pdfmake inside shared-ui/pdf, chosen for table layout control and because it avoids canvas-to-image blurring. jsPDF + autotable is the recorded fallback if bundle size exceeds SPFx guidance. Output is downloaded as Timesheet_<ContractCode>_<from>_<to>.pdf; period defaults to the contract span and can be trimmed. Saving to a document library is a documented extension point, not built now.

Browser PDF pipeline in the lazy chunk

flowchart TB
  DATA["IDataService<br/>contract, entries,<br/>holidays"] --> WP
  U["User downloads<br/>the timesheet"] --> WP
  WP["Calendar web part<br/>main bundle"] -->|"dynamic import<br/>on demand"| M
  subgraph CH["Lazy chunk"]
    M["orpheus-timesheet-pdf<br/>shared-ui/pdf"]
    MB["Timesheet model builder<br/>pure logic, unit-tested"]
    DD["pdfmake doc definition<br/>A4 landscape<br/>month x day grid, totals,<br/>three signature boxes"]
    PM["pdfmake in the browser<br/>vector text, no canvas"]
    M --> MB --> DD --> PM
  end
  PM --> F["Download<br/>Timesheet_ContractCode<br/>_from_to.pdf<br/>period: contract span,<br/>can be trimmed"]
  PM -.->|"recorded fallback<br/>if bundle too large"| JS["jsPDF + autotable"]
  F -.->|"extension point,<br/>not built"| LIB["Save to a<br/>document library"]

Considered Options

Option Summary Pros Cons
✅ pdfmake Declarative document model with table support Precise table layout; vector text; no server Bundle size
jsPDF + autotable Imperative PDF with table plugin Smaller bundle Less layout control; fallback only
HTML → canvas → image → PDF Render DOM, rasterise Easy to match the on-screen look Blurry output; large files
Server-side generation (Azure Function, Power Automate) Build PDF outside the browser Could also store the file Introduces hosting, auth and cost; out of scope

Consequences

Positive:

Negative / Trade-offs:

Risks: | Risk ID | Description | Impact (E/H/M/L) | Mitigation | |---------|-------------|------------------|------------| | R1 | pdfmake pushes the web part bundle over SPFx guidance | M | Switch to jsPDF + autotable; supersede this ADR | | R2 | Layout drifts from the reference image (not committed) | M | Design §6.5 is the full description; verify against it at gate G-5 |