gfx/ots/src/gpos.cc

changeset 0
6474c204b198
     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 +

mercurial