1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/ots/src/gpos.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,814 @@ 1.4 +// Copyright (c) 2011 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 +#include "gpos.h" 1.9 + 1.10 +#include <limits> 1.11 +#include <vector> 1.12 + 1.13 +#include "layout.h" 1.14 +#include "maxp.h" 1.15 + 1.16 +// GPOS - The Glyph Positioning Table 1.17 +// http://www.microsoft.com/typography/otspec/gpos.htm 1.18 + 1.19 +#define TABLE_NAME "GPOS" 1.20 + 1.21 +namespace { 1.22 + 1.23 +enum GPOS_TYPE { 1.24 + GPOS_TYPE_SINGLE_ADJUSTMENT = 1, 1.25 + GPOS_TYPE_PAIR_ADJUSTMENT = 2, 1.26 + GPOS_TYPE_CURSIVE_ATTACHMENT = 3, 1.27 + GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4, 1.28 + GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5, 1.29 + GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6, 1.30 + GPOS_TYPE_CONTEXT_POSITIONING = 7, 1.31 + GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8, 1.32 + GPOS_TYPE_EXTENSION_POSITIONING = 9, 1.33 + GPOS_TYPE_RESERVED = 10 1.34 +}; 1.35 + 1.36 +// The size of gpos header. 1.37 +const unsigned kGposHeaderSize = 10; 1.38 +// The maximum format number for anchor tables. 1.39 +const uint16_t kMaxAnchorFormat = 3; 1.40 +// The maximum number of class value. 1.41 +const uint16_t kMaxClassDefValue = 0xFFFF; 1.42 + 1.43 +// Lookup type parsers. 1.44 +bool ParseSingleAdjustment(const ots::OpenTypeFile *file, 1.45 + const uint8_t *data, const size_t length); 1.46 +bool ParsePairAdjustment(const ots::OpenTypeFile *file, 1.47 + const uint8_t *data, const size_t length); 1.48 +bool ParseCursiveAttachment(const ots::OpenTypeFile *file, 1.49 + const uint8_t *data, const size_t length); 1.50 +bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file, 1.51 + const uint8_t *data, const size_t length); 1.52 +bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file, 1.53 + const uint8_t *data, const size_t length); 1.54 +bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file, 1.55 + const uint8_t *data, const size_t length); 1.56 +bool ParseContextPositioning(const ots::OpenTypeFile *file, 1.57 + const uint8_t *data, const size_t length); 1.58 +bool ParseChainedContextPositioning(const ots::OpenTypeFile *file, 1.59 + const uint8_t *data, const size_t length); 1.60 +bool ParseExtensionPositioning(const ots::OpenTypeFile *file, 1.61 + const uint8_t *data, const size_t length); 1.62 + 1.63 +const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = { 1.64 + {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment}, 1.65 + {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment}, 1.66 + {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment}, 1.67 + {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment}, 1.68 + {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment}, 1.69 + {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment}, 1.70 + {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning}, 1.71 + {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning}, 1.72 + {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning} 1.73 +}; 1.74 + 1.75 +const ots::LookupSubtableParser kGposLookupSubtableParser = { 1.76 + arraysize(kGposTypeParsers), 1.77 + GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers 1.78 +}; 1.79 + 1.80 +// Shared Tables: ValueRecord, Anchor Table, and MarkArray 1.81 + 1.82 +bool ParseValueRecord(const ots::OpenTypeFile *file, 1.83 + ots::Buffer* subtable, const uint8_t *data, 1.84 + const size_t length, const uint16_t value_format) { 1.85 + // Check existence of adjustment fields. 1.86 + for (unsigned i = 0; i < 4; ++i) { 1.87 + if ((value_format >> i) & 0x1) { 1.88 + // Just read the field since these fileds could take an arbitrary values. 1.89 + if (!subtable->Skip(2)) { 1.90 + return OTS_FAILURE_MSG("Failed to read value reacord component"); 1.91 + } 1.92 + } 1.93 + } 1.94 + 1.95 + // Check existence of offsets to device table. 1.96 + for (unsigned i = 0; i < 4; ++i) { 1.97 + if ((value_format >> (i + 4)) & 0x1) { 1.98 + uint16_t offset = 0; 1.99 + if (!subtable->ReadU16(&offset)) { 1.100 + return OTS_FAILURE_MSG("Failed to read value record offset"); 1.101 + } 1.102 + if (offset) { 1.103 + // TODO(bashi): Is it possible that device tables locate before 1.104 + // this record? No fonts contain such offset AKAIF. 1.105 + if (offset >= length) { 1.106 + return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length); 1.107 + } 1.108 + if (!ots::ParseDeviceTable(file, data + offset, length - offset)) { 1.109 + return OTS_FAILURE_MSG("Failed to parse device table in value record"); 1.110 + } 1.111 + } 1.112 + } 1.113 + } 1.114 + return true; 1.115 +} 1.116 + 1.117 +bool ParseAnchorTable(const ots::OpenTypeFile *file, 1.118 + const uint8_t *data, const size_t length) { 1.119 + ots::Buffer subtable(data, length); 1.120 + 1.121 + uint16_t format = 0; 1.122 + // Read format and skip 2 2-byte fields that could be arbitrary values. 1.123 + if (!subtable.ReadU16(&format) || 1.124 + !subtable.Skip(4)) { 1.125 + return OTS_FAILURE_MSG("Faled to read anchor table"); 1.126 + } 1.127 + 1.128 + if (format == 0 || format > kMaxAnchorFormat) { 1.129 + return OTS_FAILURE_MSG("Bad Anchor table format %d", format); 1.130 + } 1.131 + 1.132 + // Format 2 and 3 has additional fields. 1.133 + if (format == 2) { 1.134 + // Format 2 provides an index to a glyph contour point, which will take 1.135 + // arbitrary value. 1.136 + uint16_t anchor_point = 0; 1.137 + if (!subtable.ReadU16(&anchor_point)) { 1.138 + return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table"); 1.139 + } 1.140 + } else if (format == 3) { 1.141 + uint16_t offset_x_device = 0; 1.142 + uint16_t offset_y_device = 0; 1.143 + if (!subtable.ReadU16(&offset_x_device) || 1.144 + !subtable.ReadU16(&offset_y_device)) { 1.145 + return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table"); 1.146 + } 1.147 + const unsigned format_end = static_cast<unsigned>(10); 1.148 + if (offset_x_device) { 1.149 + if (offset_x_device < format_end || offset_x_device >= length) { 1.150 + return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device); 1.151 + } 1.152 + if (!ots::ParseDeviceTable(file, data + offset_x_device, 1.153 + length - offset_x_device)) { 1.154 + return OTS_FAILURE_MSG("Failed to parse device table in anchor table"); 1.155 + } 1.156 + } 1.157 + if (offset_y_device) { 1.158 + if (offset_y_device < format_end || offset_y_device >= length) { 1.159 + return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device); 1.160 + } 1.161 + if (!ots::ParseDeviceTable(file, data + offset_y_device, 1.162 + length - offset_y_device)) { 1.163 + return OTS_FAILURE_MSG("Failed to parse device table in anchor table"); 1.164 + } 1.165 + } 1.166 + } 1.167 + return true; 1.168 +} 1.169 + 1.170 +bool ParseMarkArrayTable(const ots::OpenTypeFile *file, 1.171 + const uint8_t *data, const size_t length, 1.172 + const uint16_t class_count) { 1.173 + ots::Buffer subtable(data, length); 1.174 + 1.175 + uint16_t mark_count = 0; 1.176 + if (!subtable.ReadU16(&mark_count)) { 1.177 + return OTS_FAILURE_MSG("Can't read mark table length"); 1.178 + } 1.179 + 1.180 + // MarkRecord consists of 4-bytes. 1.181 + const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2; 1.182 + if (mark_records_end > std::numeric_limits<uint16_t>::max()) { 1.183 + return OTS_FAILURE_MSG("Bad mark table length"); 1.184 + } 1.185 + for (unsigned i = 0; i < mark_count; ++i) { 1.186 + uint16_t class_value = 0; 1.187 + uint16_t offset_mark_anchor = 0; 1.188 + if (!subtable.ReadU16(&class_value) || 1.189 + !subtable.ReadU16(&offset_mark_anchor)) { 1.190 + return OTS_FAILURE_MSG("Can't read mark table %d", i); 1.191 + } 1.192 + // |class_value| may take arbitrary values including 0 here so we don't 1.193 + // check the value. 1.194 + if (offset_mark_anchor < mark_records_end || 1.195 + offset_mark_anchor >= length) { 1.196 + return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i); 1.197 + } 1.198 + if (!ParseAnchorTable(file, data + offset_mark_anchor, 1.199 + length - offset_mark_anchor)) { 1.200 + return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i); 1.201 + } 1.202 + } 1.203 + 1.204 + return true; 1.205 +} 1.206 + 1.207 +// Lookup Type 1: 1.208 +// Single Adjustment Positioning Subtable 1.209 +bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data, 1.210 + const size_t length) { 1.211 + ots::Buffer subtable(data, length); 1.212 + 1.213 + uint16_t format = 0; 1.214 + uint16_t offset_coverage = 0; 1.215 + uint16_t value_format = 0; 1.216 + if (!subtable.ReadU16(&format) || 1.217 + !subtable.ReadU16(&offset_coverage) || 1.218 + !subtable.ReadU16(&value_format)) { 1.219 + return OTS_FAILURE_MSG("Can't read single adjustment information"); 1.220 + } 1.221 + 1.222 + if (format == 1) { 1.223 + // Format 1 exactly one value record. 1.224 + if (!ParseValueRecord(file, &subtable, data, length, value_format)) { 1.225 + return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table"); 1.226 + } 1.227 + } else if (format == 2) { 1.228 + uint16_t value_count = 0; 1.229 + if (!subtable.ReadU16(&value_count)) { 1.230 + return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table"); 1.231 + } 1.232 + for (unsigned i = 0; i < value_count; ++i) { 1.233 + if (!ParseValueRecord(file, &subtable, data, length, value_format)) { 1.234 + return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i); 1.235 + } 1.236 + } 1.237 + } else { 1.238 + return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format); 1.239 + } 1.240 + 1.241 + if (offset_coverage < subtable.offset() || offset_coverage >= length) { 1.242 + return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage); 1.243 + } 1.244 + 1.245 + if (!ots::ParseCoverageTable(file, data + offset_coverage, 1.246 + length - offset_coverage, 1.247 + file->maxp->num_glyphs)) { 1.248 + return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table"); 1.249 + } 1.250 + 1.251 + return true; 1.252 +} 1.253 + 1.254 +bool ParsePairSetTable(const ots::OpenTypeFile *file, 1.255 + const uint8_t *data, const size_t length, 1.256 + const uint16_t value_format1, 1.257 + const uint16_t value_format2, 1.258 + const uint16_t num_glyphs) { 1.259 + ots::Buffer subtable(data, length); 1.260 + 1.261 + uint16_t value_count = 0; 1.262 + if (!subtable.ReadU16(&value_count)) { 1.263 + return OTS_FAILURE_MSG("Failed to read pair set table structure"); 1.264 + } 1.265 + for (unsigned i = 0; i < value_count; ++i) { 1.266 + // Check pair value record. 1.267 + uint16_t glyph_id = 0; 1.268 + if (!subtable.ReadU16(&glyph_id)) { 1.269 + return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i); 1.270 + } 1.271 + if (glyph_id >= num_glyphs) { 1.272 + return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs); 1.273 + } 1.274 + if (!ParseValueRecord(file, &subtable, data, length, value_format1)) { 1.275 + return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table"); 1.276 + } 1.277 + if (!ParseValueRecord(file, &subtable, data, length, value_format2)) { 1.278 + return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table"); 1.279 + } 1.280 + } 1.281 + return true; 1.282 +} 1.283 + 1.284 +bool ParsePairPosFormat1(const ots::OpenTypeFile *file, 1.285 + const uint8_t *data, const size_t length, 1.286 + const uint16_t value_format1, 1.287 + const uint16_t value_format2, 1.288 + const uint16_t num_glyphs) { 1.289 + ots::Buffer subtable(data, length); 1.290 + 1.291 + // Skip 8 bytes that are already read before. 1.292 + if (!subtable.Skip(8)) { 1.293 + return OTS_FAILURE_MSG("Failed to read pair pos table structure"); 1.294 + } 1.295 + 1.296 + uint16_t pair_set_count = 0; 1.297 + if (!subtable.ReadU16(&pair_set_count)) { 1.298 + return OTS_FAILURE_MSG("Failed to read pair pos set count"); 1.299 + } 1.300 + 1.301 + const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10; 1.302 + if (pair_pos_end > std::numeric_limits<uint16_t>::max()) { 1.303 + return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end); 1.304 + } 1.305 + for (unsigned i = 0; i < pair_set_count; ++i) { 1.306 + uint16_t pair_set_offset = 0; 1.307 + if (!subtable.ReadU16(&pair_set_offset)) { 1.308 + return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i); 1.309 + } 1.310 + if (pair_set_offset < pair_pos_end || pair_set_offset >= length) { 1.311 + return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i); 1.312 + } 1.313 + // Check pair set tables 1.314 + if (!ParsePairSetTable(file, data + pair_set_offset, length - pair_set_offset, 1.315 + value_format1, value_format2, 1.316 + num_glyphs)) { 1.317 + return OTS_FAILURE_MSG("Failed to parse pair set table %d", i); 1.318 + } 1.319 + } 1.320 + 1.321 + return true; 1.322 +} 1.323 + 1.324 +bool ParsePairPosFormat2(const ots::OpenTypeFile *file, 1.325 + const uint8_t *data, const size_t length, 1.326 + const uint16_t value_format1, 1.327 + const uint16_t value_format2, 1.328 + const uint16_t num_glyphs) { 1.329 + ots::Buffer subtable(data, length); 1.330 + 1.331 + // Skip 8 bytes that are already read before. 1.332 + if (!subtable.Skip(8)) { 1.333 + return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure"); 1.334 + } 1.335 + 1.336 + uint16_t offset_class_def1 = 0; 1.337 + uint16_t offset_class_def2 = 0; 1.338 + uint16_t class1_count = 0; 1.339 + uint16_t class2_count = 0; 1.340 + if (!subtable.ReadU16(&offset_class_def1) || 1.341 + !subtable.ReadU16(&offset_class_def2) || 1.342 + !subtable.ReadU16(&class1_count) || 1.343 + !subtable.ReadU16(&class2_count)) { 1.344 + return OTS_FAILURE_MSG("Failed to read pair pos format 2 data"); 1.345 + } 1.346 + 1.347 + // Check class 1 records. 1.348 + for (unsigned i = 0; i < class1_count; ++i) { 1.349 + // Check class 2 records. 1.350 + for (unsigned j = 0; j < class2_count; ++j) { 1.351 + if (value_format1 && !ParseValueRecord(file, &subtable, data, length, 1.352 + value_format1)) { 1.353 + return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i); 1.354 + } 1.355 + if (value_format2 && !ParseValueRecord(file, &subtable, data, length, 1.356 + value_format2)) { 1.357 + return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i); 1.358 + } 1.359 + } 1.360 + } 1.361 + 1.362 + // Check class definition tables. 1.363 + if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length || 1.364 + offset_class_def2 < subtable.offset() || offset_class_def2 >= length) { 1.365 + return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2); 1.366 + } 1.367 + if (!ots::ParseClassDefTable(file, data + offset_class_def1, 1.368 + length - offset_class_def1, 1.369 + num_glyphs, kMaxClassDefValue)) { 1.370 + return OTS_FAILURE_MSG("Failed to parse class definition table 1"); 1.371 + } 1.372 + if (!ots::ParseClassDefTable(file, data + offset_class_def2, 1.373 + length - offset_class_def2, 1.374 + num_glyphs, kMaxClassDefValue)) { 1.375 + return OTS_FAILURE_MSG("Failed to parse class definition table 2"); 1.376 + } 1.377 + 1.378 + return true; 1.379 +} 1.380 + 1.381 +// Lookup Type 2: 1.382 +// Pair Adjustment Positioning Subtable 1.383 +bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data, 1.384 + const size_t length) { 1.385 + ots::Buffer subtable(data, length); 1.386 + 1.387 + uint16_t format = 0; 1.388 + uint16_t offset_coverage = 0; 1.389 + uint16_t value_format1 = 0; 1.390 + uint16_t value_format2 = 0; 1.391 + if (!subtable.ReadU16(&format) || 1.392 + !subtable.ReadU16(&offset_coverage) || 1.393 + !subtable.ReadU16(&value_format1) || 1.394 + !subtable.ReadU16(&value_format2)) { 1.395 + return OTS_FAILURE_MSG("Failed to read pair adjustment structure"); 1.396 + } 1.397 + 1.398 + if (format == 1) { 1.399 + if (!ParsePairPosFormat1(file, data, length, value_format1, value_format2, 1.400 + file->maxp->num_glyphs)) { 1.401 + return OTS_FAILURE_MSG("Failed to parse pair pos format 1"); 1.402 + } 1.403 + } else if (format == 2) { 1.404 + if (!ParsePairPosFormat2(file, data, length, value_format1, value_format2, 1.405 + file->maxp->num_glyphs)) { 1.406 + return OTS_FAILURE_MSG("Failed to parse pair format 2"); 1.407 + } 1.408 + } else { 1.409 + return OTS_FAILURE_MSG("Bad pos pair format %d", format); 1.410 + } 1.411 + 1.412 + if (offset_coverage < subtable.offset() || offset_coverage >= length) { 1.413 + return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage); 1.414 + } 1.415 + if (!ots::ParseCoverageTable(file, data + offset_coverage, 1.416 + length - offset_coverage, 1.417 + file->maxp->num_glyphs)) { 1.418 + return OTS_FAILURE_MSG("Failed to parse coverage table"); 1.419 + } 1.420 + 1.421 + return true; 1.422 +} 1.423 + 1.424 +// Lookup Type 3 1.425 +// Cursive Attachment Positioning Subtable 1.426 +bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data, 1.427 + const size_t length) { 1.428 + ots::Buffer subtable(data, length); 1.429 + 1.430 + uint16_t format = 0; 1.431 + uint16_t offset_coverage = 0; 1.432 + uint16_t entry_exit_count = 0; 1.433 + if (!subtable.ReadU16(&format) || 1.434 + !subtable.ReadU16(&offset_coverage) || 1.435 + !subtable.ReadU16(&entry_exit_count)) { 1.436 + return OTS_FAILURE_MSG("Failed to read cursive attachment structure"); 1.437 + } 1.438 + 1.439 + if (format != 1) { 1.440 + return OTS_FAILURE_MSG("Bad cursive attachment format %d", format); 1.441 + } 1.442 + 1.443 + // Check entry exit records. 1.444 + const unsigned entry_exit_records_end = 1.445 + 2 * static_cast<unsigned>(entry_exit_count) + 6; 1.446 + if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) { 1.447 + return OTS_FAILURE_MSG("Bad entry exit record end %d", entry_exit_records_end); 1.448 + } 1.449 + for (unsigned i = 0; i < entry_exit_count; ++i) { 1.450 + uint16_t offset_entry_anchor = 0; 1.451 + uint16_t offset_exit_anchor = 0; 1.452 + if (!subtable.ReadU16(&offset_entry_anchor) || 1.453 + !subtable.ReadU16(&offset_exit_anchor)) { 1.454 + return OTS_FAILURE_MSG("Can't read entry exit record %d", i); 1.455 + } 1.456 + // These offsets could be NULL. 1.457 + if (offset_entry_anchor) { 1.458 + if (offset_entry_anchor < entry_exit_records_end || 1.459 + offset_entry_anchor >= length) { 1.460 + return OTS_FAILURE_MSG("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i); 1.461 + } 1.462 + if (!ParseAnchorTable(file, data + offset_entry_anchor, 1.463 + length - offset_entry_anchor)) { 1.464 + return OTS_FAILURE_MSG("Failed to parse entry anchor table in entry exit record %d", i); 1.465 + } 1.466 + } 1.467 + if (offset_exit_anchor) { 1.468 + if (offset_exit_anchor < entry_exit_records_end || 1.469 + offset_exit_anchor >= length) { 1.470 + return OTS_FAILURE_MSG("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i); 1.471 + } 1.472 + if (!ParseAnchorTable(file, data + offset_exit_anchor, 1.473 + length - offset_exit_anchor)) { 1.474 + return OTS_FAILURE_MSG("Failed to parse exit anchor table in entry exit record %d", i); 1.475 + } 1.476 + } 1.477 + } 1.478 + 1.479 + if (offset_coverage < subtable.offset() || offset_coverage >= length) { 1.480 + return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage); 1.481 + } 1.482 + if (!ots::ParseCoverageTable(file, data + offset_coverage, 1.483 + length - offset_coverage, 1.484 + file->maxp->num_glyphs)) { 1.485 + return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment"); 1.486 + } 1.487 + 1.488 + return true; 1.489 +} 1.490 + 1.491 +bool ParseAnchorArrayTable(const ots::OpenTypeFile *file, 1.492 + const uint8_t *data, const size_t length, 1.493 + const uint16_t class_count) { 1.494 + ots::Buffer subtable(data, length); 1.495 + 1.496 + uint16_t record_count = 0; 1.497 + if (!subtable.ReadU16(&record_count)) { 1.498 + return OTS_FAILURE_MSG("Can't read anchor array length"); 1.499 + } 1.500 + 1.501 + const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) * 1.502 + static_cast<unsigned>(class_count) + 2; 1.503 + if (anchor_array_end > std::numeric_limits<uint16_t>::max()) { 1.504 + return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end); 1.505 + } 1.506 + for (unsigned i = 0; i < record_count; ++i) { 1.507 + for (unsigned j = 0; j < class_count; ++j) { 1.508 + uint16_t offset_record = 0; 1.509 + if (!subtable.ReadU16(&offset_record)) { 1.510 + return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i); 1.511 + } 1.512 + // |offset_record| could be NULL. 1.513 + if (offset_record) { 1.514 + if (offset_record < anchor_array_end || offset_record >= length) { 1.515 + return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i); 1.516 + } 1.517 + if (!ParseAnchorTable(file, data + offset_record, 1.518 + length - offset_record)) { 1.519 + return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i); 1.520 + } 1.521 + } 1.522 + } 1.523 + } 1.524 + return true; 1.525 +} 1.526 + 1.527 +bool ParseLigatureArrayTable(const ots::OpenTypeFile *file, 1.528 + const uint8_t *data, const size_t length, 1.529 + const uint16_t class_count) { 1.530 + ots::Buffer subtable(data, length); 1.531 + 1.532 + uint16_t ligature_count = 0; 1.533 + if (!subtable.ReadU16(&ligature_count)) { 1.534 + return OTS_FAILURE_MSG("Failed to read ligature count"); 1.535 + } 1.536 + for (unsigned i = 0; i < ligature_count; ++i) { 1.537 + uint16_t offset_ligature_attach = 0; 1.538 + if (!subtable.ReadU16(&offset_ligature_attach)) { 1.539 + return OTS_FAILURE_MSG("Can't read ligature offset %d", i); 1.540 + } 1.541 + if (offset_ligature_attach < 2 || offset_ligature_attach >= length) { 1.542 + return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i); 1.543 + } 1.544 + if (!ParseAnchorArrayTable(file, data + offset_ligature_attach, 1.545 + length - offset_ligature_attach, class_count)) { 1.546 + return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i); 1.547 + } 1.548 + } 1.549 + return true; 1.550 +} 1.551 + 1.552 +// Common parser for Lookup Type 4, 5 and 6. 1.553 +bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file, 1.554 + const uint8_t *data, const size_t length, 1.555 + const GPOS_TYPE type) { 1.556 + ots::Buffer subtable(data, length); 1.557 + 1.558 + uint16_t format = 0; 1.559 + uint16_t offset_coverage1 = 0; 1.560 + uint16_t offset_coverage2 = 0; 1.561 + uint16_t class_count = 0; 1.562 + uint16_t offset_mark_array = 0; 1.563 + uint16_t offset_type_specific_array = 0; 1.564 + if (!subtable.ReadU16(&format) || 1.565 + !subtable.ReadU16(&offset_coverage1) || 1.566 + !subtable.ReadU16(&offset_coverage2) || 1.567 + !subtable.ReadU16(&class_count) || 1.568 + !subtable.ReadU16(&offset_mark_array) || 1.569 + !subtable.ReadU16(&offset_type_specific_array)) { 1.570 + return OTS_FAILURE_MSG("Failed to read mark attachment subtable header"); 1.571 + } 1.572 + 1.573 + if (format != 1) { 1.574 + return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format); 1.575 + } 1.576 + 1.577 + const unsigned header_end = static_cast<unsigned>(subtable.offset()); 1.578 + if (header_end > std::numeric_limits<uint16_t>::max()) { 1.579 + return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end); 1.580 + } 1.581 + if (offset_coverage1 < header_end || offset_coverage1 >= length) { 1.582 + return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1); 1.583 + } 1.584 + if (!ots::ParseCoverageTable(file, data + offset_coverage1, 1.585 + length - offset_coverage1, 1.586 + file->maxp->num_glyphs)) { 1.587 + return OTS_FAILURE_MSG("Failed to parse converge 1 table"); 1.588 + } 1.589 + if (offset_coverage2 < header_end || offset_coverage2 >= length) { 1.590 + return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2); 1.591 + } 1.592 + if (!ots::ParseCoverageTable(file, data + offset_coverage2, 1.593 + length - offset_coverage2, 1.594 + file->maxp->num_glyphs)) { 1.595 + return OTS_FAILURE_MSG("Failed to parse coverage table 2"); 1.596 + } 1.597 + 1.598 + if (offset_mark_array < header_end || offset_mark_array >= length) { 1.599 + return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array); 1.600 + } 1.601 + if (!ParseMarkArrayTable(file, data + offset_mark_array, 1.602 + length - offset_mark_array, class_count)) { 1.603 + return OTS_FAILURE_MSG("Failed to parse mark array"); 1.604 + } 1.605 + 1.606 + if (offset_type_specific_array < header_end || 1.607 + offset_type_specific_array >= length) { 1.608 + return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array); 1.609 + } 1.610 + if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT || 1.611 + type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) { 1.612 + if (!ParseAnchorArrayTable(file, data + offset_type_specific_array, 1.613 + length - offset_type_specific_array, 1.614 + class_count)) { 1.615 + return OTS_FAILURE_MSG("Failed to parse anchor array"); 1.616 + } 1.617 + } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) { 1.618 + if (!ParseLigatureArrayTable(file, data + offset_type_specific_array, 1.619 + length - offset_type_specific_array, 1.620 + class_count)) { 1.621 + return OTS_FAILURE_MSG("Failed to parse ligature array"); 1.622 + } 1.623 + } else { 1.624 + return OTS_FAILURE_MSG("Bad attachment type %d", type); 1.625 + } 1.626 + 1.627 + return true; 1.628 +} 1.629 + 1.630 +// Lookup Type 4: 1.631 +// MarkToBase Attachment Positioning Subtable 1.632 +bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file, 1.633 + const uint8_t *data, const size_t length) { 1.634 + return ParseMarkToAttachmentSubtables(file, data, length, 1.635 + GPOS_TYPE_MARK_TO_BASE_ATTACHMENT); 1.636 +} 1.637 + 1.638 +// Lookup Type 5: 1.639 +// MarkToLigature Attachment Positioning Subtable 1.640 +bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file, 1.641 + const uint8_t *data, const size_t length) { 1.642 + return ParseMarkToAttachmentSubtables(file, data, length, 1.643 + GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT); 1.644 +} 1.645 + 1.646 +// Lookup Type 6: 1.647 +// MarkToMark Attachment Positioning Subtable 1.648 +bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file, 1.649 + const uint8_t *data, const size_t length) { 1.650 + return ParseMarkToAttachmentSubtables(file, data, length, 1.651 + GPOS_TYPE_MARK_TO_MARK_ATTACHMENT); 1.652 +} 1.653 + 1.654 +// Lookup Type 7: 1.655 +// Contextual Positioning Subtables 1.656 +bool ParseContextPositioning(const ots::OpenTypeFile *file, 1.657 + const uint8_t *data, const size_t length) { 1.658 + return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs, 1.659 + file->gpos->num_lookups); 1.660 +} 1.661 + 1.662 +// Lookup Type 8: 1.663 +// Chaining Contexual Positioning Subtable 1.664 +bool ParseChainedContextPositioning(const ots::OpenTypeFile *file, 1.665 + const uint8_t *data, const size_t length) { 1.666 + return ots::ParseChainingContextSubtable(file, data, length, 1.667 + file->maxp->num_glyphs, 1.668 + file->gpos->num_lookups); 1.669 +} 1.670 + 1.671 +// Lookup Type 9: 1.672 +// Extension Positioning 1.673 +bool ParseExtensionPositioning(const ots::OpenTypeFile *file, 1.674 + const uint8_t *data, const size_t length) { 1.675 + return ots::ParseExtensionSubtable(file, data, length, 1.676 + &kGposLookupSubtableParser); 1.677 +} 1.678 + 1.679 +} // namespace 1.680 + 1.681 +#define DROP_THIS_TABLE(msg_) \ 1.682 + do { \ 1.683 + file->gpos->data = 0; \ 1.684 + file->gpos->length = 0; \ 1.685 + OTS_FAILURE_MSG(msg_ ", table discarded"); \ 1.686 + } while (0) 1.687 + 1.688 +namespace ots { 1.689 + 1.690 +// As far as I checked, following fonts contain invalid GPOS table and 1.691 +// OTS will drop their GPOS table. 1.692 +// 1.693 +// # invalid delta format in device table 1.694 +// samanata.ttf 1.695 +// 1.696 +// # bad size range in device table 1.697 +// Sarai_07.ttf 1.698 +// 1.699 +// # bad offset to PairSetTable 1.700 +// chandas1-2.ttf 1.701 +// 1.702 +// # bad offset to FeatureTable 1.703 +// glrso12.ttf 1.704 +// gllr12.ttf 1.705 +// glbo12.ttf 1.706 +// glb12.ttf 1.707 +// glro12.ttf 1.708 +// glbso12.ttf 1.709 +// glrc12.ttf 1.710 +// glrsc12.ttf 1.711 +// glbs12.ttf 1.712 +// glrs12.ttf 1.713 +// glr12.ttf 1.714 +// 1.715 +// # ScriptRecords aren't sorted by tag 1.716 +// Garogier_unhinted.otf 1.717 +// 1.718 +// # bad start coverage index in CoverageFormat2 1.719 +// AndBasR.ttf 1.720 +// CharisSILB.ttf 1.721 +// CharisSILBI.ttf 1.722 +// CharisSILI.ttf 1.723 +// CharisSILR.ttf 1.724 +// DoulosSILR.ttf 1.725 +// GenBasBI.ttf 1.726 +// GenBasI.ttf 1.727 +// GenBkBasI.ttf 1.728 +// GenBkBasB.ttf 1.729 +// GenBkBasR.ttf 1.730 +// Padauk-Bold.ttf 1.731 +// Padauk.ttf 1.732 +// 1.733 +// # Contour point indexes aren't sorted 1.734 +// Arial Unicode.ttf 1.735 + 1.736 +bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { 1.737 + // Parsing GPOS table requires num_glyphs which is contained in maxp table. 1.738 + if (!file->maxp) { 1.739 + return OTS_FAILURE_MSG("missing maxp table needed in GPOS"); 1.740 + } 1.741 + 1.742 + Buffer table(data, length); 1.743 + 1.744 + OpenTypeGPOS *gpos = new OpenTypeGPOS; 1.745 + file->gpos = gpos; 1.746 + 1.747 + uint32_t version = 0; 1.748 + uint16_t offset_script_list = 0; 1.749 + uint16_t offset_feature_list = 0; 1.750 + uint16_t offset_lookup_list = 0; 1.751 + if (!table.ReadU32(&version) || 1.752 + !table.ReadU16(&offset_script_list) || 1.753 + !table.ReadU16(&offset_feature_list) || 1.754 + !table.ReadU16(&offset_lookup_list)) { 1.755 + DROP_THIS_TABLE("Incomplete table"); 1.756 + return true; 1.757 + } 1.758 + 1.759 + if (version != 0x00010000) { 1.760 + DROP_THIS_TABLE("Bad version"); 1.761 + return true; 1.762 + } 1.763 + if ((offset_script_list < kGposHeaderSize || 1.764 + offset_script_list >= length) || 1.765 + (offset_feature_list < kGposHeaderSize || 1.766 + offset_feature_list >= length) || 1.767 + (offset_lookup_list < kGposHeaderSize || 1.768 + offset_lookup_list >= length)) { 1.769 + DROP_THIS_TABLE("Bad offset in table header"); 1.770 + return true; 1.771 + } 1.772 + 1.773 + if (!ParseLookupListTable(file, data + offset_lookup_list, 1.774 + length - offset_lookup_list, 1.775 + &kGposLookupSubtableParser, 1.776 + &gpos->num_lookups)) { 1.777 + DROP_THIS_TABLE("Failed to parse lookup list table"); 1.778 + return true; 1.779 + } 1.780 + 1.781 + uint16_t num_features = 0; 1.782 + if (!ParseFeatureListTable(file, data + offset_feature_list, 1.783 + length - offset_feature_list, gpos->num_lookups, 1.784 + &num_features)) { 1.785 + DROP_THIS_TABLE("Failed to parse feature list table"); 1.786 + return true; 1.787 + } 1.788 + 1.789 + if (!ParseScriptListTable(file, data + offset_script_list, 1.790 + length - offset_script_list, num_features)) { 1.791 + DROP_THIS_TABLE("Failed to parse script list table"); 1.792 + return true; 1.793 + } 1.794 + 1.795 + gpos->data = data; 1.796 + gpos->length = length; 1.797 + return true; 1.798 +} 1.799 + 1.800 +bool ots_gpos_should_serialise(OpenTypeFile *file) { 1.801 + return file->gpos != NULL && file->gpos->data != NULL; 1.802 +} 1.803 + 1.804 +bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) { 1.805 + if (!out->Write(file->gpos->data, file->gpos->length)) { 1.806 + return OTS_FAILURE_MSG("Failed to write GPOS table"); 1.807 + } 1.808 + 1.809 + return true; 1.810 +} 1.811 + 1.812 +void ots_gpos_free(OpenTypeFile *file) { 1.813 + delete file->gpos; 1.814 +} 1.815 + 1.816 +} // namespace ots 1.817 +