1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/ots/src/math.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,608 @@ 1.4 +// Copyright (c) 2014 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +// We use an underscore to avoid confusion with the standard math.h library. 1.9 +#include "math_.h" 1.10 + 1.11 +#include <limits> 1.12 +#include <vector> 1.13 + 1.14 +#include "layout.h" 1.15 +#include "maxp.h" 1.16 + 1.17 +// MATH - The MATH Table 1.18 +// The specification is not yet public but has been submitted to the MPEG group 1.19 +// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font 1.20 +// Format" Color Font Technology and MATH layout support'. Meanwhile, you can 1.21 +// contact Microsoft's engineer Murray Sargent to obtain a copy. 1.22 + 1.23 +#define TABLE_NAME "MATH" 1.24 + 1.25 +namespace { 1.26 + 1.27 +// The size of MATH header. 1.28 +// Version 1.29 +// MathConstants 1.30 +// MathGlyphInfo 1.31 +// MathVariants 1.32 +const unsigned kMathHeaderSize = 4 + 3 * 2; 1.33 + 1.34 +// The size of the MathGlyphInfo header. 1.35 +// MathItalicsCorrectionInfo 1.36 +// MathTopAccentAttachment 1.37 +// ExtendedShapeCoverage 1.38 +// MathKernInfo 1.39 +const unsigned kMathGlyphInfoHeaderSize = 4 * 2; 1.40 + 1.41 +// The size of the MathValueRecord. 1.42 +// Value 1.43 +// DeviceTable 1.44 +const unsigned kMathValueRecordSize = 2 * 2; 1.45 + 1.46 +// The size of the GlyphPartRecord. 1.47 +// glyph 1.48 +// StartConnectorLength 1.49 +// EndConnectorLength 1.50 +// FullAdvance 1.51 +// PartFlags 1.52 +const unsigned kGlyphPartRecordSize = 5 * 2; 1.53 + 1.54 +// Shared Table: MathValueRecord 1.55 + 1.56 +bool ParseMathValueRecord(const ots::OpenTypeFile *file, 1.57 + ots::Buffer* subtable, const uint8_t *data, 1.58 + const size_t length) { 1.59 + // Check the Value field. 1.60 + if (!subtable->Skip(2)) { 1.61 + return OTS_FAILURE(); 1.62 + } 1.63 + 1.64 + // Check the offset to device table. 1.65 + uint16_t offset = 0; 1.66 + if (!subtable->ReadU16(&offset)) { 1.67 + return OTS_FAILURE(); 1.68 + } 1.69 + if (offset) { 1.70 + if (offset >= length) { 1.71 + return OTS_FAILURE(); 1.72 + } 1.73 + if (!ots::ParseDeviceTable(file, data + offset, length - offset)) { 1.74 + return OTS_FAILURE(); 1.75 + } 1.76 + } 1.77 + 1.78 + return true; 1.79 +} 1.80 + 1.81 +bool ParseMathConstantsTable(const ots::OpenTypeFile *file, 1.82 + const uint8_t *data, size_t length) { 1.83 + ots::Buffer subtable(data, length); 1.84 + 1.85 + // Part 1: int16 or uint16 constants. 1.86 + // ScriptPercentScaleDown 1.87 + // ScriptScriptPercentScaleDown 1.88 + // DelimitedSubFormulaMinHeight 1.89 + // DisplayOperatorMinHeight 1.90 + if (!subtable.Skip(4 * 2)) { 1.91 + return OTS_FAILURE(); 1.92 + } 1.93 + 1.94 + // Part 2: MathValueRecord constants. 1.95 + // MathLeading 1.96 + // AxisHeight 1.97 + // AccentBaseHeight 1.98 + // FlattenedAccentBaseHeight 1.99 + // SubscriptShiftDown 1.100 + // SubscriptTopMax 1.101 + // SubscriptBaselineDropMin 1.102 + // SuperscriptShiftUp 1.103 + // SuperscriptShiftUpCramped 1.104 + // SuperscriptBottomMin 1.105 + // 1.106 + // SuperscriptBaselineDropMax 1.107 + // SubSuperscriptGapMin 1.108 + // SuperscriptBottomMaxWithSubscript 1.109 + // SpaceAfterScript 1.110 + // UpperLimitGapMin 1.111 + // UpperLimitBaselineRiseMin 1.112 + // LowerLimitGapMin 1.113 + // LowerLimitBaselineDropMin 1.114 + // StackTopShiftUp 1.115 + // StackTopDisplayStyleShiftUp 1.116 + // 1.117 + // StackBottomShiftDown 1.118 + // StackBottomDisplayStyleShiftDown 1.119 + // StackGapMin 1.120 + // StackDisplayStyleGapMin 1.121 + // StretchStackTopShiftUp 1.122 + // StretchStackBottomShiftDown 1.123 + // StretchStackGapAboveMin 1.124 + // StretchStackGapBelowMin 1.125 + // FractionNumeratorShiftUp 1.126 + // FractionNumeratorDisplayStyleShiftUp 1.127 + // 1.128 + // FractionDenominatorShiftDown 1.129 + // FractionDenominatorDisplayStyleShiftDown 1.130 + // FractionNumeratorGapMin 1.131 + // FractionNumDisplayStyleGapMin 1.132 + // FractionRuleThickness 1.133 + // FractionDenominatorGapMin 1.134 + // FractionDenomDisplayStyleGapMin 1.135 + // SkewedFractionHorizontalGap 1.136 + // SkewedFractionVerticalGap 1.137 + // OverbarVerticalGap 1.138 + // 1.139 + // OverbarRuleThickness 1.140 + // OverbarExtraAscender 1.141 + // UnderbarVerticalGap 1.142 + // UnderbarRuleThickness 1.143 + // UnderbarExtraDescender 1.144 + // RadicalVerticalGap 1.145 + // RadicalDisplayStyleVerticalGap 1.146 + // RadicalRuleThickness 1.147 + // RadicalExtraAscender 1.148 + // RadicalKernBeforeDegree 1.149 + // 1.150 + // RadicalKernAfterDegree 1.151 + for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) { 1.152 + if (!ParseMathValueRecord(file, &subtable, data, length)) { 1.153 + return OTS_FAILURE(); 1.154 + } 1.155 + } 1.156 + 1.157 + // Part 3: uint16 constant 1.158 + // RadicalDegreeBottomRaisePercent 1.159 + if (!subtable.Skip(2)) { 1.160 + return OTS_FAILURE(); 1.161 + } 1.162 + 1.163 + return true; 1.164 +} 1.165 + 1.166 +bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile *file, 1.167 + ots::Buffer* subtable, 1.168 + const uint8_t *data, 1.169 + const size_t length, 1.170 + const uint16_t num_glyphs) { 1.171 + // Check the header. 1.172 + uint16_t offset_coverage = 0; 1.173 + uint16_t sequence_count = 0; 1.174 + if (!subtable->ReadU16(&offset_coverage) || 1.175 + !subtable->ReadU16(&sequence_count)) { 1.176 + return OTS_FAILURE(); 1.177 + } 1.178 + 1.179 + const unsigned sequence_end = static_cast<unsigned>(2 * 2) + 1.180 + sequence_count * kMathValueRecordSize; 1.181 + if (sequence_end > std::numeric_limits<uint16_t>::max()) { 1.182 + return OTS_FAILURE(); 1.183 + } 1.184 + 1.185 + // Check coverage table. 1.186 + if (offset_coverage < sequence_end || offset_coverage >= length) { 1.187 + return OTS_FAILURE(); 1.188 + } 1.189 + if (!ots::ParseCoverageTable(file, data + offset_coverage, 1.190 + length - offset_coverage, 1.191 + num_glyphs, sequence_count)) { 1.192 + return OTS_FAILURE(); 1.193 + } 1.194 + 1.195 + // Check sequence. 1.196 + for (unsigned i = 0; i < sequence_count; ++i) { 1.197 + if (!ParseMathValueRecord(file, subtable, data, length)) { 1.198 + return OTS_FAILURE(); 1.199 + } 1.200 + } 1.201 + 1.202 + return true; 1.203 +} 1.204 + 1.205 +bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile *file, 1.206 + const uint8_t *data, 1.207 + size_t length, 1.208 + const uint16_t num_glyphs) { 1.209 + ots::Buffer subtable(data, length); 1.210 + return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length, 1.211 + num_glyphs); 1.212 +} 1.213 + 1.214 +bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile *file, 1.215 + const uint8_t *data, 1.216 + size_t length, 1.217 + const uint16_t num_glyphs) { 1.218 + ots::Buffer subtable(data, length); 1.219 + return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length, 1.220 + num_glyphs); 1.221 +} 1.222 + 1.223 +bool ParseMathKernTable(const ots::OpenTypeFile *file, 1.224 + const uint8_t *data, size_t length) { 1.225 + ots::Buffer subtable(data, length); 1.226 + 1.227 + // Check the Height count. 1.228 + uint16_t height_count = 0; 1.229 + if (!subtable.ReadU16(&height_count)) { 1.230 + return OTS_FAILURE(); 1.231 + } 1.232 + 1.233 + // Check the Correction Heights. 1.234 + for (unsigned i = 0; i < height_count; ++i) { 1.235 + if (!ParseMathValueRecord(file, &subtable, data, length)) { 1.236 + return OTS_FAILURE(); 1.237 + } 1.238 + } 1.239 + 1.240 + // Check the Kern Values. 1.241 + for (unsigned i = 0; i <= height_count; ++i) { 1.242 + if (!ParseMathValueRecord(file, &subtable, data, length)) { 1.243 + return OTS_FAILURE(); 1.244 + } 1.245 + } 1.246 + 1.247 + return true; 1.248 +} 1.249 + 1.250 +bool ParseMathKernInfoTable(const ots::OpenTypeFile *file, 1.251 + const uint8_t *data, size_t length, 1.252 + const uint16_t num_glyphs) { 1.253 + ots::Buffer subtable(data, length); 1.254 + 1.255 + // Check the header. 1.256 + uint16_t offset_coverage = 0; 1.257 + uint16_t sequence_count = 0; 1.258 + if (!subtable.ReadU16(&offset_coverage) || 1.259 + !subtable.ReadU16(&sequence_count)) { 1.260 + return OTS_FAILURE(); 1.261 + } 1.262 + 1.263 + const unsigned sequence_end = static_cast<unsigned>(2 * 2) + 1.264 + sequence_count * 4 * 2; 1.265 + if (sequence_end > std::numeric_limits<uint16_t>::max()) { 1.266 + return OTS_FAILURE(); 1.267 + } 1.268 + 1.269 + // Check coverage table. 1.270 + if (offset_coverage < sequence_end || offset_coverage >= length) { 1.271 + return OTS_FAILURE(); 1.272 + } 1.273 + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, 1.274 + num_glyphs, sequence_count)) { 1.275 + return OTS_FAILURE(); 1.276 + } 1.277 + 1.278 + // Check sequence of MathKernInfoRecord 1.279 + for (unsigned i = 0; i < sequence_count; ++i) { 1.280 + // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern. 1.281 + for (unsigned j = 0; j < 4; ++j) { 1.282 + uint16_t offset_math_kern = 0; 1.283 + if (!subtable.ReadU16(&offset_math_kern)) { 1.284 + return OTS_FAILURE(); 1.285 + } 1.286 + if (offset_math_kern) { 1.287 + if (offset_math_kern < sequence_end || offset_math_kern >= length || 1.288 + !ParseMathKernTable(file, data + offset_math_kern, 1.289 + length - offset_math_kern)) { 1.290 + return OTS_FAILURE(); 1.291 + } 1.292 + } 1.293 + } 1.294 + } 1.295 + 1.296 + return true; 1.297 +} 1.298 + 1.299 +bool ParseMathGlyphInfoTable(const ots::OpenTypeFile *file, 1.300 + const uint8_t *data, size_t length, 1.301 + const uint16_t num_glyphs) { 1.302 + ots::Buffer subtable(data, length); 1.303 + 1.304 + // Check Header. 1.305 + uint16_t offset_math_italics_correction_info = 0; 1.306 + uint16_t offset_math_top_accent_attachment = 0; 1.307 + uint16_t offset_extended_shaped_coverage = 0; 1.308 + uint16_t offset_math_kern_info = 0; 1.309 + if (!subtable.ReadU16(&offset_math_italics_correction_info) || 1.310 + !subtable.ReadU16(&offset_math_top_accent_attachment) || 1.311 + !subtable.ReadU16(&offset_extended_shaped_coverage) || 1.312 + !subtable.ReadU16(&offset_math_kern_info)) { 1.313 + return OTS_FAILURE(); 1.314 + } 1.315 + 1.316 + // Check subtables. 1.317 + // The specification does not say whether the offsets for 1.318 + // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may 1.319 + // be NULL, but that's the case in some fonts (e.g STIX) so we accept that. 1.320 + if (offset_math_italics_correction_info) { 1.321 + if (offset_math_italics_correction_info >= length || 1.322 + offset_math_italics_correction_info < kMathGlyphInfoHeaderSize || 1.323 + !ParseMathItalicsCorrectionInfoTable( 1.324 + file, data + offset_math_italics_correction_info, 1.325 + length - offset_math_italics_correction_info, 1.326 + num_glyphs)) { 1.327 + return OTS_FAILURE(); 1.328 + } 1.329 + } 1.330 + if (offset_math_top_accent_attachment) { 1.331 + if (offset_math_top_accent_attachment >= length || 1.332 + offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize || 1.333 + !ParseMathTopAccentAttachmentTable(file, data + 1.334 + offset_math_top_accent_attachment, 1.335 + length - 1.336 + offset_math_top_accent_attachment, 1.337 + num_glyphs)) { 1.338 + return OTS_FAILURE(); 1.339 + } 1.340 + } 1.341 + if (offset_extended_shaped_coverage) { 1.342 + if (offset_extended_shaped_coverage >= length || 1.343 + offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize || 1.344 + !ots::ParseCoverageTable(file, data + offset_extended_shaped_coverage, 1.345 + length - offset_extended_shaped_coverage, 1.346 + num_glyphs)) { 1.347 + return OTS_FAILURE(); 1.348 + } 1.349 + } 1.350 + if (offset_math_kern_info) { 1.351 + if (offset_math_kern_info >= length || 1.352 + offset_math_kern_info < kMathGlyphInfoHeaderSize || 1.353 + !ParseMathKernInfoTable(file, data + offset_math_kern_info, 1.354 + length - offset_math_kern_info, num_glyphs)) { 1.355 + return OTS_FAILURE(); 1.356 + } 1.357 + } 1.358 + 1.359 + return true; 1.360 +} 1.361 + 1.362 +bool ParseGlyphAssemblyTable(const ots::OpenTypeFile *file, 1.363 + const uint8_t *data, 1.364 + size_t length, const uint16_t num_glyphs) { 1.365 + ots::Buffer subtable(data, length); 1.366 + 1.367 + // Check the header. 1.368 + uint16_t part_count = 0; 1.369 + if (!ParseMathValueRecord(file, &subtable, data, length) || 1.370 + !subtable.ReadU16(&part_count)) { 1.371 + return OTS_FAILURE(); 1.372 + } 1.373 + 1.374 + const unsigned sequence_end = kMathValueRecordSize + 1.375 + static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize; 1.376 + if (sequence_end > std::numeric_limits<uint16_t>::max()) { 1.377 + return OTS_FAILURE(); 1.378 + } 1.379 + 1.380 + // Check the sequence of GlyphPartRecord. 1.381 + for (unsigned i = 0; i < part_count; ++i) { 1.382 + uint16_t glyph = 0; 1.383 + uint16_t part_flags = 0; 1.384 + if (!subtable.ReadU16(&glyph) || 1.385 + !subtable.Skip(2 * 3) || 1.386 + !subtable.ReadU16(&part_flags)) { 1.387 + return OTS_FAILURE(); 1.388 + } 1.389 + if (glyph >= num_glyphs) { 1.390 + OTS_WARNING("bad glyph ID: %u", glyph); 1.391 + return OTS_FAILURE(); 1.392 + } 1.393 + if (part_flags & ~0x00000001) { 1.394 + OTS_WARNING("unknown part flag: %u", part_flags); 1.395 + return OTS_FAILURE(); 1.396 + } 1.397 + } 1.398 + 1.399 + return true; 1.400 +} 1.401 + 1.402 +bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file, 1.403 + const uint8_t *data, 1.404 + size_t length, const uint16_t num_glyphs) { 1.405 + ots::Buffer subtable(data, length); 1.406 + 1.407 + // Check the header. 1.408 + uint16_t offset_glyph_assembly = 0; 1.409 + uint16_t variant_count = 0; 1.410 + if (!subtable.ReadU16(&offset_glyph_assembly) || 1.411 + !subtable.ReadU16(&variant_count)) { 1.412 + return OTS_FAILURE(); 1.413 + } 1.414 + 1.415 + const unsigned sequence_end = static_cast<unsigned>(2 * 2) + 1.416 + variant_count * 2 * 2; 1.417 + if (sequence_end > std::numeric_limits<uint16_t>::max()) { 1.418 + return OTS_FAILURE(); 1.419 + } 1.420 + 1.421 + // Check the GlyphAssembly offset. 1.422 + if (offset_glyph_assembly) { 1.423 + if (offset_glyph_assembly >= length || 1.424 + offset_glyph_assembly < sequence_end) { 1.425 + return OTS_FAILURE(); 1.426 + } 1.427 + if (!ParseGlyphAssemblyTable(file, data + offset_glyph_assembly, 1.428 + length - offset_glyph_assembly, num_glyphs)) { 1.429 + return OTS_FAILURE(); 1.430 + } 1.431 + } 1.432 + 1.433 + // Check the sequence of MathGlyphVariantRecord. 1.434 + for (unsigned i = 0; i < variant_count; ++i) { 1.435 + uint16_t glyph = 0; 1.436 + if (!subtable.ReadU16(&glyph) || 1.437 + !subtable.Skip(2)) { 1.438 + return OTS_FAILURE(); 1.439 + } 1.440 + if (glyph >= num_glyphs) { 1.441 + OTS_WARNING("bad glyph ID: %u", glyph); 1.442 + return OTS_FAILURE(); 1.443 + } 1.444 + } 1.445 + 1.446 + return true; 1.447 +} 1.448 + 1.449 +bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file, 1.450 + ots::Buffer* subtable, 1.451 + const uint8_t *data, 1.452 + size_t length, 1.453 + const uint16_t num_glyphs, 1.454 + uint16_t offset_coverage, 1.455 + uint16_t glyph_count, 1.456 + const unsigned sequence_end) { 1.457 + // Check coverage table. 1.458 + if (offset_coverage < sequence_end || offset_coverage >= length) { 1.459 + return OTS_FAILURE(); 1.460 + } 1.461 + if (!ots::ParseCoverageTable(file, data + offset_coverage, 1.462 + length - offset_coverage, 1.463 + num_glyphs, glyph_count)) { 1.464 + return OTS_FAILURE(); 1.465 + } 1.466 + 1.467 + // Check sequence of MathGlyphConstruction. 1.468 + for (unsigned i = 0; i < glyph_count; ++i) { 1.469 + uint16_t offset_glyph_construction = 0; 1.470 + if (!subtable->ReadU16(&offset_glyph_construction)) { 1.471 + return OTS_FAILURE(); 1.472 + } 1.473 + if (offset_glyph_construction < sequence_end || 1.474 + offset_glyph_construction >= length || 1.475 + !ParseMathGlyphConstructionTable(file, data + offset_glyph_construction, 1.476 + length - offset_glyph_construction, 1.477 + num_glyphs)) { 1.478 + return OTS_FAILURE(); 1.479 + } 1.480 + } 1.481 + 1.482 + return true; 1.483 +} 1.484 + 1.485 +bool ParseMathVariantsTable(const ots::OpenTypeFile *file, 1.486 + const uint8_t *data, 1.487 + size_t length, const uint16_t num_glyphs) { 1.488 + ots::Buffer subtable(data, length); 1.489 + 1.490 + // Check the header. 1.491 + uint16_t offset_vert_glyph_coverage = 0; 1.492 + uint16_t offset_horiz_glyph_coverage = 0; 1.493 + uint16_t vert_glyph_count = 0; 1.494 + uint16_t horiz_glyph_count = 0; 1.495 + if (!subtable.Skip(2) || // MinConnectorOverlap 1.496 + !subtable.ReadU16(&offset_vert_glyph_coverage) || 1.497 + !subtable.ReadU16(&offset_horiz_glyph_coverage) || 1.498 + !subtable.ReadU16(&vert_glyph_count) || 1.499 + !subtable.ReadU16(&horiz_glyph_count)) { 1.500 + return OTS_FAILURE(); 1.501 + } 1.502 + 1.503 + const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 + 1.504 + horiz_glyph_count * 2; 1.505 + if (sequence_end > std::numeric_limits<uint16_t>::max()) { 1.506 + return OTS_FAILURE(); 1.507 + } 1.508 + 1.509 + if (!ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs, 1.510 + offset_vert_glyph_coverage, 1.511 + vert_glyph_count, 1.512 + sequence_end) || 1.513 + !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs, 1.514 + offset_horiz_glyph_coverage, 1.515 + horiz_glyph_count, 1.516 + sequence_end)) { 1.517 + return OTS_FAILURE(); 1.518 + } 1.519 + 1.520 + return true; 1.521 +} 1.522 + 1.523 +} // namespace 1.524 + 1.525 +#define DROP_THIS_TABLE \ 1.526 + do { file->math->data = 0; file->math->length = 0; } while (0) 1.527 + 1.528 +namespace ots { 1.529 + 1.530 +bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { 1.531 + // Grab the number of glyphs in the file from the maxp table to check 1.532 + // GlyphIDs in MATH table. 1.533 + if (!file->maxp) { 1.534 + return OTS_FAILURE(); 1.535 + } 1.536 + const uint16_t num_glyphs = file->maxp->num_glyphs; 1.537 + 1.538 + Buffer table(data, length); 1.539 + 1.540 + OpenTypeMATH* math = new OpenTypeMATH; 1.541 + file->math = math; 1.542 + 1.543 + uint32_t version = 0; 1.544 + if (!table.ReadU32(&version)) { 1.545 + return OTS_FAILURE(); 1.546 + } 1.547 + if (version != 0x00010000) { 1.548 + OTS_WARNING("bad MATH version"); 1.549 + DROP_THIS_TABLE; 1.550 + return true; 1.551 + } 1.552 + 1.553 + uint16_t offset_math_constants = 0; 1.554 + uint16_t offset_math_glyph_info = 0; 1.555 + uint16_t offset_math_variants = 0; 1.556 + if (!table.ReadU16(&offset_math_constants) || 1.557 + !table.ReadU16(&offset_math_glyph_info) || 1.558 + !table.ReadU16(&offset_math_variants)) { 1.559 + return OTS_FAILURE(); 1.560 + } 1.561 + 1.562 + if (offset_math_constants >= length || 1.563 + offset_math_constants < kMathHeaderSize || 1.564 + offset_math_glyph_info >= length || 1.565 + offset_math_glyph_info < kMathHeaderSize || 1.566 + offset_math_variants >= length || 1.567 + offset_math_variants < kMathHeaderSize) { 1.568 + OTS_WARNING("bad offset in MATH header"); 1.569 + DROP_THIS_TABLE; 1.570 + return true; 1.571 + } 1.572 + 1.573 + if (!ParseMathConstantsTable(file, data + offset_math_constants, 1.574 + length - offset_math_constants)) { 1.575 + DROP_THIS_TABLE; 1.576 + return true; 1.577 + } 1.578 + if (!ParseMathGlyphInfoTable(file, data + offset_math_glyph_info, 1.579 + length - offset_math_glyph_info, num_glyphs)) { 1.580 + DROP_THIS_TABLE; 1.581 + return true; 1.582 + } 1.583 + if (!ParseMathVariantsTable(file, data + offset_math_variants, 1.584 + length - offset_math_variants, num_glyphs)) { 1.585 + DROP_THIS_TABLE; 1.586 + return true; 1.587 + } 1.588 + 1.589 + math->data = data; 1.590 + math->length = length; 1.591 + return true; 1.592 +} 1.593 + 1.594 +bool ots_math_should_serialise(OpenTypeFile *file) { 1.595 + return file->math != NULL && file->math->data != NULL; 1.596 +} 1.597 + 1.598 +bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) { 1.599 + if (!out->Write(file->math->data, file->math->length)) { 1.600 + return OTS_FAILURE(); 1.601 + } 1.602 + 1.603 + return true; 1.604 +} 1.605 + 1.606 +void ots_math_free(OpenTypeFile *file) { 1.607 + delete file->math; 1.608 +} 1.609 + 1.610 +} // namespace ots 1.611 +