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.
14 // MATH - The MATH Table
15 // http://www.microsoft.com/typography/otspec/math.htm
19 // The size of MATH header.
24 const unsigned kMathHeaderSize
= 4 + 3 * 2;
26 // The size of the MathGlyphInfo header.
27 // MathItalicsCorrectionInfo
28 // MathTopAccentAttachment
29 // ExtendedShapeCoverage
31 const unsigned kMathGlyphInfoHeaderSize
= 4 * 2;
33 // The size of the MathValueRecord.
36 const unsigned kMathValueRecordSize
= 2 * 2;
38 // The size of the GlyphPartRecord.
40 // StartConnectorLength
44 const unsigned kGlyphPartRecordSize
= 5 * 2;
50 // Shared Table: MathValueRecord
52 bool OpenTypeMATH::ParseMathValueRecord(ots::Buffer
* subtable
,
54 const size_t length
) {
55 // Check the Value field.
56 if (!subtable
->Skip(2)) {
60 // Check the offset to device table.
62 if (!subtable
->ReadU16(&offset
)) {
66 if (offset
>= length
) {
69 if (!ots::ParseDeviceTable(GetFont(), data
+ offset
, length
- offset
)) {
77 bool OpenTypeMATH::ParseMathConstantsTable(const uint8_t *data
,
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)) {
90 // Part 2: MathValueRecord constants.
94 // FlattenedAccentBaseHeight
97 // SubscriptBaselineDropMin
99 // SuperscriptShiftUpCramped
100 // SuperscriptBottomMin
102 // SuperscriptBaselineDropMax
103 // SubSuperscriptGapMin
104 // SuperscriptBottomMaxWithSubscript
107 // UpperLimitBaselineRiseMin
109 // LowerLimitBaselineDropMin
111 // StackTopDisplayStyleShiftUp
113 // StackBottomShiftDown
114 // StackBottomDisplayStyleShiftDown
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();
162 bool OpenTypeMATH::ParseMathValueRecordSequenceForGlyphs(ots::Buffer
* subtable
,
165 const uint16_t num_glyphs
) {
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();
191 for (unsigned i
= 0; i
< sequence_count
; ++i
) {
192 if (!ParseMathValueRecord(subtable
, data
, length
)) {
193 return OTS_FAILURE();
200 bool OpenTypeMATH::ParseMathItalicsCorrectionInfoTable(const uint8_t *data
,
202 const uint16_t num_glyphs
) {
203 ots::Buffer
subtable(data
, length
);
204 return ParseMathValueRecordSequenceForGlyphs(&subtable
, data
, length
,
208 bool OpenTypeMATH::ParseMathTopAccentAttachmentTable(const uint8_t *data
,
210 const uint16_t num_glyphs
) {
211 ots::Buffer
subtable(data
, length
);
212 return ParseMathValueRecordSequenceForGlyphs(&subtable
, data
, length
,
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();
242 bool OpenTypeMATH::ParseMathKernInfoTable(const uint8_t *data
,
244 const uint16_t num_glyphs
) {
245 ots::Buffer
subtable(data
, length
);
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();
291 bool OpenTypeMATH::ParseMathGlyphInfoTable(const uint8_t *data
,
293 const uint16_t num_glyphs
) {
294 ots::Buffer
subtable(data
, length
);
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();
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
,
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
,
328 offset_math_top_accent_attachment
,
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
,
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();
354 bool OpenTypeMATH::ParseGlyphAssemblyTable(const uint8_t *data
,
356 const uint16_t num_glyphs
) {
357 ots::Buffer
subtable(data
, length
);
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
) {
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
);
392 bool OpenTypeMATH::ParseMathGlyphConstructionTable(const uint8_t *data
,
394 const uint16_t num_glyphs
) {
395 ots::Buffer
subtable(data
, length
);
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
) {
426 if (!subtable
.ReadU16(&glyph
) ||
428 return OTS_FAILURE();
430 if (glyph
>= num_glyphs
) {
431 return Error("bad glyph ID: %u", glyph
);
438 bool OpenTypeMATH::ParseMathGlyphConstructionSequence(ots::Buffer
* subtable
,
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.
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
,
471 return OTS_FAILURE();
478 bool OpenTypeMATH::ParseMathVariantsTable(const uint8_t *data
,
480 const uint16_t num_glyphs
) {
481 ots::Buffer
subtable(data
, length
);
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
,
506 !ParseMathGlyphConstructionSequence(&subtable
, data
, length
, num_glyphs
,
507 offset_horiz_glyph_coverage
,
510 return OTS_FAILURE();
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
));
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");
568 this->m_length
= length
;
572 bool OpenTypeMATH::Serialize(OTSStream
*out
) {
573 if (!out
->Write(this->m_data
, this->m_length
)) {
574 return OTS_FAILURE();
580 bool OpenTypeMATH::ShouldSerialize() {
581 return Table::ShouldSerialize() && this->m_data
!= NULL
;