1+ # Based on https://github.com/kostub/iosMath/blob/master/fonts/math_table_to_plist.py
2+ # Added: get_h_assembly (version 1.3 -> 1.4)
3+
4+ import sys
5+ import json
6+ from fontTools .ttLib import TTFont
7+
8+ def process_font (font_file , out_file ):
9+ font = TTFont (font_file )
10+ math_table = font ['MATH' ].table
11+ constants = get_constants (math_table )
12+ italic_c = get_italic_correction (math_table )
13+ v_variants = get_v_variants (math_table )
14+ h_variants = get_h_variants (math_table )
15+ v_assembly = get_v_assembly (math_table )
16+ h_assembly = get_h_assembly (math_table )
17+ accents = get_accent_attachments (math_table )
18+ pl = {
19+ "version" : "1.4" ,
20+ "constants" : constants ,
21+ "v_variants" : v_variants ,
22+ "h_variants" : h_variants ,
23+ "italic" : italic_c ,
24+ "accents" : accents ,
25+ "v_assembly" : v_assembly ,
26+ "h_assembly" : h_assembly }
27+ ofile = open (out_file , 'w' )
28+ json .dump (pl , ofile )
29+ ofile .close ()
30+
31+ def get_constants (math_table ):
32+ constants = math_table .MathConstants
33+ if constants is None :
34+ raise 'Cannot find MathConstants in MATH table'
35+
36+ int_consts = [ 'ScriptPercentScaleDown' ,
37+ 'ScriptScriptPercentScaleDown' ,
38+ 'DelimitedSubFormulaMinHeight' ,
39+ 'DisplayOperatorMinHeight' ,
40+ 'RadicalDegreeBottomRaisePercent' ]
41+ consts = { c : getattr (constants , c ) for c in int_consts }
42+
43+ record_consts = [ 'MathLeading' ,
44+ 'AxisHeight' ,
45+ 'AccentBaseHeight' ,
46+ 'FlattenedAccentBaseHeight' ,
47+ 'SubscriptShiftDown' ,
48+ 'SubscriptTopMax' ,
49+ 'SubscriptBaselineDropMin' ,
50+ 'SuperscriptShiftUp' ,
51+ 'SuperscriptShiftUpCramped' ,
52+ 'SuperscriptBottomMin' ,
53+ 'SuperscriptBaselineDropMax' ,
54+ 'SubSuperscriptGapMin' ,
55+ 'SuperscriptBottomMaxWithSubscript' ,
56+ 'SpaceAfterScript' ,
57+ 'UpperLimitGapMin' ,
58+ 'UpperLimitBaselineRiseMin' ,
59+ 'LowerLimitGapMin' ,
60+ 'LowerLimitBaselineDropMin' ,
61+ 'StackTopShiftUp' ,
62+ 'StackTopDisplayStyleShiftUp' ,
63+ 'StackBottomShiftDown' ,
64+ 'StackBottomDisplayStyleShiftDown' ,
65+ 'StackGapMin' ,
66+ 'StackDisplayStyleGapMin' ,
67+ 'StretchStackTopShiftUp' ,
68+ 'StretchStackBottomShiftDown' ,
69+ 'StretchStackGapAboveMin' ,
70+ 'StretchStackGapBelowMin' ,
71+ 'FractionNumeratorShiftUp' ,
72+ 'FractionNumeratorDisplayStyleShiftUp' ,
73+ 'FractionDenominatorShiftDown' ,
74+ 'FractionDenominatorDisplayStyleShiftDown' ,
75+ 'FractionNumeratorGapMin' ,
76+ 'FractionNumDisplayStyleGapMin' ,
77+ 'FractionRuleThickness' ,
78+ 'FractionDenominatorGapMin' ,
79+ 'FractionDenomDisplayStyleGapMin' ,
80+ 'SkewedFractionHorizontalGap' ,
81+ 'SkewedFractionVerticalGap' ,
82+ 'OverbarVerticalGap' ,
83+ 'OverbarRuleThickness' ,
84+ 'OverbarExtraAscender' ,
85+ 'UnderbarVerticalGap' ,
86+ 'UnderbarRuleThickness' ,
87+ 'UnderbarExtraDescender' ,
88+ 'RadicalVerticalGap' ,
89+ 'RadicalDisplayStyleVerticalGap' ,
90+ 'RadicalRuleThickness' ,
91+ 'RadicalExtraAscender' ,
92+ 'RadicalKernBeforeDegree' ,
93+ 'RadicalKernAfterDegree' ,
94+ ]
95+ consts_2 = { c : getattr (constants , c ).Value for c in record_consts }
96+ consts .update (consts_2 )
97+
98+ variants = math_table .MathVariants
99+ consts ['MinConnectorOverlap' ] = variants .MinConnectorOverlap
100+ return consts
101+
102+ def get_italic_correction (math_table ):
103+ glyph_info = math_table .MathGlyphInfo
104+ if glyph_info is None :
105+ raise "Cannot find MathGlyphInfo in MATH table."
106+ italic = glyph_info .MathItalicsCorrectionInfo
107+ if italic is None :
108+ raise "Cannot find Italic Correction in GlyphInfo"
109+
110+ glyphs = italic .Coverage .glyphs
111+ count = italic .ItalicsCorrectionCount
112+ records = italic .ItalicsCorrection
113+ italic_dict = {}
114+ for i in range (count ):
115+ name = glyphs [i ]
116+ record = records [i ]
117+ if record .DeviceTable is not None :
118+ raise "Don't know how to process device table for italic correction."
119+ italic_dict [name ] = record .Value
120+ return italic_dict
121+
122+ def get_accent_attachments (math_table ):
123+ glyph_info = math_table .MathGlyphInfo
124+ if glyph_info is None :
125+ raise "Cannot find MathGlyphInfo in MATH table."
126+ attach = glyph_info .MathTopAccentAttachment
127+ if attach is None :
128+ raise "Cannot find Top Accent Attachment in GlyphInfo"
129+
130+ glyphs = attach .TopAccentCoverage .glyphs
131+ count = attach .TopAccentAttachmentCount
132+ records = attach .TopAccentAttachment
133+ attach_dict = {}
134+ for i in range (count ):
135+ name = glyphs [i ]
136+ record = records [i ]
137+ if record .DeviceTable is not None :
138+ raise "Don't know how to process device table for accent attachment."
139+ attach_dict [name ] = record .Value
140+ return attach_dict
141+
142+ def get_v_variants (math_table ):
143+ variants = math_table .MathVariants
144+ vglyphs = variants .VertGlyphCoverage .glyphs
145+ vconstruction = variants .VertGlyphConstruction
146+ count = variants .VertGlyphCount
147+ variant_dict = {}
148+ for i in range (count ):
149+ name = vglyphs [i ]
150+ record = vconstruction [i ]
151+ glyph_variants = [x .VariantGlyph for x in
152+ record .MathGlyphVariantRecord ]
153+ variant_dict [name ] = glyph_variants
154+ return variant_dict
155+
156+ def get_h_variants (math_table ):
157+ variants = math_table .MathVariants
158+ hglyphs = variants .HorizGlyphCoverage .glyphs
159+ hconstruction = variants .HorizGlyphConstruction
160+ count = variants .HorizGlyphCount
161+ variant_dict = {}
162+ for i in range (count ):
163+ name = hglyphs [i ]
164+ record = hconstruction [i ]
165+ glyph_variants = [x .VariantGlyph for x in
166+ record .MathGlyphVariantRecord ]
167+ variant_dict [name ] = glyph_variants
168+ return variant_dict
169+
170+ def get_v_assembly (math_table ):
171+ variants = math_table .MathVariants
172+ vglyphs = variants .VertGlyphCoverage .glyphs
173+ vconstruction = variants .VertGlyphConstruction
174+ count = variants .VertGlyphCount
175+ assembly_dict = {}
176+ for i in range (count ):
177+ name = vglyphs [i ]
178+ record = vconstruction [i ]
179+ assembly = record .GlyphAssembly
180+ if assembly is not None :
181+ # There is an assembly for this glyph
182+ italic = assembly .ItalicsCorrection .Value
183+ parts = [part_dict (part ) for part in assembly .PartRecords ]
184+ assembly_dict [name ] = {
185+ "italic" : assembly .ItalicsCorrection .Value ,
186+ "parts" : parts }
187+ return assembly_dict
188+
189+ def get_h_assembly (math_table ):
190+ variants = math_table .MathVariants
191+ hglyphs = variants .HorizGlyphCoverage .glyphs
192+ hconstruction = variants .HorizGlyphConstruction
193+ count = variants .HorizGlyphCount
194+ assembly_dict = {}
195+ for i in range (count ):
196+ name = hglyphs [i ]
197+ record = hconstruction [i ]
198+ assembly = record .GlyphAssembly
199+ if assembly is not None :
200+ # There is an assembly for this glyph
201+ italic = assembly .ItalicsCorrection .Value
202+ parts = [part_dict (part ) for part in assembly .PartRecords ]
203+ assembly_dict [name ] = {
204+ "italic" : assembly .ItalicsCorrection .Value ,
205+ "parts" : parts }
206+ return assembly_dict
207+
208+ def part_dict (part ):
209+ return {
210+ "glyph" : part .glyph ,
211+ "startConnector" : part .StartConnectorLength ,
212+ "endConnector" : part .EndConnectorLength ,
213+ "advance" : part .FullAdvance ,
214+ "extender" : (part .PartFlags == 1 ) }
215+
216+ process_font ('../../CSharpMath.Rendering/Reference Fonts/latinmodern-math.otf' , 'latinmodern-math.json' )
0 commit comments