Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / ots / src / math.cc
blob9124a88c512de0492566811a8463f407a65e64bd
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.
6 #include "math_.h"
8 #include <limits>
9 #include <vector>
11 #include "layout.h"
12 #include "maxp.h"
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"
22 namespace {
24 // The size of MATH header.
25 // Version
26 // MathConstants
27 // MathGlyphInfo
28 // MathVariants
29 const unsigned kMathHeaderSize = 4 + 3 * 2;
31 // The size of the MathGlyphInfo header.
32 // MathItalicsCorrectionInfo
33 // MathTopAccentAttachment
34 // ExtendedShapeCoverage
35 // MathKernInfo
36 const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
38 // The size of the MathValueRecord.
39 // Value
40 // DeviceTable
41 const unsigned kMathValueRecordSize = 2 * 2;
43 // The size of the GlyphPartRecord.
44 // glyph
45 // StartConnectorLength
46 // EndConnectorLength
47 // FullAdvance
48 // PartFlags
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)) {
58 return OTS_FAILURE();
61 // Check the offset to device table.
62 uint16_t offset = 0;
63 if (!subtable->ReadU16(&offset)) {
64 return OTS_FAILURE();
66 if (offset) {
67 if (offset >= length) {
68 return OTS_FAILURE();
70 if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
71 return OTS_FAILURE();
75 return true;
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)) {
88 return OTS_FAILURE();
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
103 // SuperscriptBaselineDropMax
104 // SubSuperscriptGapMin
105 // SuperscriptBottomMaxWithSubscript
106 // SpaceAfterScript
107 // UpperLimitGapMin
108 // UpperLimitBaselineRiseMin
109 // LowerLimitGapMin
110 // LowerLimitBaselineDropMin
111 // StackTopShiftUp
112 // StackTopDisplayStyleShiftUp
114 // StackBottomShiftDown
115 // StackBottomDisplayStyleShiftDown
116 // StackGapMin
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();
160 return true;
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();
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();
192 // Check sequence.
193 for (unsigned i = 0; i < sequence_count; ++i) {
194 if (!ParseMathValueRecord(file, subtable, data, length)) {
195 return OTS_FAILURE();
199 return true;
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);
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);
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();
244 return true;
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);
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();
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();
293 return true;
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);
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();
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();
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();
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();
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();
356 return true;
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);
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();
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) {
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();
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);
394 return true;
397 bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file,
398 const uint8_t *data,
399 size_t length, const uint16_t num_glyphs) {
400 ots::Buffer subtable(data, length);
402 // Check the header.
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) {
430 uint16_t glyph = 0;
431 if (!subtable.ReadU16(&glyph) ||
432 !subtable.Skip(2)) {
433 return OTS_FAILURE();
435 if (glyph >= num_glyphs) {
436 return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
440 return true;
443 bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file,
444 ots::Buffer* subtable,
445 const uint8_t *data,
446 size_t length,
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,
471 num_glyphs)) {
472 return OTS_FAILURE();
476 return true;
479 bool ParseMathVariantsTable(const ots::OpenTypeFile *file,
480 const uint8_t *data,
481 size_t length, const uint16_t num_glyphs) {
482 ots::Buffer subtable(data, length);
484 // Check the header.
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,
505 vert_glyph_count,
506 sequence_end) ||
507 !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
508 offset_horiz_glyph_coverage,
509 horiz_glyph_count,
510 sequence_end)) {
511 return OTS_FAILURE();
514 return true;
517 } // namespace
519 #define DROP_THIS_TABLE(msg_) \
520 do { \
521 OTS_FAILURE_MSG(msg_ ", table discarded"); \
522 file->math->data = 0; \
523 file->math->length = 0; \
524 } while (0)
526 namespace ots {
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.
531 if (!file->maxp) {
532 return OTS_FAILURE();
534 const uint16_t num_glyphs = file->maxp->num_glyphs;
536 Buffer table(data, length);
538 OpenTypeMATH* math = new OpenTypeMATH;
539 file->math = math;
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");
547 return true;
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");
566 return true;
569 if (!ParseMathConstantsTable(file, data + offset_math_constants,
570 length - offset_math_constants)) {
571 DROP_THIS_TABLE("failed to parse MathConstants table");
572 return true;
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");
577 return true;
579 if (!ParseMathVariantsTable(file, data + offset_math_variants,
580 length - offset_math_variants, num_glyphs)) {
581 DROP_THIS_TABLE("failed to parse MathVariants table");
582 return true;
585 math->data = data;
586 math->length = length;
587 return true;
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();
599 return true;
602 void ots_math_free(OpenTypeFile *file) {
603 delete file->math;
606 } // namespace ots
608 #undef TABLE_NAME
609 #undef DROP_THIS_TABLE