font_metrics.dart
7.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
import '../../ast/font_metrics.dart';
import '../../ast/size.dart';
import '../../ast/types.dart';
import 'font_metrics_data.dart';
import 'unicode_scripts.dart';
/// This file contains metrics regarding fonts and individual symbols. The sigma
/// and xi variables, as well as the metricMap map contain data extracted from
/// TeX, TeX font metrics, and the TTF files. These data are then exposed via
/// the `metrics` variable and the getCharacterMetrics function.
// In TeX, there are actually three sets of dimensions, one for each of
// textstyle (size index 5 and higher: >=9pt), scriptstyle (size index 3 and 4:
// 7-8pt), and scriptscriptstyle (size index 1 and 2: 5-6pt). These are
// provided in the the arrays below, in that order.
//
// The font metrics are stored in fonts cmsy10, cmsy7, and cmsy5 respsectively.
// This was determined by running the following script:
//
// latex -interaction=nonstopmode \
// '\documentclass{article}\usepackage{amsmath}\begin{document}' \
// '$a$ \expandafter\show\the\textfont2' \
// '\expandafter\show\the\scriptfont2' \
// '\expandafter\show\the\scriptscriptfont2' \
// '\stop'
//
// The metrics themselves were retreived using the following commands:
//
// tftopl cmsy10
// tftopl cmsy7
// tftopl cmsy5
//
// The output of each of these commands is quite lengthy. The only part we
// care about is the FONTDIMEN section. Each value is measured in EMs.
const sigmasAndXis = {
'slant': [0.250, 0.250, 0.250], // sigma1
'space': [0.000, 0.000, 0.000], // sigma2
'stretch': [0.000, 0.000, 0.000], // sigma3
'shrink': [0.000, 0.000, 0.000], // sigma4
'xHeight': [0.431, 0.431, 0.431], // sigma5
'quad': [1.000, 1.171, 1.472], // sigma6
'extraSpace': [0.000, 0.000, 0.000], // sigma7
'num1': [0.677, 0.732, 0.925], // sigma8
'num2': [0.394, 0.384, 0.387], // sigma9
'num3': [0.444, 0.471, 0.504], // sigma10
'denom1': [0.686, 0.752, 1.025], // sigma11
'denom2': [0.345, 0.344, 0.532], // sigma12
'sup1': [0.413, 0.503, 0.504], // sigma13
'sup2': [0.363, 0.431, 0.404], // sigma14
'sup3': [0.289, 0.286, 0.294], // sigma15
'sub1': [0.150, 0.143, 0.200], // sigma16
'sub2': [0.247, 0.286, 0.400], // sigma17
'supDrop': [0.386, 0.353, 0.494], // sigma18
'subDrop': [0.050, 0.071, 0.100], // sigma19
'delim1': [2.390, 1.700, 1.980], // sigma20
'delim2': [1.010, 1.157, 1.420], // sigma21
'axisHeight': [0.250, 0.250, 0.250], // sigma22
// These font metrics are extracted from TeX by using tftopl on cmex10.tfm;
// they correspond to the font parameters of the extension fonts (family 3).
// See the TeXbook, page 441. In AMSTeX, the extension fonts scale; to
// match cmex7, we'd use cmex7.tfm values for script and scriptscript
// values.
'defaultRuleThickness': [0.04, 0.049, 0.049], // xi8; cmex7: 0.049
'bigOpSpacing1': [0.111, 0.111, 0.111], // xi9
'bigOpSpacing2': [0.166, 0.166, 0.166], // xi10
'bigOpSpacing3': [0.2, 0.2, 0.2], // xi11
'bigOpSpacing4': [0.6, 0.611, 0.611], // xi12; cmex7: 0.611
'bigOpSpacing5': [0.1, 0.143, 0.143], // xi13; cmex7: 0.143
// The \sqrt rule width is taken from the height of the surd character.
// Since we use the same font at all sizes, this thickness doesn't scale.
'sqrtRuleThickness': [0.04, 0.04, 0.04],
// This value determines how large a pt is, for metrics which are defined
// in terms of pts.
// This value is also used in katex.less; if you change it make sure the
// values match.
'ptPerEm': [10.0, 10.0, 10.0],
// The space between adjacent `|` columns in an array definition. From
// `\showthe\doublerulesep` in LaTeX. Equals 2.0 / ptPerEm.
'doubleRuleSep': [0.2, 0.2, 0.2],
// The width of separator lines in {array} environments. From
// `\showthe\arrayrulewidth` in LaTeX. Equals 0.4 / ptPerEm.
'arrayRuleWidth': [0.04, 0.04, 0.04],
// Two values from LaTeX source2e:
'fboxsep': [0.3, 0.3, 0.3], // 3 pt / ptPerEm
'fboxrule': [0.04, 0.04, 0.04], // 0.4 pt / ptPerEm
};
final textFontMetrics = FontMetrics.fromMap(
sigmasAndXis.map((key, value) => MapEntry(key, value[0])))!;
final scriptFontMetrics = FontMetrics.fromMap(
sigmasAndXis.map((key, value) => MapEntry(key, value[1])))!;
final scriptscriptFontMetrics = FontMetrics.fromMap(
sigmasAndXis.map((key, value) => MapEntry(key, value[2])))!;
const extraCharacterMap = {
// Latin-1
'Å': 'A',
'Ç': 'C',
'Ð': 'D',
'Þ': 'o',
'å': 'a',
'ç': 'c',
'ð': 'd',
'þ': 'o',
// Cyrillic
'А': 'A',
'Б': 'B',
'В': 'B',
'Г': 'F',
'Д': 'A',
'Е': 'E',
'Ж': 'K',
'З': '3',
'И': 'N',
'Й': 'N',
'К': 'K',
'Л': 'N',
'М': 'M',
'Н': 'H',
'О': 'O',
'П': 'N',
'Р': 'P',
'С': 'C',
'Т': 'T',
'У': 'y',
'Ф': 'O',
'Х': 'X',
'Ц': 'U',
'Ч': 'h',
'Ш': 'W',
'Щ': 'W',
'Ъ': 'B',
'Ы': 'X',
'Ь': 'B',
'Э': '3',
'Ю': 'X',
'Я': 'R',
'а': 'a',
'б': 'b',
'в': 'a',
'г': 'r',
'д': 'y',
'е': 'e',
'ж': 'm',
'з': 'e',
'и': 'n',
'й': 'n',
'к': 'n',
'л': 'n',
'м': 'm',
'н': 'n',
'о': 'o',
'п': 'n',
'р': 'p',
'с': 'c',
'т': 'o',
'у': 'y',
'ф': 'b',
'х': 'x',
'ц': 'n',
'ч': 'n',
'ш': 'w',
'щ': 'w',
'ъ': 'a',
'ы': 'm',
'ь': 'a',
'э': 'e',
'ю': 'm',
'я': 'r',
};
class CharacterMetrics {
final double depth;
final double height;
final double italic;
final double skew;
final double width;
const CharacterMetrics(
this.depth,
this.height,
this.italic,
this.skew,
this.width,
);
}
final Map<String, Map<int, CharacterMetrics>> metricsMap = fontMetricsData;
CharacterMetrics? getCharacterMetrics(
{required String character, required String fontName, required Mode mode}) {
final metricsMapFont = metricsMap[fontName];
if (metricsMapFont == null) {
throw Exception('Font metrics not found for font: $fontName.');
}
final ch = character.codeUnitAt(0);
if (metricsMapFont.containsKey(ch)) {
return metricsMapFont[ch];
}
final extraCh = extraCharacterMap[character[0]]?.codeUnitAt(0);
if (extraCh != null) {
return metricsMapFont[ch];
}
if (mode == Mode.text && supportedCodepoint(ch)) {
// We don't typically have font metrics for Asian scripts.
// But since we support them in text mode, we need to return
// some sort of metrics.
// So if the character is in a script we support but we
// don't have metrics for it, just use the metrics for
// the Latin capital letter M. This is close enough because
// we (currently) only care about the height of the glpyh
// not its width.
return metricsMapFont[77]; // 77 is the charcode for 'M'
}
return null;
}
FontMetrics getGlobalMetrics(MathSize size) {
switch (size) {
case MathSize.tiny:
case MathSize.size2:
return scriptscriptFontMetrics;
case MathSize.scriptsize:
case MathSize.footnotesize:
return scriptFontMetrics;
case MathSize.small:
case MathSize.normalsize:
case MathSize.large:
case MathSize.Large:
case MathSize.LARGE:
case MathSize.huge:
case MathSize.HUGE:
return textFontMetrics;
default:
throw ArgumentError(size);
}
}