Skip to content

pdf: render math via the editor's Unicode approximation - #67

Merged
devin-ai-integration[bot] merged 1 commit into
mainfrom
devin/1791315295-pdf-math
Oct 6, 2026
Merged

devin-ai-integration[bot] merged 1 commit into
mainfrom
devin/1791315295-pdf-math

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

PDF export ignored math — $…$/$$…$$ printed as raw LaTeX with the $ stripped (delimiters are concealed by md::Analysis, so the spans vanished but the source leaked through as plain text). This PR renders math in the PDF with the same Unicode approximation the editor already uses.

How: src/math.rs gains a pub(crate) pieces() API — the same walker as subs(), refactored into a shared scan(src, failed) that additionally reports every construct it could not read (unknown commands, scripts with no glyphs, malformed \frac args) as Piece { range, text: None }. No conversion tables were duplicated; subs()/approx() are unchanged for the editor.

src/pdf.rs now handles Analysis.maths in special()/special_start() — the same arm that already covers images, wiki-links and footnotes:

  • Inline $…$ → converted runs in an italic DejaVu face (math_style()).
  • $$…$$ with block: true → centered Paragraph on its own line (flow.element(…).aligned(Center)).
  • Piece { text: None } → the LaTeX source in mono (mono_style()) so \foo{n} or e^{i\pi} reads as source, not broken glyphs.
  • Money guards are untouched — R$ 10 e $ 20 still parses as plain prose (math_spans behavior unchanged).

Font coverage: every bundled Noto face misses ~half of the 416 characters math.rs can emit (all arrows, ∑ ≤ ∞ ∫, phonetic super/subscripts, double-struck). The exporter now embeds a pyftsubset-built DejaVu Sans + DejaVu Sans-Oblique subset covering ASCII, Latin-1/Ext-A, Greek, Cyrillic, General Punctuation and the whole math charset — +286 KB in the binary (DejaVuSans-math-subset.ttf 149 KB + DejaVuSans-Oblique-math-subset.ttf 137 KB), same ~286 KB per exported PDF since genpdf embeds the fonts it uses. License included (assets/fonts/DejaVu-LICENSE.txt, Bitstream Vera). Four emittable chars DejaVu lacks (⌢ ⌣ ⨆ ⨿) are listed in MATH_TOFU — a run containing them falls back to LaTeX source instead of showing .notdef boxes.

Tests: math_converts_to_unicode (inline + display through the real Emitter/inline path), unconvertible_math_shows_its_source, money_amounts_stay_literal, math_glyphs_reach_the_pdf (asserts the converted codepoints appear in the embedded font's ToUnicode CMap — i.e. real glyphs were drawn, not tofu).

Verified in the app: exported a note via the real "Export as PDF…" flow and opened the PDF in Preview — all glyphs render (α² ≤ ∞, xᵢ → ∑ᵢ, ∫₀¹, √(c), →), no tofu. PDFKit text extraction confirms:

Inline α² ≤ ∞ and xᵢ → ∑ᵢ xᵢ done.
∫₀¹ f(x) = (a)/(b) + √(c) + lim_n → ∞ n
Fallback keeps source: e^iπ and \foo{n}.
Money: R$ 10 e $ 20 literal.

The PDF (Preview):
PDF export rendered in Preview

Same note in the editor for comparison:
Editor rendering of the same note

Link to Devin session: https://app.devin.ai/sessions/a075cd854db947cdb802a60f4e413e85
Open in Devin Desktop: https://app.devin.ai/desktop/session/a075cd854db947cdb802a60f4e413e85?variant=devin
Requested by: @horizzon3507

$`$…$`$ and $$`…$$` spans in PDF export now use math.rs's
Unicode approximator through a new pub(crate) pieces() API that also
marks the constructs it cannot read. Converted math prints italic in a
DejaVu Sans subset (the Noto faces lack operators, arrows and
super/subscripts), $$ blocks center on their own line, and failed or
unsupported atoms fall back to their LaTeX source in mono so they read
as source rather than broken glyphs. Money guards stay literal.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot merged commit e9ee94f into main Oct 6, 2026
6 checks 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