Skip to content

Swap PDF reports to DejaVu Sans (fix Unicode black squares) - #36

Merged
eagnespuerto merged 1 commit into
mainfrom
major-update-microlensing
Aug 12, 2026
Merged

Swap PDF reports to DejaVu Sans (fix Unicode black squares)#36
eagnespuerto merged 1 commit into
mainfrom
major-update-microlensing

Conversation

@eagnespuerto

Copy link
Copy Markdown
Owner

Both the transit report (report.py) and the microlensing report (microlensing_report.py) were rendering Greek letters, superscripts, and astronomy symbols (θ, Δ, μ, ², °, ⊕, ±, ≈, ≥, …) as black squares because Helvetica's built-in WinAnsi subset doesn't cover them. This landed hardest on the microlensing PDF, whose observables + planet-predictions tables use θ_E, μ_rel, M⊕/M♃, ΔBIC, and per-parameter ± error notation on almost every row.

Fix: register DejaVu Sans (regular + bold) as reportlab TrueType fonts on module import, and swap the hardcoded "Helvetica" / "Helvetica-Bold" strings for the resolved font names. DejaVu ships bundled with matplotlib (already a Vetstar dep) so no new install is needed and no system-font path assumption creeps in — production containers on Render and Fly.io will have it automatically.

Changes

  • New backend/app/pdf_fonts.py: ensure_dejavu_registered() + font_normal() / font_bold(). Idempotent. Looks up matplotlib.get_data_path()/fonts/ttf/DejaVuSans{,-Bold}.ttf, registers via reportlab.pdfbase.pdfmetrics, and binds a font family so Paragraph inline ... markup uses the bold face. Silent no-op if matplotlib/DejaVu aren't available (falls back to Helvetica with a warning — same behaviour as before, no regression).
  • backend/app/report.py: import + apply FONT_NORMAL / FONT_BOLD across every TableStyle "FONTNAME" cmd, canvas.setFont, and _build_styles() ParagraphStyle (which previously inherited Helvetica from getSampleStyleSheet's Heading1/BodyText — that inheritance is now explicitly overridden per style).
  • backend/app/microlensing_report.py: same swap, plus ml_small ParagraphStyle now sets fontName explicitly (was inheriting from Normal → Helvetica).

Verified via pypdf text extraction: Greek + symbol glyphs that appear in the microlensing PDF (θ, Δ, σ, °, ⊕, ±, ≈, μ, ≥) now round-trip correctly through the DejaVuSans font stream — no black squares. PDF font resource dictionary shows /AAAAAA+DejaVuSans and /AAAAAA+DejaVuSans-Bold (subsetted embeddings) alongside the inescapable base14 /Helvetica reportlab always writes.

Tests: 45/45 microlensing tests still pass.

Both the transit report (report.py) and the microlensing report
(microlensing_report.py) were rendering Greek letters, superscripts,
and astronomy symbols (θ, Δ, μ, ², °, ⊕, ±, ≈, ≥, …) as black
squares because Helvetica's built-in WinAnsi subset doesn't cover them.
This landed hardest on the microlensing PDF, whose observables +
planet-predictions tables use θ_E, μ_rel, M⊕/M♃, ΔBIC, and
per-parameter ± error notation on almost every row.

Fix: register DejaVu Sans (regular + bold) as reportlab TrueType fonts
on module import, and swap the hardcoded "Helvetica" / "Helvetica-Bold"
strings for the resolved font names. DejaVu ships bundled with
matplotlib (already a Vetstar dep) so no new install is needed and no
system-font path assumption creeps in — production containers on Render
and Fly.io will have it automatically.

Changes
- New backend/app/pdf_fonts.py: `ensure_dejavu_registered()` +
  `font_normal()` / `font_bold()`. Idempotent. Looks up
  matplotlib.get_data_path()/fonts/ttf/DejaVuSans{,-Bold}.ttf,
  registers via reportlab.pdfbase.pdfmetrics, and binds a font family
  so Paragraph inline <b>...</b> markup uses the bold face. Silent
  no-op if matplotlib/DejaVu aren't available (falls back to Helvetica
  with a warning — same behaviour as before, no regression).
- backend/app/report.py: import + apply FONT_NORMAL / FONT_BOLD across
  every TableStyle "FONTNAME" cmd, canvas.setFont, and _build_styles()
  ParagraphStyle (which previously inherited Helvetica from
  getSampleStyleSheet's Heading1/BodyText — that inheritance is now
  explicitly overridden per style).
- backend/app/microlensing_report.py: same swap, plus ml_small
  ParagraphStyle now sets fontName explicitly (was inheriting from
  Normal → Helvetica).

Verified via pypdf text extraction: Greek + symbol glyphs that appear
in the microlensing PDF (θ, Δ, σ, °, ⊕, ±, ≈, μ, ≥) now round-trip
correctly through the DejaVuSans font stream — no black squares.
PDF font resource dictionary shows /AAAAAA+DejaVuSans and
/AAAAAA+DejaVuSans-Bold (subsetted embeddings) alongside the
inescapable base14 /Helvetica reportlab always writes.

Tests: 45/45 microlensing tests still pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@eagnespuerto
eagnespuerto merged commit 8978ba2 into main Aug 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant