|
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style license that can be |
|
3 // found in the LICENSE file. |
|
4 |
|
5 // We use an underscore to avoid confusion with the standard math.h library. |
|
6 #include "math_.h" |
|
7 |
|
8 #include <limits> |
|
9 #include <vector> |
|
10 |
|
11 #include "layout.h" |
|
12 #include "maxp.h" |
|
13 |
|
14 // MATH - The MATH Table |
|
15 // The specification is not yet public but has been submitted to the MPEG group |
|
16 // in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font |
|
17 // Format" Color Font Technology and MATH layout support'. Meanwhile, you can |
|
18 // contact Microsoft's engineer Murray Sargent to obtain a copy. |
|
19 |
|
20 #define TABLE_NAME "MATH" |
|
21 |
|
22 namespace { |
|
23 |
|
24 // The size of MATH header. |
|
25 // Version |
|
26 // MathConstants |
|
27 // MathGlyphInfo |
|
28 // MathVariants |
|
29 const unsigned kMathHeaderSize = 4 + 3 * 2; |
|
30 |
|
31 // The size of the MathGlyphInfo header. |
|
32 // MathItalicsCorrectionInfo |
|
33 // MathTopAccentAttachment |
|
34 // ExtendedShapeCoverage |
|
35 // MathKernInfo |
|
36 const unsigned kMathGlyphInfoHeaderSize = 4 * 2; |
|
37 |
|
38 // The size of the MathValueRecord. |
|
39 // Value |
|
40 // DeviceTable |
|
41 const unsigned kMathValueRecordSize = 2 * 2; |
|
42 |
|
43 // The size of the GlyphPartRecord. |
|
44 // glyph |
|
45 // StartConnectorLength |
|
46 // EndConnectorLength |
|
47 // FullAdvance |
|
48 // PartFlags |
|
49 const unsigned kGlyphPartRecordSize = 5 * 2; |
|
50 |
|
51 // Shared Table: MathValueRecord |
|
52 |
|
53 bool ParseMathValueRecord(const ots::OpenTypeFile *file, |
|
54 ots::Buffer* subtable, const uint8_t *data, |
|
55 const size_t length) { |
|
56 // Check the Value field. |
|
57 if (!subtable->Skip(2)) { |
|
58 return OTS_FAILURE(); |
|
59 } |
|
60 |
|
61 // Check the offset to device table. |
|
62 uint16_t offset = 0; |
|
63 if (!subtable->ReadU16(&offset)) { |
|
64 return OTS_FAILURE(); |
|
65 } |
|
66 if (offset) { |
|
67 if (offset >= length) { |
|
68 return OTS_FAILURE(); |
|
69 } |
|
70 if (!ots::ParseDeviceTable(file, data + offset, length - offset)) { |
|
71 return OTS_FAILURE(); |
|
72 } |
|
73 } |
|
74 |
|
75 return true; |
|
76 } |
|
77 |
|
78 bool ParseMathConstantsTable(const ots::OpenTypeFile *file, |
|
79 const uint8_t *data, size_t length) { |
|
80 ots::Buffer subtable(data, length); |
|
81 |
|
82 // Part 1: int16 or uint16 constants. |
|
83 // ScriptPercentScaleDown |
|
84 // ScriptScriptPercentScaleDown |
|
85 // DelimitedSubFormulaMinHeight |
|
86 // DisplayOperatorMinHeight |
|
87 if (!subtable.Skip(4 * 2)) { |
|
88 return OTS_FAILURE(); |
|
89 } |
|
90 |
|
91 // Part 2: MathValueRecord constants. |
|
92 // MathLeading |
|
93 // AxisHeight |
|
94 // AccentBaseHeight |
|
95 // FlattenedAccentBaseHeight |
|
96 // SubscriptShiftDown |
|
97 // SubscriptTopMax |
|
98 // SubscriptBaselineDropMin |
|
99 // SuperscriptShiftUp |
|
100 // SuperscriptShiftUpCramped |
|
101 // SuperscriptBottomMin |
|
102 // |
|
103 // SuperscriptBaselineDropMax |
|
104 // SubSuperscriptGapMin |
|
105 // SuperscriptBottomMaxWithSubscript |
|
106 // SpaceAfterScript |
|
107 // UpperLimitGapMin |
|
108 // UpperLimitBaselineRiseMin |
|
109 // LowerLimitGapMin |
|
110 // LowerLimitBaselineDropMin |
|
111 // StackTopShiftUp |
|
112 // StackTopDisplayStyleShiftUp |
|
113 // |
|
114 // StackBottomShiftDown |
|
115 // StackBottomDisplayStyleShiftDown |
|
116 // StackGapMin |
|
117 // StackDisplayStyleGapMin |
|
118 // StretchStackTopShiftUp |
|
119 // StretchStackBottomShiftDown |
|
120 // StretchStackGapAboveMin |
|
121 // StretchStackGapBelowMin |
|
122 // FractionNumeratorShiftUp |
|
123 // FractionNumeratorDisplayStyleShiftUp |
|
124 // |
|
125 // FractionDenominatorShiftDown |
|
126 // FractionDenominatorDisplayStyleShiftDown |
|
127 // FractionNumeratorGapMin |
|
128 // FractionNumDisplayStyleGapMin |
|
129 // FractionRuleThickness |
|
130 // FractionDenominatorGapMin |
|
131 // FractionDenomDisplayStyleGapMin |
|
132 // SkewedFractionHorizontalGap |
|
133 // SkewedFractionVerticalGap |
|
134 // OverbarVerticalGap |
|
135 // |
|
136 // OverbarRuleThickness |
|
137 // OverbarExtraAscender |
|
138 // UnderbarVerticalGap |
|
139 // UnderbarRuleThickness |
|
140 // UnderbarExtraDescender |
|
141 // RadicalVerticalGap |
|
142 // RadicalDisplayStyleVerticalGap |
|
143 // RadicalRuleThickness |
|
144 // RadicalExtraAscender |
|
145 // RadicalKernBeforeDegree |
|
146 // |
|
147 // RadicalKernAfterDegree |
|
148 for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) { |
|
149 if (!ParseMathValueRecord(file, &subtable, data, length)) { |
|
150 return OTS_FAILURE(); |
|
151 } |
|
152 } |
|
153 |
|
154 // Part 3: uint16 constant |
|
155 // RadicalDegreeBottomRaisePercent |
|
156 if (!subtable.Skip(2)) { |
|
157 return OTS_FAILURE(); |
|
158 } |
|
159 |
|
160 return true; |
|
161 } |
|
162 |
|
163 bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile *file, |
|
164 ots::Buffer* subtable, |
|
165 const uint8_t *data, |
|
166 const size_t length, |
|
167 const uint16_t num_glyphs) { |
|
168 // Check the header. |
|
169 uint16_t offset_coverage = 0; |
|
170 uint16_t sequence_count = 0; |
|
171 if (!subtable->ReadU16(&offset_coverage) || |
|
172 !subtable->ReadU16(&sequence_count)) { |
|
173 return OTS_FAILURE(); |
|
174 } |
|
175 |
|
176 const unsigned sequence_end = static_cast<unsigned>(2 * 2) + |
|
177 sequence_count * kMathValueRecordSize; |
|
178 if (sequence_end > std::numeric_limits<uint16_t>::max()) { |
|
179 return OTS_FAILURE(); |
|
180 } |
|
181 |
|
182 // Check coverage table. |
|
183 if (offset_coverage < sequence_end || offset_coverage >= length) { |
|
184 return OTS_FAILURE(); |
|
185 } |
|
186 if (!ots::ParseCoverageTable(file, data + offset_coverage, |
|
187 length - offset_coverage, |
|
188 num_glyphs, sequence_count)) { |
|
189 return OTS_FAILURE(); |
|
190 } |
|
191 |
|
192 // Check sequence. |
|
193 for (unsigned i = 0; i < sequence_count; ++i) { |
|
194 if (!ParseMathValueRecord(file, subtable, data, length)) { |
|
195 return OTS_FAILURE(); |
|
196 } |
|
197 } |
|
198 |
|
199 return true; |
|
200 } |
|
201 |
|
202 bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile *file, |
|
203 const uint8_t *data, |
|
204 size_t length, |
|
205 const uint16_t num_glyphs) { |
|
206 ots::Buffer subtable(data, length); |
|
207 return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length, |
|
208 num_glyphs); |
|
209 } |
|
210 |
|
211 bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile *file, |
|
212 const uint8_t *data, |
|
213 size_t length, |
|
214 const uint16_t num_glyphs) { |
|
215 ots::Buffer subtable(data, length); |
|
216 return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length, |
|
217 num_glyphs); |
|
218 } |
|
219 |
|
220 bool ParseMathKernTable(const ots::OpenTypeFile *file, |
|
221 const uint8_t *data, size_t length) { |
|
222 ots::Buffer subtable(data, length); |
|
223 |
|
224 // Check the Height count. |
|
225 uint16_t height_count = 0; |
|
226 if (!subtable.ReadU16(&height_count)) { |
|
227 return OTS_FAILURE(); |
|
228 } |
|
229 |
|
230 // Check the Correction Heights. |
|
231 for (unsigned i = 0; i < height_count; ++i) { |
|
232 if (!ParseMathValueRecord(file, &subtable, data, length)) { |
|
233 return OTS_FAILURE(); |
|
234 } |
|
235 } |
|
236 |
|
237 // Check the Kern Values. |
|
238 for (unsigned i = 0; i <= height_count; ++i) { |
|
239 if (!ParseMathValueRecord(file, &subtable, data, length)) { |
|
240 return OTS_FAILURE(); |
|
241 } |
|
242 } |
|
243 |
|
244 return true; |
|
245 } |
|
246 |
|
247 bool ParseMathKernInfoTable(const ots::OpenTypeFile *file, |
|
248 const uint8_t *data, size_t length, |
|
249 const uint16_t num_glyphs) { |
|
250 ots::Buffer subtable(data, length); |
|
251 |
|
252 // Check the header. |
|
253 uint16_t offset_coverage = 0; |
|
254 uint16_t sequence_count = 0; |
|
255 if (!subtable.ReadU16(&offset_coverage) || |
|
256 !subtable.ReadU16(&sequence_count)) { |
|
257 return OTS_FAILURE(); |
|
258 } |
|
259 |
|
260 const unsigned sequence_end = static_cast<unsigned>(2 * 2) + |
|
261 sequence_count * 4 * 2; |
|
262 if (sequence_end > std::numeric_limits<uint16_t>::max()) { |
|
263 return OTS_FAILURE(); |
|
264 } |
|
265 |
|
266 // Check coverage table. |
|
267 if (offset_coverage < sequence_end || offset_coverage >= length) { |
|
268 return OTS_FAILURE(); |
|
269 } |
|
270 if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, |
|
271 num_glyphs, sequence_count)) { |
|
272 return OTS_FAILURE(); |
|
273 } |
|
274 |
|
275 // Check sequence of MathKernInfoRecord |
|
276 for (unsigned i = 0; i < sequence_count; ++i) { |
|
277 // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern. |
|
278 for (unsigned j = 0; j < 4; ++j) { |
|
279 uint16_t offset_math_kern = 0; |
|
280 if (!subtable.ReadU16(&offset_math_kern)) { |
|
281 return OTS_FAILURE(); |
|
282 } |
|
283 if (offset_math_kern) { |
|
284 if (offset_math_kern < sequence_end || offset_math_kern >= length || |
|
285 !ParseMathKernTable(file, data + offset_math_kern, |
|
286 length - offset_math_kern)) { |
|
287 return OTS_FAILURE(); |
|
288 } |
|
289 } |
|
290 } |
|
291 } |
|
292 |
|
293 return true; |
|
294 } |
|
295 |
|
296 bool ParseMathGlyphInfoTable(const ots::OpenTypeFile *file, |
|
297 const uint8_t *data, size_t length, |
|
298 const uint16_t num_glyphs) { |
|
299 ots::Buffer subtable(data, length); |
|
300 |
|
301 // Check Header. |
|
302 uint16_t offset_math_italics_correction_info = 0; |
|
303 uint16_t offset_math_top_accent_attachment = 0; |
|
304 uint16_t offset_extended_shaped_coverage = 0; |
|
305 uint16_t offset_math_kern_info = 0; |
|
306 if (!subtable.ReadU16(&offset_math_italics_correction_info) || |
|
307 !subtable.ReadU16(&offset_math_top_accent_attachment) || |
|
308 !subtable.ReadU16(&offset_extended_shaped_coverage) || |
|
309 !subtable.ReadU16(&offset_math_kern_info)) { |
|
310 return OTS_FAILURE(); |
|
311 } |
|
312 |
|
313 // Check subtables. |
|
314 // The specification does not say whether the offsets for |
|
315 // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may |
|
316 // be NULL, but that's the case in some fonts (e.g STIX) so we accept that. |
|
317 if (offset_math_italics_correction_info) { |
|
318 if (offset_math_italics_correction_info >= length || |
|
319 offset_math_italics_correction_info < kMathGlyphInfoHeaderSize || |
|
320 !ParseMathItalicsCorrectionInfoTable( |
|
321 file, data + offset_math_italics_correction_info, |
|
322 length - offset_math_italics_correction_info, |
|
323 num_glyphs)) { |
|
324 return OTS_FAILURE(); |
|
325 } |
|
326 } |
|
327 if (offset_math_top_accent_attachment) { |
|
328 if (offset_math_top_accent_attachment >= length || |
|
329 offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize || |
|
330 !ParseMathTopAccentAttachmentTable(file, data + |
|
331 offset_math_top_accent_attachment, |
|
332 length - |
|
333 offset_math_top_accent_attachment, |
|
334 num_glyphs)) { |
|
335 return OTS_FAILURE(); |
|
336 } |
|
337 } |
|
338 if (offset_extended_shaped_coverage) { |
|
339 if (offset_extended_shaped_coverage >= length || |
|
340 offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize || |
|
341 !ots::ParseCoverageTable(file, data + offset_extended_shaped_coverage, |
|
342 length - offset_extended_shaped_coverage, |
|
343 num_glyphs)) { |
|
344 return OTS_FAILURE(); |
|
345 } |
|
346 } |
|
347 if (offset_math_kern_info) { |
|
348 if (offset_math_kern_info >= length || |
|
349 offset_math_kern_info < kMathGlyphInfoHeaderSize || |
|
350 !ParseMathKernInfoTable(file, data + offset_math_kern_info, |
|
351 length - offset_math_kern_info, num_glyphs)) { |
|
352 return OTS_FAILURE(); |
|
353 } |
|
354 } |
|
355 |
|
356 return true; |
|
357 } |
|
358 |
|
359 bool ParseGlyphAssemblyTable(const ots::OpenTypeFile *file, |
|
360 const uint8_t *data, |
|
361 size_t length, const uint16_t num_glyphs) { |
|
362 ots::Buffer subtable(data, length); |
|
363 |
|
364 // Check the header. |
|
365 uint16_t part_count = 0; |
|
366 if (!ParseMathValueRecord(file, &subtable, data, length) || |
|
367 !subtable.ReadU16(&part_count)) { |
|
368 return OTS_FAILURE(); |
|
369 } |
|
370 |
|
371 const unsigned sequence_end = kMathValueRecordSize + |
|
372 static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize; |
|
373 if (sequence_end > std::numeric_limits<uint16_t>::max()) { |
|
374 return OTS_FAILURE(); |
|
375 } |
|
376 |
|
377 // Check the sequence of GlyphPartRecord. |
|
378 for (unsigned i = 0; i < part_count; ++i) { |
|
379 uint16_t glyph = 0; |
|
380 uint16_t part_flags = 0; |
|
381 if (!subtable.ReadU16(&glyph) || |
|
382 !subtable.Skip(2 * 3) || |
|
383 !subtable.ReadU16(&part_flags)) { |
|
384 return OTS_FAILURE(); |
|
385 } |
|
386 if (glyph >= num_glyphs) { |
|
387 OTS_WARNING("bad glyph ID: %u", glyph); |
|
388 return OTS_FAILURE(); |
|
389 } |
|
390 if (part_flags & ~0x00000001) { |
|
391 OTS_WARNING("unknown part flag: %u", part_flags); |
|
392 return OTS_FAILURE(); |
|
393 } |
|
394 } |
|
395 |
|
396 return true; |
|
397 } |
|
398 |
|
399 bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file, |
|
400 const uint8_t *data, |
|
401 size_t length, const uint16_t num_glyphs) { |
|
402 ots::Buffer subtable(data, length); |
|
403 |
|
404 // Check the header. |
|
405 uint16_t offset_glyph_assembly = 0; |
|
406 uint16_t variant_count = 0; |
|
407 if (!subtable.ReadU16(&offset_glyph_assembly) || |
|
408 !subtable.ReadU16(&variant_count)) { |
|
409 return OTS_FAILURE(); |
|
410 } |
|
411 |
|
412 const unsigned sequence_end = static_cast<unsigned>(2 * 2) + |
|
413 variant_count * 2 * 2; |
|
414 if (sequence_end > std::numeric_limits<uint16_t>::max()) { |
|
415 return OTS_FAILURE(); |
|
416 } |
|
417 |
|
418 // Check the GlyphAssembly offset. |
|
419 if (offset_glyph_assembly) { |
|
420 if (offset_glyph_assembly >= length || |
|
421 offset_glyph_assembly < sequence_end) { |
|
422 return OTS_FAILURE(); |
|
423 } |
|
424 if (!ParseGlyphAssemblyTable(file, data + offset_glyph_assembly, |
|
425 length - offset_glyph_assembly, num_glyphs)) { |
|
426 return OTS_FAILURE(); |
|
427 } |
|
428 } |
|
429 |
|
430 // Check the sequence of MathGlyphVariantRecord. |
|
431 for (unsigned i = 0; i < variant_count; ++i) { |
|
432 uint16_t glyph = 0; |
|
433 if (!subtable.ReadU16(&glyph) || |
|
434 !subtable.Skip(2)) { |
|
435 return OTS_FAILURE(); |
|
436 } |
|
437 if (glyph >= num_glyphs) { |
|
438 OTS_WARNING("bad glyph ID: %u", glyph); |
|
439 return OTS_FAILURE(); |
|
440 } |
|
441 } |
|
442 |
|
443 return true; |
|
444 } |
|
445 |
|
446 bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file, |
|
447 ots::Buffer* subtable, |
|
448 const uint8_t *data, |
|
449 size_t length, |
|
450 const uint16_t num_glyphs, |
|
451 uint16_t offset_coverage, |
|
452 uint16_t glyph_count, |
|
453 const unsigned sequence_end) { |
|
454 // Check coverage table. |
|
455 if (offset_coverage < sequence_end || offset_coverage >= length) { |
|
456 return OTS_FAILURE(); |
|
457 } |
|
458 if (!ots::ParseCoverageTable(file, data + offset_coverage, |
|
459 length - offset_coverage, |
|
460 num_glyphs, glyph_count)) { |
|
461 return OTS_FAILURE(); |
|
462 } |
|
463 |
|
464 // Check sequence of MathGlyphConstruction. |
|
465 for (unsigned i = 0; i < glyph_count; ++i) { |
|
466 uint16_t offset_glyph_construction = 0; |
|
467 if (!subtable->ReadU16(&offset_glyph_construction)) { |
|
468 return OTS_FAILURE(); |
|
469 } |
|
470 if (offset_glyph_construction < sequence_end || |
|
471 offset_glyph_construction >= length || |
|
472 !ParseMathGlyphConstructionTable(file, data + offset_glyph_construction, |
|
473 length - offset_glyph_construction, |
|
474 num_glyphs)) { |
|
475 return OTS_FAILURE(); |
|
476 } |
|
477 } |
|
478 |
|
479 return true; |
|
480 } |
|
481 |
|
482 bool ParseMathVariantsTable(const ots::OpenTypeFile *file, |
|
483 const uint8_t *data, |
|
484 size_t length, const uint16_t num_glyphs) { |
|
485 ots::Buffer subtable(data, length); |
|
486 |
|
487 // Check the header. |
|
488 uint16_t offset_vert_glyph_coverage = 0; |
|
489 uint16_t offset_horiz_glyph_coverage = 0; |
|
490 uint16_t vert_glyph_count = 0; |
|
491 uint16_t horiz_glyph_count = 0; |
|
492 if (!subtable.Skip(2) || // MinConnectorOverlap |
|
493 !subtable.ReadU16(&offset_vert_glyph_coverage) || |
|
494 !subtable.ReadU16(&offset_horiz_glyph_coverage) || |
|
495 !subtable.ReadU16(&vert_glyph_count) || |
|
496 !subtable.ReadU16(&horiz_glyph_count)) { |
|
497 return OTS_FAILURE(); |
|
498 } |
|
499 |
|
500 const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 + |
|
501 horiz_glyph_count * 2; |
|
502 if (sequence_end > std::numeric_limits<uint16_t>::max()) { |
|
503 return OTS_FAILURE(); |
|
504 } |
|
505 |
|
506 if (!ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs, |
|
507 offset_vert_glyph_coverage, |
|
508 vert_glyph_count, |
|
509 sequence_end) || |
|
510 !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs, |
|
511 offset_horiz_glyph_coverage, |
|
512 horiz_glyph_count, |
|
513 sequence_end)) { |
|
514 return OTS_FAILURE(); |
|
515 } |
|
516 |
|
517 return true; |
|
518 } |
|
519 |
|
520 } // namespace |
|
521 |
|
522 #define DROP_THIS_TABLE \ |
|
523 do { file->math->data = 0; file->math->length = 0; } while (0) |
|
524 |
|
525 namespace ots { |
|
526 |
|
527 bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { |
|
528 // Grab the number of glyphs in the file from the maxp table to check |
|
529 // GlyphIDs in MATH table. |
|
530 if (!file->maxp) { |
|
531 return OTS_FAILURE(); |
|
532 } |
|
533 const uint16_t num_glyphs = file->maxp->num_glyphs; |
|
534 |
|
535 Buffer table(data, length); |
|
536 |
|
537 OpenTypeMATH* math = new OpenTypeMATH; |
|
538 file->math = math; |
|
539 |
|
540 uint32_t version = 0; |
|
541 if (!table.ReadU32(&version)) { |
|
542 return OTS_FAILURE(); |
|
543 } |
|
544 if (version != 0x00010000) { |
|
545 OTS_WARNING("bad MATH version"); |
|
546 DROP_THIS_TABLE; |
|
547 return true; |
|
548 } |
|
549 |
|
550 uint16_t offset_math_constants = 0; |
|
551 uint16_t offset_math_glyph_info = 0; |
|
552 uint16_t offset_math_variants = 0; |
|
553 if (!table.ReadU16(&offset_math_constants) || |
|
554 !table.ReadU16(&offset_math_glyph_info) || |
|
555 !table.ReadU16(&offset_math_variants)) { |
|
556 return OTS_FAILURE(); |
|
557 } |
|
558 |
|
559 if (offset_math_constants >= length || |
|
560 offset_math_constants < kMathHeaderSize || |
|
561 offset_math_glyph_info >= length || |
|
562 offset_math_glyph_info < kMathHeaderSize || |
|
563 offset_math_variants >= length || |
|
564 offset_math_variants < kMathHeaderSize) { |
|
565 OTS_WARNING("bad offset in MATH header"); |
|
566 DROP_THIS_TABLE; |
|
567 return true; |
|
568 } |
|
569 |
|
570 if (!ParseMathConstantsTable(file, data + offset_math_constants, |
|
571 length - offset_math_constants)) { |
|
572 DROP_THIS_TABLE; |
|
573 return true; |
|
574 } |
|
575 if (!ParseMathGlyphInfoTable(file, data + offset_math_glyph_info, |
|
576 length - offset_math_glyph_info, num_glyphs)) { |
|
577 DROP_THIS_TABLE; |
|
578 return true; |
|
579 } |
|
580 if (!ParseMathVariantsTable(file, data + offset_math_variants, |
|
581 length - offset_math_variants, num_glyphs)) { |
|
582 DROP_THIS_TABLE; |
|
583 return true; |
|
584 } |
|
585 |
|
586 math->data = data; |
|
587 math->length = length; |
|
588 return true; |
|
589 } |
|
590 |
|
591 bool ots_math_should_serialise(OpenTypeFile *file) { |
|
592 return file->math != NULL && file->math->data != NULL; |
|
593 } |
|
594 |
|
595 bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) { |
|
596 if (!out->Write(file->math->data, file->math->length)) { |
|
597 return OTS_FAILURE(); |
|
598 } |
|
599 |
|
600 return true; |
|
601 } |
|
602 |
|
603 void ots_math_free(OpenTypeFile *file) { |
|
604 delete file->math; |
|
605 } |
|
606 |
|
607 } // namespace ots |
|
608 |