Bug 1785744 [wpt PR 35504] - Recalc style for elements where :toggle() pseudo-class...
[gecko.git] / gfx / ots / src / math.cc
blobc94e3bee36c3d77dfac17981f2a0f38792bdc873
1 // Copyright (c) 2014-2017 The OTS 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.
5 // We use an underscore to avoid confusion with the standard math.h library.
6 #include "math_.h"
8 #include <limits>
9 #include <vector>
11 #include "layout.h"
12 #include "maxp.h"
14 // MATH - The MATH Table
15 // http://www.microsoft.com/typography/otspec/math.htm
17 namespace {
19 // The size of MATH header.
20 // Version
21 // MathConstants
22 // MathGlyphInfo
23 // MathVariants
24 const unsigned kMathHeaderSize = 4 + 3 * 2;
26 // The size of the MathGlyphInfo header.
27 // MathItalicsCorrectionInfo
28 // MathTopAccentAttachment
29 // ExtendedShapeCoverage
30 // MathKernInfo
31 const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
33 // The size of the MathValueRecord.
34 // Value
35 // DeviceTable
36 const unsigned kMathValueRecordSize = 2 * 2;
38 // The size of the GlyphPartRecord.
39 // glyph
40 // StartConnectorLength
41 // EndConnectorLength
42 // FullAdvance
43 // PartFlags
44 const unsigned kGlyphPartRecordSize = 5 * 2;
46 } // namespace
48 namespace ots {
50 // Shared Table: MathValueRecord
52 bool OpenTypeMATH::ParseMathValueRecord(ots::Buffer* subtable,
53 const uint8_t *data,
54 const size_t length) {
55 // Check the Value field.
56 if (!subtable->Skip(2)) {
57 return OTS_FAILURE();
60 // Check the offset to device table.
61 uint16_t offset = 0;
62 if (!subtable->ReadU16(&offset)) {
63 return OTS_FAILURE();
65 if (offset) {
66 if (offset >= length) {
67 return OTS_FAILURE();
69 if (!ots::ParseDeviceTable(GetFont(), data + offset, length - offset)) {
70 return OTS_FAILURE();
74 return true;
77 bool OpenTypeMATH::ParseMathConstantsTable(const uint8_t *data,
78 size_t length) {
79 ots::Buffer subtable(data, length);
81 // Part 1: int16 or uint16 constants.
82 // ScriptPercentScaleDown
83 // ScriptScriptPercentScaleDown
84 // DelimitedSubFormulaMinHeight
85 // DisplayOperatorMinHeight
86 if (!subtable.Skip(4 * 2)) {
87 return OTS_FAILURE();
90 // Part 2: MathValueRecord constants.
91 // MathLeading
92 // AxisHeight
93 // AccentBaseHeight
94 // FlattenedAccentBaseHeight
95 // SubscriptShiftDown
96 // SubscriptTopMax
97 // SubscriptBaselineDropMin
98 // SuperscriptShiftUp
99 // SuperscriptShiftUpCramped
100 // SuperscriptBottomMin
102 // SuperscriptBaselineDropMax
103 // SubSuperscriptGapMin
104 // SuperscriptBottomMaxWithSubscript
105 // SpaceAfterScript
106 // UpperLimitGapMin
107 // UpperLimitBaselineRiseMin
108 // LowerLimitGapMin
109 // LowerLimitBaselineDropMin
110 // StackTopShiftUp
111 // StackTopDisplayStyleShiftUp
113 // StackBottomShiftDown
114 // StackBottomDisplayStyleShiftDown
115 // StackGapMin
116 // StackDisplayStyleGapMin
117 // StretchStackTopShiftUp
118 // StretchStackBottomShiftDown
119 // StretchStackGapAboveMin
120 // StretchStackGapBelowMin
121 // FractionNumeratorShiftUp
122 // FractionNumeratorDisplayStyleShiftUp
124 // FractionDenominatorShiftDown
125 // FractionDenominatorDisplayStyleShiftDown
126 // FractionNumeratorGapMin
127 // FractionNumDisplayStyleGapMin
128 // FractionRuleThickness
129 // FractionDenominatorGapMin
130 // FractionDenomDisplayStyleGapMin
131 // SkewedFractionHorizontalGap
132 // SkewedFractionVerticalGap
133 // OverbarVerticalGap
135 // OverbarRuleThickness
136 // OverbarExtraAscender
137 // UnderbarVerticalGap
138 // UnderbarRuleThickness
139 // UnderbarExtraDescender
140 // RadicalVerticalGap
141 // RadicalDisplayStyleVerticalGap
142 // RadicalRuleThickness
143 // RadicalExtraAscender
144 // RadicalKernBeforeDegree
146 // RadicalKernAfterDegree
147 for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
148 if (!ParseMathValueRecord(&subtable, data, length)) {
149 return OTS_FAILURE();
153 // Part 3: uint16 constant
154 // RadicalDegreeBottomRaisePercent
155 if (!subtable.Skip(2)) {
156 return OTS_FAILURE();
159 return true;
162 bool OpenTypeMATH::ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
163 const uint8_t *data,
164 const size_t length,
165 const uint16_t num_glyphs) {
166 // Check the header.
167 uint16_t offset_coverage = 0;
168 uint16_t sequence_count = 0;
169 if (!subtable->ReadU16(&offset_coverage) ||
170 !subtable->ReadU16(&sequence_count)) {
171 return OTS_FAILURE();
174 const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
175 sequence_count * kMathValueRecordSize;
176 if (sequence_end > std::numeric_limits<uint16_t>::max()) {
177 return OTS_FAILURE();
180 // Check coverage table.
181 if (offset_coverage < sequence_end || offset_coverage >= length) {
182 return OTS_FAILURE();
184 if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
185 length - offset_coverage,
186 num_glyphs, sequence_count)) {
187 return OTS_FAILURE();
190 // Check sequence.
191 for (unsigned i = 0; i < sequence_count; ++i) {
192 if (!ParseMathValueRecord(subtable, data, length)) {
193 return OTS_FAILURE();
197 return true;
200 bool OpenTypeMATH::ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
201 size_t length,
202 const uint16_t num_glyphs) {
203 ots::Buffer subtable(data, length);
204 return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
205 num_glyphs);
208 bool OpenTypeMATH::ParseMathTopAccentAttachmentTable(const uint8_t *data,
209 size_t length,
210 const uint16_t num_glyphs) {
211 ots::Buffer subtable(data, length);
212 return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
213 num_glyphs);
216 bool OpenTypeMATH::ParseMathKernTable(const uint8_t *data, size_t length) {
217 ots::Buffer subtable(data, length);
219 // Check the Height count.
220 uint16_t height_count = 0;
221 if (!subtable.ReadU16(&height_count)) {
222 return OTS_FAILURE();
225 // Check the Correction Heights.
226 for (unsigned i = 0; i < height_count; ++i) {
227 if (!ParseMathValueRecord(&subtable, data, length)) {
228 return OTS_FAILURE();
232 // Check the Kern Values.
233 for (unsigned i = 0; i <= height_count; ++i) {
234 if (!ParseMathValueRecord(&subtable, data, length)) {
235 return OTS_FAILURE();
239 return true;
242 bool OpenTypeMATH::ParseMathKernInfoTable(const uint8_t *data,
243 size_t length,
244 const uint16_t num_glyphs) {
245 ots::Buffer subtable(data, length);
247 // Check the header.
248 uint16_t offset_coverage = 0;
249 uint16_t sequence_count = 0;
250 if (!subtable.ReadU16(&offset_coverage) ||
251 !subtable.ReadU16(&sequence_count)) {
252 return OTS_FAILURE();
255 const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
256 sequence_count * 4 * 2;
257 if (sequence_end > std::numeric_limits<uint16_t>::max()) {
258 return OTS_FAILURE();
261 // Check coverage table.
262 if (offset_coverage < sequence_end || offset_coverage >= length) {
263 return OTS_FAILURE();
265 if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage, length - offset_coverage,
266 num_glyphs, sequence_count)) {
267 return OTS_FAILURE();
270 // Check sequence of MathKernInfoRecord
271 for (unsigned i = 0; i < sequence_count; ++i) {
272 // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
273 for (unsigned j = 0; j < 4; ++j) {
274 uint16_t offset_math_kern = 0;
275 if (!subtable.ReadU16(&offset_math_kern)) {
276 return OTS_FAILURE();
278 if (offset_math_kern) {
279 if (offset_math_kern < sequence_end || offset_math_kern >= length ||
280 !ParseMathKernTable(data + offset_math_kern,
281 length - offset_math_kern)) {
282 return OTS_FAILURE();
288 return true;
291 bool OpenTypeMATH::ParseMathGlyphInfoTable(const uint8_t *data,
292 size_t length,
293 const uint16_t num_glyphs) {
294 ots::Buffer subtable(data, length);
296 // Check Header.
297 uint16_t offset_math_italics_correction_info = 0;
298 uint16_t offset_math_top_accent_attachment = 0;
299 uint16_t offset_extended_shaped_coverage = 0;
300 uint16_t offset_math_kern_info = 0;
301 if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
302 !subtable.ReadU16(&offset_math_top_accent_attachment) ||
303 !subtable.ReadU16(&offset_extended_shaped_coverage) ||
304 !subtable.ReadU16(&offset_math_kern_info)) {
305 return OTS_FAILURE();
308 // Check subtables.
309 // The specification does not say whether the offsets for
310 // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
311 // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
312 if (offset_math_italics_correction_info) {
313 if (offset_math_italics_correction_info >= length ||
314 offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
315 !ParseMathItalicsCorrectionInfoTable(
316 data + offset_math_italics_correction_info,
317 length - offset_math_italics_correction_info,
318 num_glyphs)) {
319 return OTS_FAILURE();
322 if (offset_math_top_accent_attachment) {
323 if (offset_math_top_accent_attachment >= length ||
324 offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
325 !ParseMathTopAccentAttachmentTable(data +
326 offset_math_top_accent_attachment,
327 length -
328 offset_math_top_accent_attachment,
329 num_glyphs)) {
330 return OTS_FAILURE();
333 if (offset_extended_shaped_coverage) {
334 if (offset_extended_shaped_coverage >= length ||
335 offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
336 !ots::ParseCoverageTable(GetFont(), data + offset_extended_shaped_coverage,
337 length - offset_extended_shaped_coverage,
338 num_glyphs)) {
339 return OTS_FAILURE();
342 if (offset_math_kern_info) {
343 if (offset_math_kern_info >= length ||
344 offset_math_kern_info < kMathGlyphInfoHeaderSize ||
345 !ParseMathKernInfoTable(data + offset_math_kern_info,
346 length - offset_math_kern_info, num_glyphs)) {
347 return OTS_FAILURE();
351 return true;
354 bool OpenTypeMATH::ParseGlyphAssemblyTable(const uint8_t *data,
355 size_t length,
356 const uint16_t num_glyphs) {
357 ots::Buffer subtable(data, length);
359 // Check the header.
360 uint16_t part_count = 0;
361 if (!ParseMathValueRecord(&subtable, data, length) ||
362 !subtable.ReadU16(&part_count)) {
363 return OTS_FAILURE();
366 const unsigned sequence_end = kMathValueRecordSize +
367 static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
368 if (sequence_end > std::numeric_limits<uint16_t>::max()) {
369 return OTS_FAILURE();
372 // Check the sequence of GlyphPartRecord.
373 for (unsigned i = 0; i < part_count; ++i) {
374 uint16_t glyph = 0;
375 uint16_t part_flags = 0;
376 if (!subtable.ReadU16(&glyph) ||
377 !subtable.Skip(2 * 3) ||
378 !subtable.ReadU16(&part_flags)) {
379 return OTS_FAILURE();
381 if (glyph >= num_glyphs) {
382 return Error("bad glyph ID: %u", glyph);
384 if (part_flags & ~0x00000001) {
385 return Error("unknown part flag: %u", part_flags);
389 return true;
392 bool OpenTypeMATH::ParseMathGlyphConstructionTable(const uint8_t *data,
393 size_t length,
394 const uint16_t num_glyphs) {
395 ots::Buffer subtable(data, length);
397 // Check the header.
398 uint16_t offset_glyph_assembly = 0;
399 uint16_t variant_count = 0;
400 if (!subtable.ReadU16(&offset_glyph_assembly) ||
401 !subtable.ReadU16(&variant_count)) {
402 return OTS_FAILURE();
405 const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
406 variant_count * 2 * 2;
407 if (sequence_end > std::numeric_limits<uint16_t>::max()) {
408 return OTS_FAILURE();
411 // Check the GlyphAssembly offset.
412 if (offset_glyph_assembly) {
413 if (offset_glyph_assembly >= length ||
414 offset_glyph_assembly < sequence_end) {
415 return OTS_FAILURE();
417 if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly,
418 length - offset_glyph_assembly, num_glyphs)) {
419 return OTS_FAILURE();
423 // Check the sequence of MathGlyphVariantRecord.
424 for (unsigned i = 0; i < variant_count; ++i) {
425 uint16_t glyph = 0;
426 if (!subtable.ReadU16(&glyph) ||
427 !subtable.Skip(2)) {
428 return OTS_FAILURE();
430 if (glyph >= num_glyphs) {
431 return Error("bad glyph ID: %u", glyph);
435 return true;
438 bool OpenTypeMATH::ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
439 const uint8_t *data,
440 size_t length,
441 const uint16_t num_glyphs,
442 uint16_t offset_coverage,
443 uint16_t glyph_count,
444 const unsigned sequence_end) {
445 // Zero glyph count, nothing to parse.
446 if (!glyph_count) {
447 return true;
450 // Check coverage table.
451 if (offset_coverage < sequence_end || offset_coverage >= length) {
452 return OTS_FAILURE();
454 if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
455 length - offset_coverage,
456 num_glyphs, glyph_count)) {
457 return OTS_FAILURE();
460 // Check sequence of MathGlyphConstruction.
461 for (unsigned i = 0; i < glyph_count; ++i) {
462 uint16_t offset_glyph_construction = 0;
463 if (!subtable->ReadU16(&offset_glyph_construction)) {
464 return OTS_FAILURE();
466 if (offset_glyph_construction < sequence_end ||
467 offset_glyph_construction >= length ||
468 !ParseMathGlyphConstructionTable(data + offset_glyph_construction,
469 length - offset_glyph_construction,
470 num_glyphs)) {
471 return OTS_FAILURE();
475 return true;
478 bool OpenTypeMATH::ParseMathVariantsTable(const uint8_t *data,
479 size_t length,
480 const uint16_t num_glyphs) {
481 ots::Buffer subtable(data, length);
483 // Check the header.
484 uint16_t offset_vert_glyph_coverage = 0;
485 uint16_t offset_horiz_glyph_coverage = 0;
486 uint16_t vert_glyph_count = 0;
487 uint16_t horiz_glyph_count = 0;
488 if (!subtable.Skip(2) || // MinConnectorOverlap
489 !subtable.ReadU16(&offset_vert_glyph_coverage) ||
490 !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
491 !subtable.ReadU16(&vert_glyph_count) ||
492 !subtable.ReadU16(&horiz_glyph_count)) {
493 return OTS_FAILURE();
496 const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
497 horiz_glyph_count * 2;
498 if (sequence_end > std::numeric_limits<uint16_t>::max()) {
499 return OTS_FAILURE();
502 if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
503 offset_vert_glyph_coverage,
504 vert_glyph_count,
505 sequence_end) ||
506 !ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
507 offset_horiz_glyph_coverage,
508 horiz_glyph_count,
509 sequence_end)) {
510 return OTS_FAILURE();
513 return true;
516 bool OpenTypeMATH::Parse(const uint8_t *data, size_t length) {
517 // Grab the number of glyphs in the font from the maxp table to check
518 // GlyphIDs in MATH table.
519 OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
520 GetFont()->GetTypedTable(OTS_TAG_MAXP));
521 if (!maxp) {
522 return Error("Required maxp table missing");
524 const uint16_t num_glyphs = maxp->num_glyphs;
526 Buffer table(data, length);
528 uint32_t version = 0;
529 if (!table.ReadU32(&version)) {
530 return OTS_FAILURE();
532 if (version != 0x00010000) {
533 return Drop("bad MATH version");
536 uint16_t offset_math_constants = 0;
537 uint16_t offset_math_glyph_info = 0;
538 uint16_t offset_math_variants = 0;
539 if (!table.ReadU16(&offset_math_constants) ||
540 !table.ReadU16(&offset_math_glyph_info) ||
541 !table.ReadU16(&offset_math_variants)) {
542 return OTS_FAILURE();
545 if (offset_math_constants >= length ||
546 offset_math_constants < kMathHeaderSize ||
547 offset_math_glyph_info >= length ||
548 offset_math_glyph_info < kMathHeaderSize ||
549 offset_math_variants >= length ||
550 offset_math_variants < kMathHeaderSize) {
551 return Drop("bad offset in MATH header");
554 if (!ParseMathConstantsTable(data + offset_math_constants,
555 length - offset_math_constants)) {
556 return Drop("failed to parse MathConstants table");
558 if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info,
559 length - offset_math_glyph_info, num_glyphs)) {
560 return Drop("failed to parse MathGlyphInfo table");
562 if (!ParseMathVariantsTable(data + offset_math_variants,
563 length - offset_math_variants, num_glyphs)) {
564 return Drop("failed to parse MathVariants table");
567 this->m_data = data;
568 this->m_length = length;
569 return true;
572 bool OpenTypeMATH::Serialize(OTSStream *out) {
573 if (!out->Write(this->m_data, this->m_length)) {
574 return OTS_FAILURE();
577 return true;
580 bool OpenTypeMATH::ShouldSerialize() {
581 return Table::ShouldSerialize() && this->m_data != NULL;
584 } // namespace ots