ADR-073: Open Sans Fonts, KPI Cards, and PDF Visual Polish
Status: Accepted Date: 2026-02-25
Context
The PDF report generated by pdf_report.py used Vera TTF (bundled with ReportLab) and
rendered sizing totals as a plain bullet list of bold-label + value pairs. Three problems
motivated this ADR:
- Vera is a technical font — it reads well in code editors but looks dated in a customer-facing pre-sales document. Pre-sales engineers present PDFs directly to CIOs and procurement stakeholders; the visual impression matters.
- Plain-text totals waste prime real estate — page 1 top-of-body is the highest-value area of the report. Six bullet lines convey the same six numbers that could fit in two compact rows of branded cards.
- Long storage strings wrap —
format_storagereturns"5284.0 GiB (5.2 TiB)"; at 17 pt in a 154 pt column the parenthetical wraps onto a second line, making KPI cards unusable without a compact formatter.
Additionally, the health findings summary table could land with its heading on one page and data on the next (orphan split), and the datastore-to-VM mapping used a floating bold paragraph with no visual container.
Decisions
D1 — Open Sans Light / SemiBold, with Vera fallback
Bundle OpenSansLight.ttf and OpenSansSemiBold.ttf (OFL) in src/store_predict/data/.
Register via _register_fonts() which resolves fonts relative to the installed package
__file__ path. If the .ttf files are absent (CI, stripped environments), the function
silently falls back to the Vera fonts already embedded in ReportLab.
Why Open Sans and not another font?
- License — Open Font License 1.1 — safe to bundle and distribute.
- Legibility — humanist sans-serif with excellent Latin and accented character coverage; well-suited to FR/EN bilingual reports.
- Weight pair — Light (300) for body reads cleanly at 9–10 pt; SemiBold (600) gives enough contrast for headings at 13 pt without the harsh weight of Bold.
- ReportLab compatibility — static TTF weight files work directly with
TTFont; no subsetting or variable-font toolchain needed.
D2 — KPI card strip for totals
Replace six plain Paragraph lines in the totals section with two Table rows styled
as brand-blue cards (_make_kpi_cards):
- Row 1: VMs / CPUs / Memory
- Row 2: Provisioned / In Use / Required
Each card shows a small light-blue label (8 pt) above a large white value (17 pt). A thin
vertical separator between cards (LINEAFTER) divides the strip visually.
Compact storage formatter — _fmt_kpi_storage(mib) returns a single-unit string
("5.2 TiB" or "238 GiB") for use inside KPI cards only; the full dual-unit
format_storage string is preserved everywhere else (averages section, table rows).
D3 — Page-number footer on every page
_draw_footer draws a #cccccc rule at y=18 and a centred grey page number at y=6,
called from both on_first_page and on_later_pages callbacks. The bottom margin
(20 mm) provides enough clearance.
D4 — Brand-blue HRFlowable after each section heading
A 1.5 pt HRFlowable in _BRAND_BLUE (#1e3a5f) immediately after each heading
paragraph creates a clear visual separator without the weight of a full box or card.
D5 — KeepTogether for health findings summary
The heading + severity count table are wrapped in KeepTogether([...]) to prevent the
Platypus layout engine from splitting them across pages. The block is small (≤ 6 rows)
so KeepTogether never causes a page to overflow.
D6 — Datastore→VM tables with spanning DS name header
Replace the Paragraph(f"<b>{ds.name}</b>") + plain Table pattern with a single
Table per datastore where row 0 is the DS name spanning all 3 columns (SPAN (0,0)
(-1,0)) with a light-blue (#d0e8f4) background and brand-blue bold text. This
creates an unambiguous visual container linking each datastore to its VMs.
D7 — Dell logo not auto-injected
The bundled dell_logo.png is no longer loaded at module level and applied as a default.
Dell branding is shown only when the caller explicitly passes dell_logo_bytes. This
prevents the Dell logo from appearing in white-label or partner-branded reports.
Alternatives Considered
- DejaVu fonts (bundled with ReportLab) — wider character set but heavier and less polished for print. Rejected in favour of Open Sans.
- Helvetica (built-in PDF font) — no TTF needed, but limited French character coverage without encoding tricks. Rejected.
- CSS-like card layout via nested tables — more complex; ReportLab
TableStyleachieves the same result with less overhead. Not applicable.
Consequences
- PDF file size increases by ~370 KB (two bundled TTF files), acceptable for a customer-facing deliverable.
pyproject.tomlpackage-data now includesdata/*.ttfto ensure fonts are installed with the package in Docker and editable installs._register_fonts()is called once at module import; subsequentgenerate_report_pdf()calls reuse the already-registered font names.- All 54 PDF tests continue to pass; the Vera fallback path ensures tests work without the bundled fonts if needed.