Skip to content

[read-fonts] Add MATH table and supporting functions. - #2105

Merged
dfrg merged 2 commits into
mainfrom
tables/math
Sep 8, 2026
Merged

[read-fonts] Add MATH table and supporting functions.#2105
dfrg merged 2 commits into
mainfrom
tables/math

Conversation

@dfrg

@dfrg dfrg commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Broken into two commits for reviewer convenience. The first is just the codegen boilerplate. The second contains all the helper accessor methods. These were specifically designed to map nicely to the hb_ot_math_* functions because we'll need to implement those to replace HarfBuzz in Chrome. They should generalize to all consumers of this table though and the raw fields/methods are still there.

Plan for implementing on the HarfRust side: harfbuzz/harfrust#466

MATH describes mathematical typesetting: the constants that position
scripts, fractions and radicals, per-glyph italics correction, top accent
attachment and kerning, and the variants and assemblies that stretch a
delimiter to fit.

Chromium reads all four of those groups through HarfBuzz, so the surface
is not hypothetical.

Two parts of the table needed more than the usual attributes.
MathValueRecord carries a device offset measured from the table that
contains the record rather than from the record itself, which the
generated accessor already handles by taking the parent's offset data.
MathKern holds one more kern value than it has correction heights, since
the first applies below the lowest height and the last above the highest;
`add($height_count, 1)` gives that array its count.

The constants are 51 MathValueRecords in a fixed order, where a
transposition would be silent -- every field has the same type. The test
gives each record a value that encodes its index, so reading them back
through the named accessors checks the whole run.
@cmyr

cmyr commented Sep 8, 2026

Copy link
Copy Markdown
Member

does this put to rest (to my great shame) #1269 ?

@dfrg

dfrg commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

The first rule of #1269 is that we do not talk about #1269.

@cmyr cmyr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice and easy now that we've got records-in-tables...

Comment thread read-fonts/src/tables/math.rs Outdated

/// Which corner of a glyph a [`MathKern`] applies to.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum MathKernSide {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'side' seems like a funny word here, is it our choice or are we matching prior art?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair! renaming to MathKernCorner

}

impl Math<'_> {
/// Whether this is one of the Cambria Math builds that stores

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤢

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Most measurements in MATH are a MathValueRecord: a design unit value and
an optional offset to a Device or VariationIndex table. Reading one means
finding the record, resolving its offset against the table it was read
from, and applying the adjustment.

MathValue carries the result, and keeps its two halves apart because they
are in different units. A device table adjusts by whole pixels, so scaling
that into design units needs the units per em, which this table does not
know; returning a single number would have to guess at one. A
VariationIndex contributes nothing at all: the deltas it names live in an
item variation store, and MATH has none.

MathConstant numbers the constants the way HarfBuzz does. One macro list
defines the enum, its numbering, and the arms that read each field, so a
constant cannot be numbered as one field and read from another, and the
matches have no catch-all.

MathKern hands over its bands rather than searching them. Which band a
height falls in depends on whether the comparison happens in design units,
in pixels, or in a scaled position -- the last of which also decides which
way up the vertical axis is -- and only the caller knows that.

has_swapped_min_heights reports the Cambria Math builds that store two
constants the wrong way round. It reports the font and leaves the
substitution to the caller, so the constants keep saying what the font
says.
@dfrg
dfrg merged commit 9a4f247 into main Sep 8, 2026
23 checks passed
@dfrg
dfrg deleted the tables/math branch September 8, 2026 19:50
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.

2 participants