feat(invoice): add Noto Sans Thai fallback font for Thai glyphs
Summary
Thai characters in invoice and receipt PDFs now render with Noto Sans Thai instead of showing up blank.
What
- Adds
NotoSansThai-Regular.ttfandNotoSansThai-Bold.ttf(~38 KB each, from the official notofonts repository) toserver/polar/invoice/fonts/. - Registers Thai in
InvoiceGeneratoras an always-loaded fallback font, the same way Hebrew and Arabic are.ReceiptGeneratorbuilds onInvoiceGenerator, so receipts get it too. - Extends
test_generator_registers_unicode_fallback_fontsto check thatเธresolves to the Thai font in regular and bold.
Why
Neither PP Neue Montreal nor the existing Noto fallbacks include the Thai block (U+0E00โU+0E7F), so Thai customer names, addresses and line items rendered as empty space.
How
The fonts are small, so they're committed next to the Hebrew and Arabic fonts. They aren't downloaded at Docker build time like the CJK fonts (which are tens of MB each), so dev, test and production all have them without changes to the Dockerfile.
fpdf2 runs without a text shaper here, so I rendered a sample invoice to check that stacked vowel and tone marks display correctly. The sample has a Thai customer name in bold and a Thai line item with Thai digits:

The image is hosted in commit 7ad13f8 on this branch, and the next commit removes it, so it's not part of the diff.
Checklist
- This PR addresses a single concern (one bug fix, one feature, one refactor)
- The diff is reasonably sized and easy to review
- New functionality is covered by tests
- Linting and type checking pass (
uv run task lint && uv run task lint_types): ruff lint and format are clean, and thetests/invoiceandtests/receiptsuites pass (109 passed, 4 skipped). mypy was not run locally. - No unrelated changes or drive-by fixes are included
๐ค Generated with Claude Code
https://claude.ai/code/session_01TJbggMDA6pDQESPPFpoHB8
Generated by Claude Code