diff --git a/Lib/ufo2ft/constants.py b/Lib/ufo2ft/constants.py
index 696839fc..eed6f3a1 100644
--- a/Lib/ufo2ft/constants.py
+++ b/Lib/ufo2ft/constants.py
@@ -43,6 +43,11 @@ class CFFOptimization(IntEnum):
# ]
COLR_CLIP_BOXES_KEY = UFO2FT_PREFIX + "colrClipBoxes"
+GLYPHS_MATH_PREFIX = "com.nagwa.MATHPlugin."
+GLYPHS_MATH_CONSTANTS_KEY = GLYPHS_MATH_PREFIX + "constants"
+GLYPHS_MATH_VARIANTS_KEY = GLYPHS_MATH_PREFIX + "variants"
+GLYPHS_MATH_EXTENDED_SHAPE_KEY = GLYPHS_MATH_PREFIX + "extendedShape"
+
OBJECT_LIBS_KEY = "public.objectLibs"
OPENTYPE_CATEGORIES_KEY = "public.openTypeCategories"
OPENTYPE_META_KEY = "public.openTypeMeta"
diff --git a/Lib/ufo2ft/outlineCompiler.py b/Lib/ufo2ft/outlineCompiler.py
index 8bd2fffd..8ae12f39 100644
--- a/Lib/ufo2ft/outlineCompiler.py
+++ b/Lib/ufo2ft/outlineCompiler.py
@@ -30,6 +30,10 @@
COLOR_LAYERS_KEY,
COLOR_PALETTES_KEY,
COLR_CLIP_BOXES_KEY,
+ GLYPHS_MATH_CONSTANTS_KEY,
+ GLYPHS_MATH_EXTENDED_SHAPE_KEY,
+ GLYPHS_MATH_PREFIX,
+ GLYPHS_MATH_VARIANTS_KEY,
OPENTYPE_META_KEY,
OPENTYPE_POST_UNDERLINE_POSITION_KEY,
UNICODE_VARIATION_SEQUENCES_KEY,
@@ -94,6 +98,7 @@ class BaseOutlineCompiler:
"vhea",
"COLR",
"CPAL",
+ "MATH",
"meta",
]
)
@@ -175,6 +180,8 @@ def compile(self):
self.setupTable_CPAL()
if self.meta:
self.setupTable_meta()
+ if any(key.startswith(GLYPHS_MATH_PREFIX) for key in self.ufo.lib):
+ self.setupTable_MATH()
self.setupOtherTables()
if self.colorLayers and self.colrAutoClipBoxes:
self._computeCOLRClipBoxes()
@@ -1050,6 +1057,181 @@ def setupTable_meta(self):
f"public.openTypeMeta '{key}' value should be bytes or a string."
)
+ def _bboxWidth(self, glyph):
+ bbox = self.glyphBoundingBoxes[glyph]
+ if bbox is None:
+ return 0
+ return bbox.xMax - bbox.xMin
+
+ def _bboxHeight(self, glyph):
+ bbox = self.glyphBoundingBoxes[glyph]
+ if bbox is None:
+ return 0
+ return bbox.yMax - bbox.yMin
+
+ def setupTable_MATH(self):
+ """
+ This builds MATH table based on data in the UFO font. The data is stored
+ either in private font/glyph lib keys or as glyph anchors with specific
+ names.
+
+ The data is based on GlyphsApp MATH plugin data as written out by
+ glyphsLib to the UFO font.
+
+ The font lib keys are:
+ - com.nagwa.MATHPlugin.constants: a dictionary of MATH constants as
+ expected by fontTools.otlLib.builder.buildMathTable(). Example:
+
+ ufo.lib["com.nagwa.MATHPlugin.constants"] = {
+ "ScriptPercentScaleDown": 70,
+ "ScriptScriptPercentScaleDown": 60,
+ ...
+ }
+
+ - com.nagwa.MATHPlugin.extendedShape: a list of glyph names that
+ are extended shapes. Example:
+
+ ufo.lib["com.nagwa.MATHPlugin.extendedShapes"] = ["integral", "radical"]
+
+ The glyph lib keys are:
+ - com.nagwa.MATHPlugin.variants: a dictionary of MATH glyph variants
+ keyed by glyph names, and each value is a dictionary with keys
+ "hVariants", "vVariants", "hAssembly", and "vAssembly". Example:
+
+ ufo["braceleft"].lib["com.nagwa.MATHPlugin.variants"] = {
+ "vVariants": ["braceleft", "braceleft.s1", "braceleft.s2"],
+ "vAssembly": [
+ # glyph name, flags, start connector length, end connector length
+ ["braceleft.bottom", 0, 0, 200],
+ ["braceleft.extender", 1, 200, 200],
+ ["braceleft.middle", 0, 100, 100],
+ ["braceleft.extender", 1, 200, 200],
+ ["braceleft.top", 0, 200, 0],
+ ],
+ }
+
+ The anchors are:
+ - math.ic: italic correction anchor
+ - math.ta: top accent attachment anchor
+ - math.tr*: top right kerning anchors
+ - math.tl*: top left kerning anchors
+ - math.br*: bottom right kerning anchors
+ - math.bl*: bottom left kerning anchors
+ """
+ if "MATH" not in self.tables:
+ return
+
+ from fontTools.otlLib.builder import buildMathTable
+
+ ufo = self.ufo
+ constants = ufo.lib.get(GLYPHS_MATH_CONSTANTS_KEY)
+ min_connector_overlap = constants.pop("MinConnectorOverlap", 0)
+
+ italics_correction = {}
+ top_accent_attachment = {}
+ math_kerns = {}
+ kerning_sides = {
+ "tr": "TopRight",
+ "tl": "TopLeft",
+ "br": "BottomRight",
+ "bl": "BottomLeft",
+ }
+ for name, glyph in self.allGlyphs.items():
+ kerns = {}
+ for anchor in glyph.anchors:
+ if anchor.name == "math.ic":
+ # The anchor x position is absolute, but we want
+ # a value relative to the glyph's width.
+ italics_correction[name] = anchor.x - glyph.width
+ if anchor.name == "math.ta":
+ top_accent_attachment[name] = anchor.x
+ for aName in kerning_sides.keys():
+ if anchor.name.startswith(f"math.{aName}"):
+ side = kerning_sides[aName]
+ # The anchor x positions are absolute, but we want
+ # values relative to the glyph's width/origin.
+ x, y = anchor.x, anchor.y
+ if side.endswith("Right"):
+ x -= glyph.width
+ elif side.endswith("Left"):
+ x = -x
+ kerns.setdefault(side, []).append([x, y])
+ if kerns:
+ math_kerns[name] = {}
+ # Convert anchor positions to correction heights and kern
+ # values.
+ for side, pts in kerns.items():
+ pts = sorted(pts, key=lambda pt: pt[1])
+ # Y positions, the last one is ignored as the last kern
+ # value is applied to all heights greater than the last one.
+ correctionHeights = [pt[1] for pt in pts[:-1]]
+ # X positions
+ kernValues = [pt[0] for pt in pts]
+ math_kerns[name][side] = (correctionHeights, kernValues)
+
+ # buildMathTable takes two dictionaries of glyph variants, one for
+ # horizontal variants and one for vertical variants, and items are
+ # tuples of glyph name and the advance width/height of the variant.
+ # Here we convert the UFO data to the expected format and measure the
+ # advances.
+ h_variants = {}
+ v_variants = {}
+ # It also takes two dictionaries of glyph assemblies, one for
+ # horizontal assemblies and one for vertical assemblies, and items are
+ # lists of tuples of assembly parts and italics correction, and the
+ # assembly part includes the advance width/height of the part. Here we
+ # convert the UFO data to the expected format and measure the advances.
+ h_assemblies = {}
+ v_assemblies = {}
+ for name, glyph in self.allGlyphs.items():
+ if GLYPHS_MATH_VARIANTS_KEY in glyph.lib:
+ variants = glyph.lib[GLYPHS_MATH_VARIANTS_KEY]
+ if "hVariants" in variants:
+ h_variants[name] = [
+ (n, self._bboxWidth(n)) for n in variants["hVariants"]
+ ]
+ if "vVariants" in variants:
+ v_variants[name] = [
+ (n, self._bboxHeight(n)) for n in variants["vVariants"]
+ ]
+ if "hAssembly" in variants:
+ parts = variants["hAssembly"]
+ h_assemblies[name] = (
+ [(*part, self._bboxWidth(part[0])) for part in parts],
+ # If the last part has italic correction, we use it as
+ # the assembly's.
+ italics_correction.pop(parts[-1][0], 0),
+ )
+ if "vAssembly" in variants:
+ parts = variants["vAssembly"]
+ v_assemblies[name] = (
+ [(*part, self._bboxHeight(part[0])) for part in parts],
+ # If the last part has italic correction, we use it as
+ # the assembly's.
+ italics_correction.pop(parts[-1][0], 0),
+ )
+
+ # Collect the set of extended shapes, and if a shape has vertical
+ # variants, add the variants to the set.
+ extended_shapes = set(ufo.lib.get(GLYPHS_MATH_EXTENDED_SHAPE_KEY, []))
+ for name, variants in v_variants.items():
+ if name in extended_shapes:
+ extended_shapes.update(v[0] for v in variants)
+
+ buildMathTable(
+ self.otf,
+ constants=constants,
+ italicsCorrections=italics_correction,
+ topAccentAttachments=top_accent_attachment,
+ extendedShapes=extended_shapes,
+ mathKerns=math_kerns,
+ minConnectorOverlap=min_connector_overlap,
+ vertGlyphVariants=v_variants,
+ horizGlyphVariants=h_variants,
+ vertGlyphAssembly=v_assemblies,
+ horizGlyphAssembly=h_assemblies,
+ )
+
def setupOtherTables(self):
"""
Make the other tables. The default implementation does nothing.
@@ -1652,6 +1834,7 @@ def __init__(
self.draw = self._drawDefaultNotdef
self.drawPoints = self._drawDefaultNotdefPoints
self.reverseContour = reverseContour
+ self.lib = {}
def __len__(self):
if self.name == ".notdef":
diff --git a/requirements.txt b/requirements.txt
index aed05ff6..4ddd37a5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-fonttools[ufo,lxml]==4.48.1
+fonttools[ufo,lxml]==4.49.0
defcon==0.10.3
compreffor==0.5.5
booleanOperations==0.9.0
diff --git a/setup.py b/setup.py
index 762d06f9..e819092c 100644
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@
setup_requires=pytest_runner + wheel + ["setuptools_scm"],
tests_require=["pytest>=2.8"],
install_requires=[
- "fonttools[ufo]>=4.47.2",
+ "fonttools[ufo]>=4.49.0",
"cffsubr>=0.3.0",
"booleanOperations>=0.9.0",
],
diff --git a/tests/data/TestMathFont-Regular.ufo/features.fea b/tests/data/TestMathFont-Regular.ufo/features.fea
new file mode 100644
index 00000000..1dafe8fd
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/features.fea
@@ -0,0 +1,4 @@
+# Prefix: Languagesystems
+languagesystem DFLT dflt;
+languagesystem math dflt;
+
diff --git a/tests/data/TestMathFont-Regular.ufo/fontinfo.plist b/tests/data/TestMathFont-Regular.ufo/fontinfo.plist
new file mode 100644
index 00000000..b0b9f7a9
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/fontinfo.plist
@@ -0,0 +1,46 @@
+
+
+
+
+ ascender
+ 800
+ capHeight
+ 656
+ descender
+ -200
+ familyName
+ Test Math Font
+ italicAngle
+ 0
+ openTypeHeadCreated
+ 2024/02/12 18:20:25
+ openTypeOS2Type
+
+ 3
+
+ postscriptBlueValues
+
+ -18
+ 0
+
+ postscriptOtherBlues
+
+ -201
+ -200
+
+ postscriptUnderlinePosition
+ -150
+ postscriptUnderlineThickness
+ 50
+ styleName
+ Regular
+ unitsPerEm
+ 1000
+ versionMajor
+ 1
+ versionMinor
+ 0
+ xHeight
+ 449
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/contents.plist b/tests/data/TestMathFont-Regular.ufo/glyphs/contents.plist
new file mode 100644
index 00000000..455616bb
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/contents.plist
@@ -0,0 +1,38 @@
+
+
+
+
+ parenleft
+ parenleft.glif
+ parenleft.bot
+ parenleft.bot.glif
+ parenleft.ext
+ parenleft.ext.glif
+ parenleft.size1
+ parenleft.size1.glif
+ parenleft.size2
+ parenleft.size2.glif
+ parenleft.size3
+ parenleft.size3.glif
+ parenleft.size4
+ parenleft.size4.glif
+ parenleft.top
+ parenleft.top.glif
+ parenright
+ parenright.glif
+ parenright.bot
+ parenright.bot.glif
+ parenright.ext
+ parenright.ext.glif
+ parenright.size1
+ parenright.size1.glif
+ parenright.size2
+ parenright.size2.glif
+ parenright.size3
+ parenright.size3.glif
+ parenright.size4
+ parenright.size4.glif
+ parenright.top
+ parenright.top.glif
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.bot.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.bot.glif
new file mode 100644
index 00000000..e1c7506b
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.bot.glif
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.ext.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.ext.glif
new file mode 100644
index 00000000..2ed6d70d
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.ext.glif
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.glif
new file mode 100644
index 00000000..b7545149
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.glif
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ com.nagwa.MATHPlugin.variants
+
+ vAssembly
+
+
+ parenleft.bot
+ 0
+ 0
+ 314
+
+
+ parenleft.ext
+ 1
+ 630
+ 630
+
+
+ parenleft.top
+ 0
+ 314
+ 0
+
+
+ vVariants
+
+ parenleft
+ parenleft.size1
+ parenleft.size2
+ parenleft.size3
+ parenleft.size4
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size1.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size1.glif
new file mode 100644
index 00000000..6d0b1d7b
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size1.glif
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size2.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size2.glif
new file mode 100644
index 00000000..a40544a0
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size2.glif
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size3.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size3.glif
new file mode 100644
index 00000000..8ef2734f
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size3.glif
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size4.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size4.glif
new file mode 100644
index 00000000..ff2c36b1
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.size4.glif
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.top.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.top.glif
new file mode 100644
index 00000000..faed22d9
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenleft.top.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.bot.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.bot.glif
new file mode 100644
index 00000000..f142d880
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.bot.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.ext.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.ext.glif
new file mode 100644
index 00000000..dfa357dc
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.ext.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.glif
new file mode 100644
index 00000000..04ce4166
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.glif
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+ com.nagwa.MATHPlugin.variants
+
+ vAssembly
+
+
+ parenright.bot
+ 0
+ 0
+ 314
+
+
+ parenright.ext
+ 1
+ 630
+ 630
+
+
+ parenright.top
+ 0
+ 314
+ 0
+
+
+ vVariants
+
+ parenright
+ parenright.size1
+ parenright.size2
+ parenright.size3
+ parenright.size4
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size1.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size1.glif
new file mode 100644
index 00000000..df14053e
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size1.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size2.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size2.glif
new file mode 100644
index 00000000..7993e2ad
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size2.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size3.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size3.glif
new file mode 100644
index 00000000..6db19801
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size3.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size4.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size4.glif
new file mode 100644
index 00000000..a481dcdc
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.size4.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.top.glif b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.top.glif
new file mode 100644
index 00000000..29212ee3
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/glyphs/parenright.top.glif
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/layercontents.plist b/tests/data/TestMathFont-Regular.ufo/layercontents.plist
new file mode 100644
index 00000000..b9c1a4f2
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+
+
+
+
+
+ public.default
+ glyphs
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/lib.plist b/tests/data/TestMathFont-Regular.ufo/lib.plist
new file mode 100644
index 00000000..74ccf15b
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/lib.plist
@@ -0,0 +1,147 @@
+
+
+
+
+ com.github.googlei18n.ufo2ft.useProductionNames
+
+ com.nagwa.MATHPlugin.constants
+
+ AccentBaseHeight
+ 449
+ AxisHeight
+ 250
+ DelimitedSubFormulaMinHeight
+ 1010
+ DisplayOperatorMinHeight
+ 2260
+ FlattenedAccentBaseHeight
+ 656
+ FractionDenomDisplayStyleGapMin
+ 137
+ FractionDenominatorDisplayStyleShiftDown
+ 685
+ FractionDenominatorGapMin
+ 45
+ FractionDenominatorShiftDown
+ 345
+ FractionNumDisplayStyleGapMin
+ 137
+ FractionNumeratorDisplayStyleShiftUp
+ 677
+ FractionNumeratorGapMin
+ 45
+ FractionNumeratorShiftUp
+ 394
+ FractionRuleThickness
+ 45
+ LowerLimitBaselineDropMin
+ 600
+ LowerLimitGapMin
+ 166
+ MinConnectorOverlap
+ 20
+ OverbarExtraAscender
+ 45
+ OverbarRuleThickness
+ 45
+ OverbarVerticalGap
+ 137
+ RadicalDegreeBottomRaisePercent
+ 60
+ RadicalDisplayStyleVerticalGap
+ 158
+ RadicalExtraAscender
+ 70
+ RadicalKernAfterDegree
+ -400
+ RadicalKernBeforeDegree
+ 277
+ RadicalRuleThickness
+ 45
+ RadicalVerticalGap
+ 57
+ ScriptPercentScaleDown
+ 70
+ ScriptScriptPercentScaleDown
+ 50
+ SkewedFractionHorizontalGap
+ 400
+ SkewedFractionVerticalGap
+ 60
+ SpaceAfterScript
+ 50
+ StackBottomDisplayStyleShiftDown
+ 685
+ StackBottomShiftDown
+ 345
+ StackDisplayStyleGapMin
+ 321
+ StackGapMin
+ 137
+ StackTopDisplayStyleShiftUp
+ 677
+ StackTopShiftUp
+ 444
+ StretchStackBottomShiftDown
+ 600
+ StretchStackGapAboveMin
+ 111
+ StretchStackGapBelowMin
+ 166
+ StretchStackTopShiftUp
+ 199
+ SubSuperscriptGapMin
+ 183
+ SubscriptBaselineDropMin
+ 50
+ SubscriptShiftDown
+ 149
+ SubscriptTopMax
+ 359
+ SuperscriptBaselineDropMax
+ 385
+ SuperscriptBottomMaxWithSubscript
+ 359
+ SuperscriptBottomMin
+ 112
+ SuperscriptShiftUp
+ 362
+ SuperscriptShiftUpCramped
+ 289
+ UnderbarExtraDescender
+ 45
+ UnderbarRuleThickness
+ 45
+ UnderbarVerticalGap
+ 137
+ UpperLimitBaselineRiseMin
+ 199
+ UpperLimitGapMin
+ 111
+
+ com.nagwa.MATHPlugin.extendedShape
+
+ parenleft
+ parenright
+
+ public.glyphOrder
+
+ parenleft
+ parenleft.bot
+ parenleft.ext
+ parenleft.size1
+ parenleft.size2
+ parenleft.size3
+ parenleft.size4
+ parenleft.top
+ parenright
+ parenright.bot
+ parenright.ext
+ parenright.size1
+ parenright.size2
+ parenright.size3
+ parenright.size4
+ parenright.top
+
+
+
diff --git a/tests/data/TestMathFont-Regular.ufo/metainfo.plist b/tests/data/TestMathFont-Regular.ufo/metainfo.plist
new file mode 100644
index 00000000..7b8b34ac
--- /dev/null
+++ b/tests/data/TestMathFont-Regular.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ creator
+ com.github.fonttools.ufoLib
+ formatVersion
+ 3
+
+
diff --git a/tests/outlineCompiler_test.py b/tests/outlineCompiler_test.py
index df52f4bf..86cc1b21 100644
--- a/tests/outlineCompiler_test.py
+++ b/tests/outlineCompiler_test.py
@@ -18,6 +18,9 @@
)
from ufo2ft.constants import (
GLYPHS_DONT_USE_PRODUCTION_NAMES,
+ GLYPHS_MATH_CONSTANTS_KEY,
+ GLYPHS_MATH_EXTENDED_SHAPE_KEY,
+ GLYPHS_MATH_VARIANTS_KEY,
OPENTYPE_POST_UNDERLINE_POSITION_KEY,
SPARSE_OTF_MASTER_TABLES,
SPARSE_TTF_MASTER_TABLES,
@@ -1403,6 +1406,28 @@ def test_achVendId_space_padded_if_less_than_4_chars(
assert font["OS/2"].achVendID == expected
+def test_MATH_table(FontClass):
+ ufo = FontClass(getpath("TestMathFont-Regular.ufo"))
+ result = compileTTF(ufo)
+ assert "MATH" in result
+
+ math = result["MATH"].table
+
+ for key, value in ufo.lib[GLYPHS_MATH_CONSTANTS_KEY].items():
+ attr = getattr(math.MathConstants, key)
+ if isinstance(attr, int):
+ assert attr == value
+ else:
+ assert attr.Value == value
+
+ extendedShapes = set(ufo.lib[GLYPHS_MATH_EXTENDED_SHAPE_KEY])
+ for glyph in ufo.lib[GLYPHS_MATH_EXTENDED_SHAPE_KEY]:
+ if variants := ufo[glyph].lib.get(GLYPHS_MATH_VARIANTS_KEY):
+ extendedShapes.update(variants.get("vVariants", []))
+
+ assert set(math.MathGlyphInfo.ExtendedShapeCoverage.glyphs) == extendedShapes
+
+
if __name__ == "__main__":
import sys