Skip to main content

math_alphanumeric_base

Function math_alphanumeric_base 

Source
pub fn math_alphanumeric_base(c: char) -> Option<char>
Expand description

The unstyled letter a Mathematical Alphanumeric Symbol stands for: Unicode’s own <font> compatibility decomposition, for the whole U+1D400..=U+1D7FF block plus every <font> decomposition in the Letterlike Symbols block U+2100..U+214F. None for anything else.

Why this exists. default_math_variant_char is the FORWARD direction — plain letter to styled codepoint — and it is what makes a ${x} come out as 𝑥 U+1D465. Fonts, however, cover this block very unevenly, and a codepoint the chosen face has no cmap entry for is emitted as gid 0 (.notdef). What that LOOKS like depends on the face and is never what the author asked for: a TrueType face draws a tofu box, and a CFF/OTF face — including latinmodern-math.otf, this port’s own default math font — usually has an EMPTY .notdef, so the character occupies its advance and paints nothing at all. That is the whole of “some glyphs are not drawn in PDF mode”: no error, no warning, just absent letters.

What the bundled faces actually cover, measured off their cmaps rather than assumed, because the argument for this function rests on it: latinmodern-math.otf covers every ASSIGNED codepoint of U+1D400..=U+1D7FF except the two script LOWERCASE runs (U+1D4B6..=U+1D4CF and U+1D4EA..=U+1D503, plus the Letterlike ℯ ℊ ℴ that fill their holes) and the two bold digammas U+1D7CA/U+1D7CB — 51 codepoints in all. Its Fraktur, Double-struck, Greek, digit, sans-serif, typewriter and script-CAPITAL runs are complete. DejaVuMathTeXGyre.ttf lacks only the two digammas. The bundled TEXT faces (Junicode, IPAex) cover none of the block at all, which is the configuration that actually bites: a document with an uploaded text font and no math font (the playground, and --font) has every \pi from math.satyh land on .notdef.

So this is the INVERSE direction, used only as a last resort by the math layout path (primitives::push_char_glyph) when neither the math font nor the text font covers the styled codepoint. Falling back to π for a 𝜋 no font in the document can draw loses the italic styling and keeps the mathematics; falling back to .notdef loses both. This continues the port’s existing metrics-probe policy — primitives::resolve_variant_char already declines the forward remap when the target is uncoverable — rather than introducing a new one; it just reaches the cases that policy cannot, because math.satyh hands those codepoints over ALREADY styled (greek-lowercase 0x1D70B 0x1D745 for \pi) and there is no plain letter left to decline back to.

Provenance. The whole table was diffed against Unicode 14.0’s own <font> decompositions (Python unicodedata.decomposition) over all of U+0000..U+10FFFF: zero disagreements on any codepoint both sides map. What the tests below pin is that same data, transcribed run by run (every_alphabetic_run_decomposes_to_a_z_a_z, every_greek_run_…, every_digit_run_…, letterlike_table_is_exactly_unicodes_font_set), so a re-derivation is a diff against those literals rather than a fresh audit.

Three <font> groups outside the two blocks above are DELIBERATELY left out, not missed: the Hebrew presentation forms U+FB20..U+FB29, the Arabic Mathematical Alphabetic Symbols U+1EE00.., and the segmented digits U+1FBF0..U+1FBF9. The first and third are not mathematics this port can receive from default_math_variant_char; the second is, but its base letters are Arabic, which no bundled face covers either — so the substitute-is-itself-covered guard would decline all 143 of them anyway.

A DELIBERATE DIVERGENCE from upstream, and the argument that it is safe. SATySFi v0.0.6 has no counterpart to this function: its fontInfo.ml:180-187 get_glyph_id warns (Logging.warn_no_glyph) and returns FontFormat.notdef, full stop. This port takes the warning (cid::report_missing_glyphs) AND substitutes, and the two halves are separable on purpose — the warning is fidelity, the substitution is not.

It is safe because the substitution’s precondition is exactly upstream’s None branch: primitives::degrade_unrenderable_variant fires only when neither the math font nor the text font can draw the codepoint, i.e. only on inputs for which upstream’s answer is notdef. Any document whose glyphs all resolve is untouched, byte for byte. Where it does fire it replaces a notdef — a tofu box on a TrueType face, and NOTHING AT ALL on a CFF one — with the right letter in the wrong style, which is the better of the two wrong answers available and the only one an author can see. The price is that the ToUnicode CMap then carries the base letter rather than the styled codepoint; that is the same trade, since the styled codepoint in ToUnicode was previously the only trace the character had left, and it made pdftotext report a character the page did not show.