HomeWorkAboutContact

Case · Backend · Rendering

Receipt Renderer

Receipts rendered from structured JSON to matching PDF, PNG, and SVG output.

Role
Sole engineer
Year
2026
Status
Shipped
Stack
.NET 10 · SkiaSharp · QRCoder
Problem

Receipts need exact totals and matching output across PDF, PNG, and SVG.

My role

Sole engineer

Result

Shipped renderer with a shared core, golden-image tests, and architecture notes.

01The problem

Receipts sound simple until totals, taxes, logos, fonts, and different output formats all have to match. I built a renderer that keeps the math exact and the visual output stable across servers.

02What I built

  • Validate the JSON, render once with SkiaSharp, then write the same receipt through PDF, PNG, and SVG backends.
  • Expose the same rendering core through a CLI, an HTTP API, and a Telegram bot, with a Flutter macOS demo on top.

03Key decisions

Render once, write many

The renderer builds one receipt layout, then sends it through PDF, PNG, and SVG backends so the formats stay aligned.

Decimal money

Amounts use `decimal` with explicit away-from-zero rounding from input parsing to the rendered total. No floating point is used for currency.

Self-contained rendering

Logos and fonts are embedded rather than fetched at render time, which avoids SSRF risk and keeps output reproducible.

04Checks and tests

  • 184 tests on macOS (186 in Linux CI), plus 30 Flutter widget tests on the demo app.
  • Golden-image tests run on Linux in CI so visual drift is caught during review.
  • Time comes from an `IClock`, which keeps generated receipts deterministic.
  • Compiler warnings fail the build, and four ADRs document the main trade-offs.

05Code sample

csharpMoney stays decimal until it is rendered
decimal RoundMoney(decimal amount)
{
    return Math.Round(amount, 2, MidpointRounding.AwayFromZero);
}

06Trade-offs

  • Embedding fonts and logos reduces runtime flexibility, but it makes output reproducible and removes remote fetch risk.
  • The renderer prioritises deterministic layout over browser-style flowing HTML.

07Result

A small rendering toolkit with the boring parts handled properly: exact money, stable output, and architecture notes explaining the main decisions.

08What I would improve next

  • Add more receipt templates without changing the money and rendering contracts.
  • Add a visual diff review page for template changes.