1 | //! A [Math Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math) implementation. |
2 | |
3 | use crate::gpos::Device; |
4 | use crate::opentype_layout::Coverage; |
5 | use crate::parser::{ |
6 | FromData, FromSlice, LazyArray16, LazyOffsetArray16, Offset, Offset16, Stream, |
7 | }; |
8 | use crate::GlyphId; |
9 | |
10 | /// A [Math Value](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathvaluerecord) |
11 | /// with optional device corrections. |
12 | #[derive (Clone, Copy, Debug)] |
13 | pub struct MathValue<'a> { |
14 | /// The X or Y value in font design units. |
15 | pub value: i16, |
16 | /// Device corrections for this value. |
17 | pub device: Option<Device<'a>>, |
18 | } |
19 | |
20 | impl<'a> MathValue<'a> { |
21 | fn parse(data: &'a [u8], parent: &'a [u8]) -> Option<Self> { |
22 | Some(MathValueRecord::parse(data)?.get(data:parent)) |
23 | } |
24 | } |
25 | |
26 | /// A math value record with unresolved offset. |
27 | #[derive (Clone, Copy)] |
28 | struct MathValueRecord { |
29 | value: i16, |
30 | device_offset: Option<Offset16>, |
31 | } |
32 | |
33 | impl FromData for MathValueRecord { |
34 | const SIZE: usize = 4; |
35 | |
36 | fn parse(data: &[u8]) -> Option<Self> { |
37 | let mut s: Stream<'_> = Stream::new(data); |
38 | let value: i16 = s.read::<i16>()?; |
39 | let device_offset: Option = s.read::<Option<Offset16>>()?; |
40 | Some(MathValueRecord { |
41 | value, |
42 | device_offset, |
43 | }) |
44 | } |
45 | } |
46 | |
47 | impl MathValueRecord { |
48 | fn get(self, data: &[u8]) -> MathValue { |
49 | let device: Option> = self |
50 | .device_offset |
51 | .and_then(|offset: Offset16| data.get(index:offset.to_usize()..)) |
52 | .and_then(Device::parse); |
53 | MathValue { |
54 | value: self.value, |
55 | device, |
56 | } |
57 | } |
58 | } |
59 | |
60 | /// A mapping from glyphs to |
61 | /// [Math Values](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathvaluerecord). |
62 | #[derive (Clone, Copy)] |
63 | pub struct MathValues<'a> { |
64 | data: &'a [u8], |
65 | coverage: Coverage<'a>, |
66 | records: LazyArray16<'a, MathValueRecord>, |
67 | } |
68 | |
69 | impl<'a> FromSlice<'a> for MathValues<'a> { |
70 | fn parse(data: &'a [u8]) -> Option<Self> { |
71 | let mut s: Stream<'_> = Stream::new(data); |
72 | let coverage: Coverage<'_> = s.parse_at_offset16::<Coverage>(data)?; |
73 | let count: u16 = s.read::<u16>()?; |
74 | let records: LazyArray16<'_, MathValueRecord> = s.read_array16::<MathValueRecord>(count)?; |
75 | Some(MathValues { |
76 | data, |
77 | coverage, |
78 | records, |
79 | }) |
80 | } |
81 | } |
82 | |
83 | impl<'a> MathValues<'a> { |
84 | /// Returns the value for the glyph or `None` if it is not covered. |
85 | #[inline ] |
86 | pub fn get(&self, glyph: GlyphId) -> Option<MathValue<'a>> { |
87 | let index: u16 = self.coverage.get(glyph)?; |
88 | Some(self.records.get(index)?.get(self.data)) |
89 | } |
90 | } |
91 | |
92 | impl core::fmt::Debug for MathValues<'_> { |
93 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
94 | write!(f, "MathValues {{ ... }}" ) |
95 | } |
96 | } |
97 | |
98 | /// A [Math Constants Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathconstants-table). |
99 | #[derive (Clone, Copy)] |
100 | pub struct Constants<'a> { |
101 | data: &'a [u8], |
102 | } |
103 | |
104 | impl<'a> FromSlice<'a> for Constants<'a> { |
105 | fn parse(data: &'a [u8]) -> Option<Self> { |
106 | Some(Constants { data }) |
107 | } |
108 | } |
109 | |
110 | impl core::fmt::Debug for Constants<'_> { |
111 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
112 | write!(f, "Constants {{ ... }}" ) |
113 | } |
114 | } |
115 | |
116 | const SCRIPT_PERCENT_SCALE_DOWN_OFFSET: usize = 0; |
117 | const SCRIPT_SCRIPT_PERCENT_SCALE_DOWN_OFFSET: usize = 2; |
118 | const DELIMITED_SUB_FORMULA_MIN_HEIGHT_OFFSET: usize = 4; |
119 | const DISPLAY_OPERATOR_MIN_HEIGHT_OFFSET: usize = 6; |
120 | const MATH_LEADING_OFFSET: usize = 8; |
121 | const AXIS_HEIGHT_OFFSET: usize = 12; |
122 | const ACCENT_BASE_HEIGHT_OFFSET: usize = 16; |
123 | const FLATTENED_ACCENT_BASE_HEIGHT_OFFSET: usize = 20; |
124 | const SUBSCRIPT_SHIFT_DOWN_OFFSET: usize = 24; |
125 | const SUBSCRIPT_TOP_MAX_OFFSET: usize = 28; |
126 | const SUBSCRIPT_BASELINE_DROP_MIN_OFFSET: usize = 32; |
127 | const SUPERSCRIPT_SHIFT_UP_OFFSET: usize = 36; |
128 | const SUPERSCRIPT_SHIFT_UP_CRAMPED_OFFSET: usize = 40; |
129 | const SUPERSCRIPT_BOTTOM_MIN_OFFSET: usize = 44; |
130 | const SUPERSCRIPT_BASELINE_DROP_MAX_OFFSET: usize = 48; |
131 | const SUB_SUPERSCRIPT_GAP_MIN_OFFSET: usize = 52; |
132 | const SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT_OFFSET: usize = 56; |
133 | const SPACE_AFTER_SCRIPT_OFFSET: usize = 60; |
134 | const UPPER_LIMIT_GAP_MIN_OFFSET: usize = 64; |
135 | const UPPER_LIMIT_BASELINE_RISE_MIN_OFFSET: usize = 68; |
136 | const LOWER_LIMIT_GAP_MIN_OFFSET: usize = 72; |
137 | const LOWER_LIMIT_BASELINE_DROP_MIN_OFFSET: usize = 76; |
138 | const STACK_TOP_SHIFT_UP_OFFSET: usize = 80; |
139 | const STACK_TOP_DISPLAY_STYLE_SHIFT_UP_OFFSET: usize = 84; |
140 | const STACK_BOTTOM_SHIFT_DOWN_OFFSET: usize = 88; |
141 | const STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN_OFFSET: usize = 92; |
142 | const STACK_GAP_MIN_OFFSET: usize = 96; |
143 | const STACK_DISPLAY_STYLE_GAP_MIN_OFFSET: usize = 100; |
144 | const STRETCH_STACK_TOP_SHIFT_UP_OFFSET: usize = 104; |
145 | const STRETCH_STACK_BOTTOM_SHIFT_DOWN_OFFSET: usize = 108; |
146 | const STRETCH_STACK_GAP_ABOVE_MIN_OFFSET: usize = 112; |
147 | const STRETCH_STACK_GAP_BELOW_MIN_OFFSET: usize = 116; |
148 | const FRACTION_NUMERATOR_SHIFT_UP_OFFSET: usize = 120; |
149 | const FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP_OFFSET: usize = 124; |
150 | const FRACTION_DENOMINATOR_SHIFT_DOWN_OFFSET: usize = 128; |
151 | const FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN_OFFSET: usize = 132; |
152 | const FRACTION_NUMERATOR_GAP_MIN_OFFSET: usize = 136; |
153 | const FRACTION_NUM_DISPLAY_STYLE_GAP_MIN_OFFSET: usize = 140; |
154 | const FRACTION_RULE_THICKNESS_OFFSET: usize = 144; |
155 | const FRACTION_DENOMINATOR_GAP_MIN_OFFSET: usize = 148; |
156 | const FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN_OFFSET: usize = 152; |
157 | const SKEWED_FRACTION_HORIZONTAL_GAP_OFFSET: usize = 156; |
158 | const SKEWED_FRACTION_VERTICAL_GAP_OFFSET: usize = 160; |
159 | const OVERBAR_VERTICAL_GAP_OFFSET: usize = 164; |
160 | const OVERBAR_RULE_THICKNESS_OFFSET: usize = 168; |
161 | const OVERBAR_EXTRA_ASCENDER_OFFSET: usize = 172; |
162 | const UNDERBAR_VERTICAL_GAP_OFFSET: usize = 176; |
163 | const UNDERBAR_RULE_THICKNESS_OFFSET: usize = 180; |
164 | const UNDERBAR_EXTRA_DESCENDER_OFFSET: usize = 184; |
165 | const RADICAL_VERTICAL_GAP_OFFSET: usize = 188; |
166 | const RADICAL_DISPLAY_STYLE_VERTICAL_GAP_OFFSET: usize = 192; |
167 | const RADICAL_RULE_THICKNESS_OFFSET: usize = 196; |
168 | const RADICAL_EXTRA_ASCENDER_OFFSET: usize = 200; |
169 | const RADICAL_KERN_BEFORE_DEGREE_OFFSET: usize = 204; |
170 | const RADICAL_KERN_AFTER_DEGREE_OFFSET: usize = 208; |
171 | const RADICAL_DEGREE_BOTTOM_RAISE_PERCENT_OFFSET: usize = 212; |
172 | |
173 | impl<'a> Constants<'a> { |
174 | /// Percentage of scaling down for level 1 superscripts and subscripts. |
175 | #[inline ] |
176 | pub fn script_percent_scale_down(&self) -> i16 { |
177 | self.read_i16(SCRIPT_PERCENT_SCALE_DOWN_OFFSET) |
178 | } |
179 | |
180 | /// Percentage of scaling down for level 2 (scriptScript) superscripts and subscripts. |
181 | #[inline ] |
182 | pub fn script_script_percent_scale_down(&self) -> i16 { |
183 | self.read_i16(SCRIPT_SCRIPT_PERCENT_SCALE_DOWN_OFFSET) |
184 | } |
185 | |
186 | /// Minimum height required for a delimited expression (contained within parentheses, etc.) to |
187 | /// be treated as a sub-formula. |
188 | #[inline ] |
189 | pub fn delimited_sub_formula_min_height(&self) -> u16 { |
190 | self.read_u16(DELIMITED_SUB_FORMULA_MIN_HEIGHT_OFFSET) |
191 | } |
192 | |
193 | /// Minimum height of n-ary operators (such as integral and summation) for formulas in display |
194 | /// mode (that is, appearing as standalone page elements, not embedded inline within text). |
195 | #[inline ] |
196 | pub fn display_operator_min_height(&self) -> u16 { |
197 | self.read_u16(DISPLAY_OPERATOR_MIN_HEIGHT_OFFSET) |
198 | } |
199 | |
200 | /// White space to be left between math formulas to ensure proper line spacing. |
201 | #[inline ] |
202 | pub fn math_leading(&self) -> MathValue<'a> { |
203 | self.read_record(MATH_LEADING_OFFSET) |
204 | } |
205 | |
206 | /// Axis height of the font. |
207 | #[inline ] |
208 | pub fn axis_height(&self) -> MathValue<'a> { |
209 | self.read_record(AXIS_HEIGHT_OFFSET) |
210 | } |
211 | |
212 | /// Maximum (ink) height of accent base that does not require raising the accents. |
213 | #[inline ] |
214 | pub fn accent_base_height(&self) -> MathValue<'a> { |
215 | self.read_record(ACCENT_BASE_HEIGHT_OFFSET) |
216 | } |
217 | |
218 | /// Maximum (ink) height of accent base that does not require flattening the accents. |
219 | #[inline ] |
220 | pub fn flattened_accent_base_height(&self) -> MathValue<'a> { |
221 | self.read_record(FLATTENED_ACCENT_BASE_HEIGHT_OFFSET) |
222 | } |
223 | |
224 | /// The standard shift down applied to subscript elements. |
225 | #[inline ] |
226 | pub fn subscript_shift_down(&self) -> MathValue<'a> { |
227 | self.read_record(SUBSCRIPT_SHIFT_DOWN_OFFSET) |
228 | } |
229 | |
230 | /// Maximum allowed height of the (ink) top of subscripts that does not require moving |
231 | /// subscripts further down. |
232 | #[inline ] |
233 | pub fn subscript_top_max(&self) -> MathValue<'a> { |
234 | self.read_record(SUBSCRIPT_TOP_MAX_OFFSET) |
235 | } |
236 | |
237 | /// Minimum allowed drop of the baseline of subscripts relative to the (ink) bottom of the |
238 | /// base. |
239 | #[inline ] |
240 | pub fn subscript_baseline_drop_min(&self) -> MathValue<'a> { |
241 | self.read_record(SUBSCRIPT_BASELINE_DROP_MIN_OFFSET) |
242 | } |
243 | |
244 | /// Standard shift up applied to superscript elements. |
245 | #[inline ] |
246 | pub fn superscript_shift_up(&self) -> MathValue<'a> { |
247 | self.read_record(SUPERSCRIPT_SHIFT_UP_OFFSET) |
248 | } |
249 | |
250 | /// Standard shift of superscripts relative to the base, in cramped style. |
251 | #[inline ] |
252 | pub fn superscript_shift_up_cramped(&self) -> MathValue<'a> { |
253 | self.read_record(SUPERSCRIPT_SHIFT_UP_CRAMPED_OFFSET) |
254 | } |
255 | |
256 | /// Minimum allowed height of the (ink) bottom of superscripts that does not require moving |
257 | /// subscripts further up. |
258 | #[inline ] |
259 | pub fn superscript_bottom_min(&self) -> MathValue<'a> { |
260 | self.read_record(SUPERSCRIPT_BOTTOM_MIN_OFFSET) |
261 | } |
262 | |
263 | /// Maximum allowed drop of the baseline of superscripts relative to the (ink) top of the |
264 | /// base. |
265 | #[inline ] |
266 | pub fn superscript_baseline_drop_max(&self) -> MathValue<'a> { |
267 | self.read_record(SUPERSCRIPT_BASELINE_DROP_MAX_OFFSET) |
268 | } |
269 | |
270 | /// Minimum gap between the superscript and subscript ink. |
271 | #[inline ] |
272 | pub fn sub_superscript_gap_min(&self) -> MathValue<'a> { |
273 | self.read_record(SUB_SUPERSCRIPT_GAP_MIN_OFFSET) |
274 | } |
275 | |
276 | /// The maximum level to which the (ink) bottom of superscript can be pushed to increase the |
277 | /// gap between superscript and subscript, before subscript starts being moved down. |
278 | #[inline ] |
279 | pub fn superscript_bottom_max_with_subscript(&self) -> MathValue<'a> { |
280 | self.read_record(SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT_OFFSET) |
281 | } |
282 | |
283 | /// Extra white space to be added after each subscript and superscript. |
284 | #[inline ] |
285 | pub fn space_after_script(&self) -> MathValue<'a> { |
286 | self.read_record(SPACE_AFTER_SCRIPT_OFFSET) |
287 | } |
288 | |
289 | /// Minimum gap between the (ink) bottom of the upper limit, and the (ink) top of the base |
290 | /// operator. |
291 | #[inline ] |
292 | pub fn upper_limit_gap_min(&self) -> MathValue<'a> { |
293 | self.read_record(UPPER_LIMIT_GAP_MIN_OFFSET) |
294 | } |
295 | |
296 | /// Minimum distance between baseline of upper limit and (ink) top of the base operator. |
297 | #[inline ] |
298 | pub fn upper_limit_baseline_rise_min(&self) -> MathValue<'a> { |
299 | self.read_record(UPPER_LIMIT_BASELINE_RISE_MIN_OFFSET) |
300 | } |
301 | |
302 | /// Minimum gap between (ink) top of the lower limit, and (ink) bottom of the base operator. |
303 | #[inline ] |
304 | pub fn lower_limit_gap_min(&self) -> MathValue<'a> { |
305 | self.read_record(LOWER_LIMIT_GAP_MIN_OFFSET) |
306 | } |
307 | |
308 | /// Minimum distance between baseline of the lower limit and (ink) bottom of the base operator. |
309 | #[inline ] |
310 | pub fn lower_limit_baseline_drop_min(&self) -> MathValue<'a> { |
311 | self.read_record(LOWER_LIMIT_BASELINE_DROP_MIN_OFFSET) |
312 | } |
313 | |
314 | /// Standard shift up applied to the top element of a stack. |
315 | #[inline ] |
316 | pub fn stack_top_shift_up(&self) -> MathValue<'a> { |
317 | self.read_record(STACK_TOP_SHIFT_UP_OFFSET) |
318 | } |
319 | |
320 | /// Standard shift up applied to the top element of a stack in display style. |
321 | #[inline ] |
322 | pub fn stack_top_display_style_shift_up(&self) -> MathValue<'a> { |
323 | self.read_record(STACK_TOP_DISPLAY_STYLE_SHIFT_UP_OFFSET) |
324 | } |
325 | |
326 | /// Standard shift down applied to the bottom element of a stack. |
327 | #[inline ] |
328 | pub fn stack_bottom_shift_down(&self) -> MathValue<'a> { |
329 | self.read_record(STACK_BOTTOM_SHIFT_DOWN_OFFSET) |
330 | } |
331 | |
332 | /// Standard shift down applied to the bottom element of a stack in display style. |
333 | #[inline ] |
334 | pub fn stack_bottom_display_style_shift_down(&self) -> MathValue<'a> { |
335 | self.read_record(STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN_OFFSET) |
336 | } |
337 | |
338 | /// Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the |
339 | /// bottom element. |
340 | #[inline ] |
341 | pub fn stack_gap_min(&self) -> MathValue<'a> { |
342 | self.read_record(STACK_GAP_MIN_OFFSET) |
343 | } |
344 | |
345 | /// Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the |
346 | /// bottom element in display style. |
347 | #[inline ] |
348 | pub fn stack_display_style_gap_min(&self) -> MathValue<'a> { |
349 | self.read_record(STACK_DISPLAY_STYLE_GAP_MIN_OFFSET) |
350 | } |
351 | |
352 | /// Standard shift up applied to the top element of the stretch stack. |
353 | #[inline ] |
354 | pub fn stretch_stack_top_shift_up(&self) -> MathValue<'a> { |
355 | self.read_record(STRETCH_STACK_TOP_SHIFT_UP_OFFSET) |
356 | } |
357 | |
358 | /// Standard shift down applied to the bottom element of the stretch stack. |
359 | #[inline ] |
360 | pub fn stretch_stack_bottom_shift_down(&self) -> MathValue<'a> { |
361 | self.read_record(STRETCH_STACK_BOTTOM_SHIFT_DOWN_OFFSET) |
362 | } |
363 | |
364 | /// Minimum gap between the ink of the stretched element, and the (ink) bottom of the element above. |
365 | #[inline ] |
366 | pub fn stretch_stack_gap_above_min(&self) -> MathValue<'a> { |
367 | self.read_record(STRETCH_STACK_GAP_ABOVE_MIN_OFFSET) |
368 | } |
369 | |
370 | /// Minimum gap between the ink of the stretched element, and the (ink) top of the element below. |
371 | #[inline ] |
372 | pub fn stretch_stack_gap_below_min(&self) -> MathValue<'a> { |
373 | self.read_record(STRETCH_STACK_GAP_BELOW_MIN_OFFSET) |
374 | } |
375 | |
376 | /// Standard shift up applied to the numerator. |
377 | #[inline ] |
378 | pub fn fraction_numerator_shift_up(&self) -> MathValue<'a> { |
379 | self.read_record(FRACTION_NUMERATOR_SHIFT_UP_OFFSET) |
380 | } |
381 | |
382 | /// Standard shift up applied to the numerator in display style. |
383 | #[inline ] |
384 | pub fn fraction_numerator_display_style_shift_up(&self) -> MathValue<'a> { |
385 | self.read_record(FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP_OFFSET) |
386 | } |
387 | |
388 | /// Standard shift down applied to the denominator. |
389 | #[inline ] |
390 | pub fn fraction_denominator_shift_down(&self) -> MathValue<'a> { |
391 | self.read_record(FRACTION_DENOMINATOR_SHIFT_DOWN_OFFSET) |
392 | } |
393 | |
394 | /// Standard shift down applied to the denominator in display style. |
395 | #[inline ] |
396 | pub fn fraction_denominator_display_style_shift_down(&self) -> MathValue<'a> { |
397 | self.read_record(FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN_OFFSET) |
398 | } |
399 | |
400 | /// Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the |
401 | /// fraction bar. |
402 | #[inline ] |
403 | pub fn fraction_numerator_gap_min(&self) -> MathValue<'a> { |
404 | self.read_record(FRACTION_NUMERATOR_GAP_MIN_OFFSET) |
405 | } |
406 | |
407 | /// Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the |
408 | /// fraction bar in display style. |
409 | #[inline ] |
410 | pub fn fraction_num_display_style_gap_min(&self) -> MathValue<'a> { |
411 | self.read_record(FRACTION_NUM_DISPLAY_STYLE_GAP_MIN_OFFSET) |
412 | } |
413 | |
414 | /// Thickness of the fraction bar. |
415 | #[inline ] |
416 | pub fn fraction_rule_thickness(&self) -> MathValue<'a> { |
417 | self.read_record(FRACTION_RULE_THICKNESS_OFFSET) |
418 | } |
419 | |
420 | /// Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar. |
421 | #[inline ] |
422 | pub fn fraction_denominator_gap_min(&self) -> MathValue<'a> { |
423 | self.read_record(FRACTION_DENOMINATOR_GAP_MIN_OFFSET) |
424 | } |
425 | |
426 | /// Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction |
427 | /// bar in display style. |
428 | #[inline ] |
429 | pub fn fraction_denom_display_style_gap_min(&self) -> MathValue<'a> { |
430 | self.read_record(FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN_OFFSET) |
431 | } |
432 | |
433 | /// Horizontal distance between the top and bottom elements of a skewed fraction. |
434 | #[inline ] |
435 | pub fn skewed_fraction_horizontal_gap(&self) -> MathValue<'a> { |
436 | self.read_record(SKEWED_FRACTION_HORIZONTAL_GAP_OFFSET) |
437 | } |
438 | |
439 | /// Vertical distance between the ink of the top and bottom elements of a skewed fraction. |
440 | #[inline ] |
441 | pub fn skewed_fraction_vertical_gap(&self) -> MathValue<'a> { |
442 | self.read_record(SKEWED_FRACTION_VERTICAL_GAP_OFFSET) |
443 | } |
444 | |
445 | /// Distance between the overbar and the (ink) top of he base. |
446 | #[inline ] |
447 | pub fn overbar_vertical_gap(&self) -> MathValue<'a> { |
448 | self.read_record(OVERBAR_VERTICAL_GAP_OFFSET) |
449 | } |
450 | |
451 | /// Thickness of overbar. |
452 | #[inline ] |
453 | pub fn overbar_rule_thickness(&self) -> MathValue<'a> { |
454 | self.read_record(OVERBAR_RULE_THICKNESS_OFFSET) |
455 | } |
456 | |
457 | /// Extra white space reserved above the overbar. |
458 | #[inline ] |
459 | pub fn overbar_extra_ascender(&self) -> MathValue<'a> { |
460 | self.read_record(OVERBAR_EXTRA_ASCENDER_OFFSET) |
461 | } |
462 | |
463 | /// Distance between underbar and (ink) bottom of the base. |
464 | #[inline ] |
465 | pub fn underbar_vertical_gap(&self) -> MathValue<'a> { |
466 | self.read_record(UNDERBAR_VERTICAL_GAP_OFFSET) |
467 | } |
468 | |
469 | /// Thickness of underbar. |
470 | #[inline ] |
471 | pub fn underbar_rule_thickness(&self) -> MathValue<'a> { |
472 | self.read_record(UNDERBAR_RULE_THICKNESS_OFFSET) |
473 | } |
474 | |
475 | /// Extra white space reserved below the underbar. |
476 | #[inline ] |
477 | pub fn underbar_extra_descender(&self) -> MathValue<'a> { |
478 | self.read_record(UNDERBAR_EXTRA_DESCENDER_OFFSET) |
479 | } |
480 | |
481 | /// Space between the (ink) top of the expression and the bar over it. |
482 | #[inline ] |
483 | pub fn radical_vertical_gap(&self) -> MathValue<'a> { |
484 | self.read_record(RADICAL_VERTICAL_GAP_OFFSET) |
485 | } |
486 | |
487 | /// Space between the (ink) top of the expression and the bar over it. |
488 | #[inline ] |
489 | pub fn radical_display_style_vertical_gap(&self) -> MathValue<'a> { |
490 | self.read_record(RADICAL_DISPLAY_STYLE_VERTICAL_GAP_OFFSET) |
491 | } |
492 | |
493 | /// Thickness of the radical rule. |
494 | #[inline ] |
495 | pub fn radical_rule_thickness(&self) -> MathValue<'a> { |
496 | self.read_record(RADICAL_RULE_THICKNESS_OFFSET) |
497 | } |
498 | |
499 | /// Extra white space reserved above the radical. |
500 | #[inline ] |
501 | pub fn radical_extra_ascender(&self) -> MathValue<'a> { |
502 | self.read_record(RADICAL_EXTRA_ASCENDER_OFFSET) |
503 | } |
504 | |
505 | /// Extra horizontal kern before the degree of a radical, if such is present. |
506 | #[inline ] |
507 | pub fn radical_kern_before_degree(&self) -> MathValue<'a> { |
508 | self.read_record(RADICAL_KERN_BEFORE_DEGREE_OFFSET) |
509 | } |
510 | |
511 | /// Negative kern after the degree of a radical, if such is present. |
512 | #[inline ] |
513 | pub fn radical_kern_after_degree(&self) -> MathValue<'a> { |
514 | self.read_record(RADICAL_KERN_AFTER_DEGREE_OFFSET) |
515 | } |
516 | |
517 | /// Height of the bottom of the radical degree, if such is present, in proportion to the |
518 | /// ascender of the radical sign. |
519 | #[inline ] |
520 | pub fn radical_degree_bottom_raise_percent(&self) -> i16 { |
521 | self.read_i16(RADICAL_DEGREE_BOTTOM_RAISE_PERCENT_OFFSET) |
522 | } |
523 | |
524 | /// Read an `i16` at an offset into the table. |
525 | #[inline ] |
526 | fn read_i16(&self, offset: usize) -> i16 { |
527 | Stream::read_at(self.data, offset).unwrap_or(0) |
528 | } |
529 | |
530 | /// Read an `u16` at an offset into the table. |
531 | #[inline ] |
532 | fn read_u16(&self, offset: usize) -> u16 { |
533 | Stream::read_at(self.data, offset).unwrap_or(0) |
534 | } |
535 | |
536 | /// Read a `MathValueRecord` at an offset into the table. |
537 | #[inline ] |
538 | fn read_record(&self, offset: usize) -> MathValue<'a> { |
539 | self.data |
540 | .get(offset..) |
541 | .and_then(|data| MathValue::parse(data, self.data)) |
542 | .unwrap_or(MathValue { |
543 | value: 0, |
544 | device: None, |
545 | }) |
546 | } |
547 | } |
548 | |
549 | /// A [Math Kern Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathkern-table). |
550 | #[derive (Clone)] |
551 | pub struct Kern<'a> { |
552 | data: &'a [u8], |
553 | heights: LazyArray16<'a, MathValueRecord>, |
554 | kerns: LazyArray16<'a, MathValueRecord>, |
555 | } |
556 | |
557 | impl<'a> Kern<'a> { |
558 | /// Number of heights at which the kern value changes. |
559 | pub fn count(&self) -> u16 { |
560 | self.heights.len() |
561 | } |
562 | |
563 | /// The correction height at the given index. |
564 | /// |
565 | /// The index must be smaller than `count()`. |
566 | pub fn height(&self, index: u16) -> Option<MathValue<'a>> { |
567 | Some(self.heights.get(index)?.get(self.data)) |
568 | } |
569 | |
570 | /// The kern value at the given index. |
571 | /// |
572 | /// The index must be smaller than or equal to `count()`. |
573 | pub fn kern(&self, index: u16) -> Option<MathValue<'a>> { |
574 | Some(self.kerns.get(index)?.get(self.data)) |
575 | } |
576 | } |
577 | |
578 | impl<'a> FromSlice<'a> for Kern<'a> { |
579 | fn parse(data: &'a [u8]) -> Option<Self> { |
580 | let mut s: Stream<'_> = Stream::new(data); |
581 | let count: u16 = s.read::<u16>()?; |
582 | let heights: LazyArray16<'_, MathValueRecord> = s.read_array16::<MathValueRecord>(count)?; |
583 | let kerns: LazyArray16<'_, MathValueRecord> = s.read_array16::<MathValueRecord>(count:count + 1)?; |
584 | Some(Kern { |
585 | data, |
586 | heights, |
587 | kerns, |
588 | }) |
589 | } |
590 | } |
591 | |
592 | impl core::fmt::Debug for Kern<'_> { |
593 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
594 | write!(f, "Kern {{ ... }}" ) |
595 | } |
596 | } |
597 | |
598 | #[derive (Clone, Copy)] |
599 | struct KernInfoRecord { |
600 | top_right: Option<Offset16>, |
601 | top_left: Option<Offset16>, |
602 | bottom_right: Option<Offset16>, |
603 | bottom_left: Option<Offset16>, |
604 | } |
605 | |
606 | impl FromData for KernInfoRecord { |
607 | const SIZE: usize = 8; |
608 | |
609 | fn parse(data: &[u8]) -> Option<Self> { |
610 | let mut s: Stream<'_> = Stream::new(data); |
611 | Some(KernInfoRecord { |
612 | top_right: s.read::<Option<Offset16>>()?, |
613 | top_left: s.read::<Option<Offset16>>()?, |
614 | bottom_right: s.read::<Option<Offset16>>()?, |
615 | bottom_left: s.read::<Option<Offset16>>()?, |
616 | }) |
617 | } |
618 | } |
619 | |
620 | impl KernInfoRecord { |
621 | fn get<'a>(&self, data: &'a [u8]) -> KernInfo<'a> { |
622 | let parse_field: impl Fn(Option) -> … = |offset: Option<Offset16>| { |
623 | offsetOption<&[u8]> |
624 | .and_then(|offset: Offset16| data.get(index:offset.to_usize()..)) |
625 | .and_then(Kern::parse) |
626 | }; |
627 | KernInfo { |
628 | top_right: parse_field(self.top_right), |
629 | top_left: parse_field(self.top_left), |
630 | bottom_right: parse_field(self.bottom_right), |
631 | bottom_left: parse_field(self.bottom_left), |
632 | } |
633 | } |
634 | } |
635 | |
636 | /// An [entry in a Math Kern Info Table]( |
637 | /// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathkerninforecord). |
638 | #[derive (Clone, Debug)] |
639 | pub struct KernInfo<'a> { |
640 | /// The kerning data for the top-right corner. |
641 | pub top_right: Option<Kern<'a>>, |
642 | /// The kerning data for the top-left corner. |
643 | pub top_left: Option<Kern<'a>>, |
644 | /// The kerning data for the bottom-right corner. |
645 | pub bottom_right: Option<Kern<'a>>, |
646 | /// The kerning data for the bottom-left corner. |
647 | pub bottom_left: Option<Kern<'a>>, |
648 | } |
649 | |
650 | /// A [Math Kern Info Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathkerninfo-table). |
651 | #[derive (Clone, Copy)] |
652 | pub struct KernInfos<'a> { |
653 | data: &'a [u8], |
654 | coverage: Coverage<'a>, |
655 | records: LazyArray16<'a, KernInfoRecord>, |
656 | } |
657 | |
658 | impl<'a> FromSlice<'a> for KernInfos<'a> { |
659 | fn parse(data: &'a [u8]) -> Option<Self> { |
660 | let mut s: Stream<'_> = Stream::new(data); |
661 | let coverage: Coverage<'_> = s.parse_at_offset16::<Coverage>(data)?; |
662 | let count: u16 = s.read::<u16>()?; |
663 | let records: LazyArray16<'_, KernInfoRecord> = s.read_array16::<KernInfoRecord>(count)?; |
664 | Some(KernInfos { |
665 | data, |
666 | coverage, |
667 | records, |
668 | }) |
669 | } |
670 | } |
671 | |
672 | impl<'a> KernInfos<'a> { |
673 | /// Returns the kerning info for the glyph or `None` if it is not covered. |
674 | #[inline ] |
675 | pub fn get(&self, glyph: GlyphId) -> Option<KernInfo<'a>> { |
676 | let index: u16 = self.coverage.get(glyph)?; |
677 | Some(self.records.get(index)?.get(self.data)) |
678 | } |
679 | } |
680 | |
681 | impl core::fmt::Debug for KernInfos<'_> { |
682 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
683 | write!(f, "KernInfos {{ ... }}" ) |
684 | } |
685 | } |
686 | |
687 | /// A [Math Glyph Info Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathglyphinfo-table). |
688 | #[derive (Clone, Copy, Debug)] |
689 | pub struct GlyphInfo<'a> { |
690 | /// Per-glyph italics correction values. |
691 | pub italic_corrections: Option<MathValues<'a>>, |
692 | /// Per-glyph horizontal positions for attaching mathematical accents. |
693 | pub top_accent_attachments: Option<MathValues<'a>>, |
694 | /// Glyphs which are _extended shapes_. |
695 | pub extended_shapes: Option<Coverage<'a>>, |
696 | /// Per-glyph information for mathematical kerning. |
697 | pub kern_infos: Option<KernInfos<'a>>, |
698 | } |
699 | |
700 | impl<'a> FromSlice<'a> for GlyphInfo<'a> { |
701 | fn parse(data: &'a [u8]) -> Option<Self> { |
702 | let mut s: Stream<'_> = Stream::new(data); |
703 | Some(GlyphInfo { |
704 | italic_corrections: s.parse_at_offset16::<MathValues>(data), |
705 | top_accent_attachments: s.parse_at_offset16::<MathValues>(data), |
706 | extended_shapes: s.parse_at_offset16::<Coverage>(data), |
707 | kern_infos: s.parse_at_offset16::<KernInfos>(data), |
708 | }) |
709 | } |
710 | } |
711 | |
712 | /// Glyph part flags. |
713 | #[derive (Clone, Copy, Debug)] |
714 | pub struct PartFlags(pub u16); |
715 | |
716 | #[allow (missing_docs)] |
717 | impl PartFlags { |
718 | #[inline ] |
719 | pub fn extender(self) -> bool { |
720 | self.0 & 0x0001 != 0 |
721 | } |
722 | } |
723 | |
724 | impl FromData for PartFlags { |
725 | const SIZE: usize = 2; |
726 | |
727 | fn parse(data: &[u8]) -> Option<Self> { |
728 | u16::parse(data).map(PartFlags) |
729 | } |
730 | } |
731 | |
732 | /// Details for a glyph part in an assembly. |
733 | #[derive (Clone, Copy, Debug)] |
734 | pub struct GlyphPart { |
735 | /// Glyph ID for the part. |
736 | pub glyph_id: GlyphId, |
737 | /// Lengths of the connectors on the start of the glyph, in font design units. |
738 | pub start_connector_length: u16, |
739 | /// Lengths of the connectors on the end of the glyph, in font design units. |
740 | pub end_connector_length: u16, |
741 | /// The full advance of the part, in font design units. |
742 | pub full_advance: u16, |
743 | /// Part flags. |
744 | pub part_flags: PartFlags, |
745 | } |
746 | |
747 | impl FromData for GlyphPart { |
748 | const SIZE: usize = 10; |
749 | |
750 | fn parse(data: &[u8]) -> Option<Self> { |
751 | let mut s: Stream<'_> = Stream::new(data); |
752 | Some(GlyphPart { |
753 | glyph_id: s.read::<GlyphId>()?, |
754 | start_connector_length: s.read::<u16>()?, |
755 | end_connector_length: s.read::<u16>()?, |
756 | full_advance: s.read::<u16>()?, |
757 | part_flags: s.read::<PartFlags>()?, |
758 | }) |
759 | } |
760 | } |
761 | |
762 | /// A [Glyph Assembly Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#glyphassembly-table). |
763 | #[derive (Clone, Copy, Debug)] |
764 | pub struct GlyphAssembly<'a> { |
765 | /// The italics correction of the assembled glyph. |
766 | pub italics_correction: MathValue<'a>, |
767 | /// Parts the assembly is composed of. |
768 | pub parts: LazyArray16<'a, GlyphPart>, |
769 | } |
770 | |
771 | impl<'a> FromSlice<'a> for GlyphAssembly<'a> { |
772 | fn parse(data: &'a [u8]) -> Option<Self> { |
773 | let mut s: Stream<'_> = Stream::new(data); |
774 | let italics_correction: MathValue<'_> = s.read::<MathValueRecord>()?.get(data); |
775 | let count: u16 = s.read::<u16>()?; |
776 | let parts: LazyArray16<'_, GlyphPart> = s.read_array16::<GlyphPart>(count)?; |
777 | Some(GlyphAssembly { |
778 | italics_correction, |
779 | parts, |
780 | }) |
781 | } |
782 | } |
783 | |
784 | /// Description of math glyph variants. |
785 | #[derive (Clone, Copy, Debug)] |
786 | pub struct GlyphVariant { |
787 | /// The ID of the variant glyph. |
788 | pub variant_glyph: GlyphId, |
789 | /// Advance width/height, in design units, of the variant glyph. |
790 | pub advance_measurement: u16, |
791 | } |
792 | |
793 | impl FromData for GlyphVariant { |
794 | const SIZE: usize = 4; |
795 | |
796 | fn parse(data: &[u8]) -> Option<Self> { |
797 | let mut s: Stream<'_> = Stream::new(data); |
798 | Some(GlyphVariant { |
799 | variant_glyph: s.read::<GlyphId>()?, |
800 | advance_measurement: s.read::<u16>()?, |
801 | }) |
802 | } |
803 | } |
804 | |
805 | /// A [Math Glyph Construction Table]( |
806 | /// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathglyphconstruction-table). |
807 | #[derive (Clone, Copy, Debug)] |
808 | pub struct GlyphConstruction<'a> { |
809 | /// A general recipe on how to construct a variant with large advance width/height. |
810 | pub assembly: Option<GlyphAssembly<'a>>, |
811 | /// Prepared variants of the glyph with varying advances. |
812 | pub variants: LazyArray16<'a, GlyphVariant>, |
813 | } |
814 | |
815 | impl<'a> FromSlice<'a> for GlyphConstruction<'a> { |
816 | fn parse(data: &'a [u8]) -> Option<Self> { |
817 | let mut s: Stream<'_> = Stream::new(data); |
818 | let assembly: Option> = s.parse_at_offset16::<GlyphAssembly>(data); |
819 | let variant_count: u16 = s.read::<u16>()?; |
820 | let variants: LazyArray16<'_, GlyphVariant> = s.read_array16::<GlyphVariant>(variant_count)?; |
821 | Some(GlyphConstruction { assembly, variants }) |
822 | } |
823 | } |
824 | |
825 | /// A mapping from glyphs to |
826 | /// [Math Glyph Construction Tables]( |
827 | /// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathglyphconstruction-table). |
828 | #[derive (Clone, Copy)] |
829 | pub struct GlyphConstructions<'a> { |
830 | coverage: Coverage<'a>, |
831 | constructions: LazyOffsetArray16<'a, GlyphConstruction<'a>>, |
832 | } |
833 | |
834 | impl<'a> GlyphConstructions<'a> { |
835 | fn new( |
836 | data: &'a [u8], |
837 | coverage: Option<Coverage<'a>>, |
838 | offsets: LazyArray16<'a, Option<Offset16>>, |
839 | ) -> Self { |
840 | GlyphConstructions { |
841 | coverage: coverage.unwrap_or(default:Coverage::Format1 { |
842 | glyphs: LazyArray16::new(&[]), |
843 | }), |
844 | constructions: LazyOffsetArray16::new(data, offsets), |
845 | } |
846 | } |
847 | |
848 | /// Returns the construction for the glyph or `None` if it is not covered. |
849 | #[inline ] |
850 | pub fn get(&self, glyph: GlyphId) -> Option<GlyphConstruction<'a>> { |
851 | let index: u16 = self.coverage.get(glyph)?; |
852 | self.constructions.get(index) |
853 | } |
854 | } |
855 | |
856 | impl core::fmt::Debug for GlyphConstructions<'_> { |
857 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
858 | write!(f, "GlyphConstructions {{ ... }}" ) |
859 | } |
860 | } |
861 | |
862 | /// A [Math Variants Table]( |
863 | /// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathvariants-table). |
864 | #[derive (Clone, Copy, Debug)] |
865 | pub struct Variants<'a> { |
866 | /// Minimum overlap of connecting glyphs during glyph construction, in design units. |
867 | pub min_connector_overlap: u16, |
868 | /// Constructions for shapes growing in the vertical direction. |
869 | pub vertical_constructions: GlyphConstructions<'a>, |
870 | /// Constructions for shapes growing in the horizontal direction. |
871 | pub horizontal_constructions: GlyphConstructions<'a>, |
872 | } |
873 | |
874 | impl<'a> FromSlice<'a> for Variants<'a> { |
875 | fn parse(data: &'a [u8]) -> Option<Self> { |
876 | let mut s = Stream::new(data); |
877 | let min_connector_overlap = s.read::<u16>()?; |
878 | let vertical_coverage = s.parse_at_offset16::<Coverage>(data); |
879 | let horizontal_coverage = s.parse_at_offset16::<Coverage>(data); |
880 | let vertical_count = s.read::<u16>()?; |
881 | let horizontal_count = s.read::<u16>()?; |
882 | let vertical_offsets = s.read_array16::<Option<Offset16>>(vertical_count)?; |
883 | let horizontal_offsets = s.read_array16::<Option<Offset16>>(horizontal_count)?; |
884 | Some(Variants { |
885 | min_connector_overlap, |
886 | vertical_constructions: GlyphConstructions::new( |
887 | data, |
888 | vertical_coverage, |
889 | vertical_offsets, |
890 | ), |
891 | horizontal_constructions: GlyphConstructions::new( |
892 | data, |
893 | horizontal_coverage, |
894 | horizontal_offsets, |
895 | ), |
896 | }) |
897 | } |
898 | } |
899 | |
900 | /// A [Math Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math). |
901 | #[derive (Clone, Copy, Debug)] |
902 | pub struct Table<'a> { |
903 | /// Math positioning constants. |
904 | pub constants: Option<Constants<'a>>, |
905 | /// Per-glyph positioning information. |
906 | pub glyph_info: Option<GlyphInfo<'a>>, |
907 | /// Variants and assembly recipes for growable glyphs. |
908 | pub variants: Option<Variants<'a>>, |
909 | } |
910 | |
911 | impl<'a> Table<'a> { |
912 | /// Parses a table from raw data. |
913 | pub fn parse(data: &'a [u8]) -> Option<Self> { |
914 | let mut s: Stream<'_> = Stream::new(data); |
915 | let major_version: u8 = s.read::<u16>()? as u8; |
916 | s.skip::<u16>(); // minor version |
917 | if major_version != 1 { |
918 | return None; |
919 | } |
920 | |
921 | Some(Table { |
922 | constants: s.parse_at_offset16::<Constants>(data), |
923 | glyph_info: s.parse_at_offset16::<GlyphInfo>(data), |
924 | variants: s.parse_at_offset16::<Variants>(data), |
925 | }) |
926 | } |
927 | } |
928 | |
929 | trait StreamExt<'a> { |
930 | fn parse_at_offset16<T: FromSlice<'a>>(&mut self, data: &'a [u8]) -> Option<T>; |
931 | } |
932 | |
933 | impl<'a> StreamExt<'a> for Stream<'a> { |
934 | fn parse_at_offset16<T: FromSlice<'a>>(&mut self, data: &'a [u8]) -> Option<T> { |
935 | let offset: usize = self.read::<Option<Offset16>>()??.to_usize(); |
936 | data.get(index:offset..).and_then(T::parse) |
937 | } |
938 | } |
939 | |