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.
5 // We use an underscore to avoid confusion with the standard math.h library.
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.
20 #define TABLE_NAME "MATH"
24 // The size of MATH header.
29 const unsigned kMathHeaderSize
= 4 + 3 * 2;
31 // The size of the MathGlyphInfo header.
32 // MathItalicsCorrectionInfo
33 // MathTopAccentAttachment
34 // ExtendedShapeCoverage
36 const unsigned kMathGlyphInfoHeaderSize
= 4 * 2;
38 // The size of the MathValueRecord.
41 const unsigned kMathValueRecordSize
= 2 * 2;
43 // The size of the GlyphPartRecord.
45 // StartConnectorLength
49 const unsigned kGlyphPartRecordSize
= 5 * 2;
51 // Shared Table: MathValueRecord
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)) {
61 // Check the offset to device table.
63 if (!subtable
->ReadU16(&offset
)) {
67 if (offset
>= length
) {
70 if (!ots::ParseDeviceTable(file
, data
+ offset
, length
- offset
)) {
78 bool ParseMathConstantsTable(const ots::OpenTypeFile
*file
,
79 const uint8_t *data
, size_t length
) {
80 ots::Buffer
subtable(data
, length
);
82 // Part 1: int16 or uint16 constants.
83 // ScriptPercentScaleDown
84 // ScriptScriptPercentScaleDown
85 // DelimitedSubFormulaMinHeight
86 // DisplayOperatorMinHeight
87 if (!subtable
.Skip(4 * 2)) {
91 // Part 2: MathValueRecord constants.
95 // FlattenedAccentBaseHeight
98 // SubscriptBaselineDropMin
100 // SuperscriptShiftUpCramped
101 // SuperscriptBottomMin
103 // SuperscriptBaselineDropMax
104 // SubSuperscriptGapMin
105 // SuperscriptBottomMaxWithSubscript
108 // UpperLimitBaselineRiseMin
110 // LowerLimitBaselineDropMin
112 // StackTopDisplayStyleShiftUp
114 // StackBottomShiftDown
115 // StackBottomDisplayStyleShiftDown
117 // StackDisplayStyleGapMin
118 // StretchStackTopShiftUp
119 // StretchStackBottomShiftDown
120 // StretchStackGapAboveMin
121 // StretchStackGapBelowMin
122 // FractionNumeratorShiftUp
123 // FractionNumeratorDisplayStyleShiftUp
125 // FractionDenominatorShiftDown
126 // FractionDenominatorDisplayStyleShiftDown
127 // FractionNumeratorGapMin
128 // FractionNumDisplayStyleGapMin
129 // FractionRuleThickness
130 // FractionDenominatorGapMin
131 // FractionDenomDisplayStyleGapMin
132 // SkewedFractionHorizontalGap
133 // SkewedFractionVerticalGap
134 // OverbarVerticalGap
136 // OverbarRuleThickness
137 // OverbarExtraAscender
138 // UnderbarVerticalGap
139 // UnderbarRuleThickness
140 // UnderbarExtraDescender
141 // RadicalVerticalGap
142 // RadicalDisplayStyleVerticalGap
143 // RadicalRuleThickness
144 // RadicalExtraAscender
145 // RadicalKernBeforeDegree
147 // RadicalKernAfterDegree
148 for (unsigned i
= 0; i
< static_cast<unsigned>(51); ++i
) {
149 if (!ParseMathValueRecord(file
, &subtable
, data
, length
)) {
150 return OTS_FAILURE();
154 // Part 3: uint16 constant
155 // RadicalDegreeBottomRaisePercent
156 if (!subtable
.Skip(2)) {
157 return OTS_FAILURE();
163 bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile
*file
,
164 ots::Buffer
* subtable
,
167 const uint16_t num_glyphs
) {
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();
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();
182 // Check coverage table.
183 if (offset_coverage
< sequence_end
|| offset_coverage
>= length
) {
184 return OTS_FAILURE();
186 if (!ots::ParseCoverageTable(file
, data
+ offset_coverage
,
187 length
- offset_coverage
,
188 num_glyphs
, sequence_count
)) {
189 return OTS_FAILURE();
193 for (unsigned i
= 0; i
< sequence_count
; ++i
) {
194 if (!ParseMathValueRecord(file
, subtable
, data
, length
)) {
195 return OTS_FAILURE();
202 bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile
*file
,
205 const uint16_t num_glyphs
) {
206 ots::Buffer
subtable(data
, length
);
207 return ParseMathValueRecordSequenceForGlyphs(file
, &subtable
, data
, length
,
211 bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile
*file
,
214 const uint16_t num_glyphs
) {
215 ots::Buffer
subtable(data
, length
);
216 return ParseMathValueRecordSequenceForGlyphs(file
, &subtable
, data
, length
,
220 bool ParseMathKernTable(const ots::OpenTypeFile
*file
,
221 const uint8_t *data
, size_t length
) {
222 ots::Buffer
subtable(data
, length
);
224 // Check the Height count.
225 uint16_t height_count
= 0;
226 if (!subtable
.ReadU16(&height_count
)) {
227 return OTS_FAILURE();
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();
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();
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
);
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();
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();
266 // Check coverage table.
267 if (offset_coverage
< sequence_end
|| offset_coverage
>= length
) {
268 return OTS_FAILURE();
270 if (!ots::ParseCoverageTable(file
, data
+ offset_coverage
, length
- offset_coverage
,
271 num_glyphs
, sequence_count
)) {
272 return OTS_FAILURE();
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();
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();
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
);
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();
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
,
324 return OTS_FAILURE();
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
,
333 offset_math_top_accent_attachment
,
335 return OTS_FAILURE();
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
,
344 return OTS_FAILURE();
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();
359 bool ParseGlyphAssemblyTable(const ots::OpenTypeFile
*file
,
361 size_t length
, const uint16_t num_glyphs
) {
362 ots::Buffer
subtable(data
, length
);
365 uint16_t part_count
= 0;
366 if (!ParseMathValueRecord(file
, &subtable
, data
, length
) ||
367 !subtable
.ReadU16(&part_count
)) {
368 return OTS_FAILURE();
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();
377 // Check the sequence of GlyphPartRecord.
378 for (unsigned i
= 0; i
< part_count
; ++i
) {
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();
386 if (glyph
>= num_glyphs
) {
387 return OTS_FAILURE_MSG("bad glyph ID: %u", glyph
);
389 if (part_flags
& ~0x00000001) {
390 return OTS_FAILURE_MSG("unknown part flag: %u", part_flags
);
397 bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile
*file
,
399 size_t length
, const uint16_t num_glyphs
) {
400 ots::Buffer
subtable(data
, length
);
403 uint16_t offset_glyph_assembly
= 0;
404 uint16_t variant_count
= 0;
405 if (!subtable
.ReadU16(&offset_glyph_assembly
) ||
406 !subtable
.ReadU16(&variant_count
)) {
407 return OTS_FAILURE();
410 const unsigned sequence_end
= static_cast<unsigned>(2 * 2) +
411 variant_count
* 2 * 2;
412 if (sequence_end
> std::numeric_limits
<uint16_t>::max()) {
413 return OTS_FAILURE();
416 // Check the GlyphAssembly offset.
417 if (offset_glyph_assembly
) {
418 if (offset_glyph_assembly
>= length
||
419 offset_glyph_assembly
< sequence_end
) {
420 return OTS_FAILURE();
422 if (!ParseGlyphAssemblyTable(file
, data
+ offset_glyph_assembly
,
423 length
- offset_glyph_assembly
, num_glyphs
)) {
424 return OTS_FAILURE();
428 // Check the sequence of MathGlyphVariantRecord.
429 for (unsigned i
= 0; i
< variant_count
; ++i
) {
431 if (!subtable
.ReadU16(&glyph
) ||
433 return OTS_FAILURE();
435 if (glyph
>= num_glyphs
) {
436 return OTS_FAILURE_MSG("bad glyph ID: %u", glyph
);
443 bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile
*file
,
444 ots::Buffer
* subtable
,
447 const uint16_t num_glyphs
,
448 uint16_t offset_coverage
,
449 uint16_t glyph_count
,
450 const unsigned sequence_end
) {
451 // Check coverage table.
452 if (offset_coverage
< sequence_end
|| offset_coverage
>= length
) {
453 return OTS_FAILURE();
455 if (!ots::ParseCoverageTable(file
, data
+ offset_coverage
,
456 length
- offset_coverage
,
457 num_glyphs
, glyph_count
)) {
458 return OTS_FAILURE();
461 // Check sequence of MathGlyphConstruction.
462 for (unsigned i
= 0; i
< glyph_count
; ++i
) {
463 uint16_t offset_glyph_construction
= 0;
464 if (!subtable
->ReadU16(&offset_glyph_construction
)) {
465 return OTS_FAILURE();
467 if (offset_glyph_construction
< sequence_end
||
468 offset_glyph_construction
>= length
||
469 !ParseMathGlyphConstructionTable(file
, data
+ offset_glyph_construction
,
470 length
- offset_glyph_construction
,
472 return OTS_FAILURE();
479 bool ParseMathVariantsTable(const ots::OpenTypeFile
*file
,
481 size_t length
, const uint16_t num_glyphs
) {
482 ots::Buffer
subtable(data
, length
);
485 uint16_t offset_vert_glyph_coverage
= 0;
486 uint16_t offset_horiz_glyph_coverage
= 0;
487 uint16_t vert_glyph_count
= 0;
488 uint16_t horiz_glyph_count
= 0;
489 if (!subtable
.Skip(2) || // MinConnectorOverlap
490 !subtable
.ReadU16(&offset_vert_glyph_coverage
) ||
491 !subtable
.ReadU16(&offset_horiz_glyph_coverage
) ||
492 !subtable
.ReadU16(&vert_glyph_count
) ||
493 !subtable
.ReadU16(&horiz_glyph_count
)) {
494 return OTS_FAILURE();
497 const unsigned sequence_end
= 5 * 2 + vert_glyph_count
* 2 +
498 horiz_glyph_count
* 2;
499 if (sequence_end
> std::numeric_limits
<uint16_t>::max()) {
500 return OTS_FAILURE();
503 if (!ParseMathGlyphConstructionSequence(file
, &subtable
, data
, length
, num_glyphs
,
504 offset_vert_glyph_coverage
,
507 !ParseMathGlyphConstructionSequence(file
, &subtable
, data
, length
, num_glyphs
,
508 offset_horiz_glyph_coverage
,
511 return OTS_FAILURE();
519 #define DROP_THIS_TABLE(msg_) \
521 OTS_FAILURE_MSG(msg_ ", table discarded"); \
522 file->math->data = 0; \
523 file->math->length = 0; \
528 bool ots_math_parse(OpenTypeFile
*file
, const uint8_t *data
, size_t length
) {
529 // Grab the number of glyphs in the file from the maxp table to check
530 // GlyphIDs in MATH table.
532 return OTS_FAILURE();
534 const uint16_t num_glyphs
= file
->maxp
->num_glyphs
;
536 Buffer
table(data
, length
);
538 OpenTypeMATH
* math
= new OpenTypeMATH
;
541 uint32_t version
= 0;
542 if (!table
.ReadU32(&version
)) {
543 return OTS_FAILURE();
545 if (version
!= 0x00010000) {
546 DROP_THIS_TABLE("bad MATH version");
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();
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 DROP_THIS_TABLE("bad offset in MATH header");
569 if (!ParseMathConstantsTable(file
, data
+ offset_math_constants
,
570 length
- offset_math_constants
)) {
571 DROP_THIS_TABLE("failed to parse MathConstants table");
574 if (!ParseMathGlyphInfoTable(file
, data
+ offset_math_glyph_info
,
575 length
- offset_math_glyph_info
, num_glyphs
)) {
576 DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
579 if (!ParseMathVariantsTable(file
, data
+ offset_math_variants
,
580 length
- offset_math_variants
, num_glyphs
)) {
581 DROP_THIS_TABLE("failed to parse MathVariants table");
586 math
->length
= length
;
590 bool ots_math_should_serialise(OpenTypeFile
*file
) {
591 return file
->math
!= NULL
&& file
->math
->data
!= NULL
;
594 bool ots_math_serialise(OTSStream
*out
, OpenTypeFile
*file
) {
595 if (!out
->Write(file
->math
->data
, file
->math
->length
)) {
596 return OTS_FAILURE();
602 void ots_math_free(OpenTypeFile
*file
) {
609 #undef DROP_THIS_TABLE