tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gpos.cc (26036B)


      1 // Copyright (c) 2011-2017 The OTS 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.
      4 
      5 #include "gpos.h"
      6 
      7 #include <limits>
      8 #include <vector>
      9 
     10 #include "layout.h"
     11 #include "maxp.h"
     12 
     13 // GPOS - The Glyph Positioning Table
     14 // http://www.microsoft.com/typography/otspec/gpos.htm
     15 
     16 #define TABLE_NAME "GPOS"
     17 
     18 namespace {
     19 
     20 enum GPOS_TYPE {
     21  GPOS_TYPE_SINGLE_ADJUSTMENT = 1,
     22  GPOS_TYPE_PAIR_ADJUSTMENT = 2,
     23  GPOS_TYPE_CURSIVE_ATTACHMENT = 3,
     24  GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4,
     25  GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5,
     26  GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6,
     27  GPOS_TYPE_CONTEXT_POSITIONING = 7,
     28  GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8,
     29  GPOS_TYPE_EXTENSION_POSITIONING = 9,
     30  GPOS_TYPE_RESERVED = 10
     31 };
     32 
     33 // The maximum format number for anchor tables.
     34 const uint16_t kMaxAnchorFormat = 3;
     35 
     36 // Shared Tables: ValueRecord, Anchor Table, and MarkArray
     37 
     38 size_t CalcValueRecordSize(const uint16_t value_format) {
     39  size_t size = 0;
     40  for (unsigned i = 0; i < 8; ++i) {
     41    if ((value_format >> i) & 0x1) {
     42      size += 2;
     43    }
     44  }
     45 
     46  return size;
     47 }
     48 
     49 bool ParseValueRecord(const ots::Font *font,
     50                      ots::Buffer* subtable,
     51                      const uint16_t value_format) {
     52  const uint8_t *data = subtable->buffer();
     53  const size_t length = subtable->length();
     54 
     55  // Check existence of adjustment fields.
     56  for (unsigned i = 0; i < 4; ++i) {
     57    if ((value_format >> i) & 0x1) {
     58      // Just read the field since these fileds could take an arbitrary values.
     59      if (!subtable->Skip(2)) {
     60        return OTS_FAILURE_MSG("Failed to read value reacord component");
     61      }
     62    }
     63  }
     64 
     65  // Check existence of offsets to device table.
     66  for (unsigned i = 0; i < 4; ++i) {
     67    if ((value_format >> (i + 4)) & 0x1) {
     68      uint16_t offset = 0;
     69      if (!subtable->ReadU16(&offset)) {
     70        return OTS_FAILURE_MSG("Failed to read value record offset");
     71      }
     72      if (offset) {
     73        // TODO(bashi): Is it possible that device tables locate before
     74        // this record? No fonts contain such offset AKAIF.
     75        if (offset >= length) {
     76          return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length);
     77        }
     78        if (!ots::ParseDeviceTable(font, data + offset, length - offset)) {
     79          return OTS_FAILURE_MSG("Failed to parse device table in value record");
     80        }
     81      }
     82    }
     83  }
     84  return true;
     85 }
     86 
     87 bool ParseAnchorTable(const ots::Font *font,
     88                      const uint8_t *data, const size_t length) {
     89  ots::Buffer subtable(data, length);
     90 
     91  uint16_t format = 0;
     92  // Read format and skip 2 2-byte fields that could be arbitrary values.
     93  if (!subtable.ReadU16(&format) ||
     94      !subtable.Skip(4)) {
     95    return OTS_FAILURE_MSG("Faled to read anchor table");
     96  }
     97 
     98  if (format == 0 || format > kMaxAnchorFormat) {
     99    return OTS_FAILURE_MSG("Bad Anchor table format %d", format);
    100  }
    101 
    102  // Format 2 and 3 has additional fields.
    103  if (format == 2) {
    104    // Format 2 provides an index to a glyph contour point, which will take
    105    // arbitrary value.
    106    uint16_t anchor_point = 0;
    107    if (!subtable.ReadU16(&anchor_point)) {
    108      return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table");
    109    }
    110  } else if (format == 3) {
    111    uint16_t offset_x_device = 0;
    112    uint16_t offset_y_device = 0;
    113    if (!subtable.ReadU16(&offset_x_device) ||
    114        !subtable.ReadU16(&offset_y_device)) {
    115      return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table");
    116    }
    117    const unsigned format_end = static_cast<unsigned>(10);
    118    if (offset_x_device) {
    119      if (offset_x_device < format_end || offset_x_device >= length) {
    120        return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device);
    121      }
    122      if (!ots::ParseDeviceTable(font, data + offset_x_device,
    123                                 length - offset_x_device)) {
    124        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
    125      }
    126    }
    127    if (offset_y_device) {
    128      if (offset_y_device < format_end || offset_y_device >= length) {
    129        return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device);
    130      }
    131      if (!ots::ParseDeviceTable(font, data + offset_y_device,
    132                                 length - offset_y_device)) {
    133        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
    134      }
    135    }
    136  }
    137  return true;
    138 }
    139 
    140 bool ParseMarkArrayTable(const ots::Font *font,
    141                         const uint8_t *data, const size_t length) {
    142  ots::Buffer subtable(data, length);
    143 
    144  uint16_t mark_count = 0;
    145  if (!subtable.ReadU16(&mark_count)) {
    146    return OTS_FAILURE_MSG("Can't read mark table length");
    147  }
    148 
    149  // MarkRecord consists of 4-bytes.
    150  const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2;
    151  if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
    152    return OTS_FAILURE_MSG("Bad mark table length");
    153  }
    154  for (unsigned i = 0; i < mark_count; ++i) {
    155    uint16_t class_value = 0;
    156    uint16_t offset_mark_anchor = 0;
    157    if (!subtable.ReadU16(&class_value) ||
    158        !subtable.ReadU16(&offset_mark_anchor)) {
    159      return OTS_FAILURE_MSG("Can't read mark table %d", i);
    160    }
    161    // |class_value| may take arbitrary values including 0 here so we don't
    162    // check the value.
    163    if (offset_mark_anchor < mark_records_end ||
    164        offset_mark_anchor >= length) {
    165      return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i);
    166    }
    167    if (!ParseAnchorTable(font, data + offset_mark_anchor,
    168                          length - offset_mark_anchor)) {
    169      return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i);
    170    }
    171  }
    172 
    173  return true;
    174 }
    175 
    176 bool ParsePairSetTable(const ots::Font *font,
    177                       const uint8_t *data, const size_t length,
    178                       const uint16_t value_format1,
    179                       const uint16_t value_format2,
    180                       const uint16_t num_glyphs) {
    181  ots::Buffer subtable(data, length);
    182 
    183  uint16_t value_count = 0;
    184  if (!subtable.ReadU16(&value_count)) {
    185    return OTS_FAILURE_MSG("Failed to read pair set table structure");
    186  }
    187  for (unsigned i = 0; i < value_count; ++i) {
    188    // Check pair value record.
    189    uint16_t glyph_id = 0;
    190    if (!subtable.ReadU16(&glyph_id)) {
    191      return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i);
    192    }
    193    if (glyph_id >= num_glyphs) {
    194      return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
    195    }
    196    if (!ParseValueRecord(font, &subtable, value_format1)) {
    197      return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
    198    }
    199    if (!ParseValueRecord(font, &subtable, value_format2)) {
    200      return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
    201    }
    202  }
    203  return true;
    204 }
    205 
    206 bool ParsePairPosFormat1(const ots::Font *font,
    207                         const uint8_t *data, const size_t length,
    208                         const uint16_t value_format1,
    209                         const uint16_t value_format2,
    210                         const uint16_t num_glyphs) {
    211  ots::Buffer subtable(data, length);
    212 
    213  // Skip 8 bytes that are already read before.
    214  if (!subtable.Skip(8)) {
    215    return OTS_FAILURE_MSG("Failed to read pair pos table structure");
    216  }
    217 
    218  uint16_t pair_set_count = 0;
    219  if (!subtable.ReadU16(&pair_set_count)) {
    220    return OTS_FAILURE_MSG("Failed to read pair pos set count");
    221  }
    222 
    223  const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10;
    224  if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
    225    return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end);
    226  }
    227  for (unsigned i = 0; i < pair_set_count; ++i) {
    228    uint16_t pair_set_offset = 0;
    229    if (!subtable.ReadU16(&pair_set_offset)) {
    230      return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i);
    231    }
    232    if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
    233      return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i);
    234    }
    235    // Check pair set tables
    236    if (!ParsePairSetTable(font, data + pair_set_offset, length - pair_set_offset,
    237                           value_format1, value_format2,
    238                           num_glyphs)) {
    239      return OTS_FAILURE_MSG("Failed to parse pair set table %d", i);
    240    }
    241  }
    242 
    243  return true;
    244 }
    245 
    246 bool ParsePairPosFormat2(const ots::Font *font,
    247                         const uint8_t *data, const size_t length,
    248                         const uint16_t value_format1,
    249                         const uint16_t value_format2,
    250                         const uint16_t num_glyphs) {
    251  ots::Buffer subtable(data, length);
    252 
    253  // Skip 8 bytes that are already read before.
    254  if (!subtable.Skip(8)) {
    255    return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure");
    256  }
    257 
    258  uint16_t offset_class_def1 = 0;
    259  uint16_t offset_class_def2 = 0;
    260  uint16_t class1_count = 0;
    261  uint16_t class2_count = 0;
    262  if (!subtable.ReadU16(&offset_class_def1) ||
    263      !subtable.ReadU16(&offset_class_def2) ||
    264      !subtable.ReadU16(&class1_count) ||
    265      !subtable.ReadU16(&class2_count)) {
    266    return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
    267  }
    268 
    269  size_t value_record1_size = CalcValueRecordSize(value_format1);
    270  size_t value_record2_size = CalcValueRecordSize(value_format2);
    271  size_t value_records_size = size_t(class1_count) * size_t(class2_count) *
    272    (value_record1_size + value_record2_size);
    273 
    274  // Check the validity of class definition offsets.
    275  if (offset_class_def1 < subtable.offset() + value_records_size ||
    276      offset_class_def2 < subtable.offset() + value_records_size ||
    277      offset_class_def1 >= length || offset_class_def2 >= length) {
    278    return OTS_FAILURE_MSG("Bad ParsePairPosFormat2 class definition offsets %d or %d", offset_class_def1, offset_class_def2);
    279  }
    280 
    281  // Check class 1 records.
    282  if (value_record1_size || value_record2_size) {
    283    for (unsigned i = 0; i < class1_count; ++i) {
    284      // Check class 2 records.
    285      for (unsigned j = 0; j < class2_count; ++j) {
    286        if (value_format1 && value_record2_size &&
    287            !ParseValueRecord(font, &subtable, value_format1)) {
    288          return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
    289        }
    290        if (value_format2 && value_record2_size &&
    291            !ParseValueRecord(font, &subtable, value_format2)) {
    292          return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
    293        }
    294      }
    295    }
    296  }
    297 
    298  // Check class definition tables.
    299  if (!ots::ParseClassDefTable(font, data + offset_class_def1,
    300                               length - offset_class_def1,
    301                               num_glyphs, ots::kMaxClassDefValue)) {
    302    return OTS_FAILURE_MSG("Failed to parse class definition table 1");
    303  }
    304  if (!ots::ParseClassDefTable(font, data + offset_class_def2,
    305                               length - offset_class_def2,
    306                               num_glyphs, ots::kMaxClassDefValue)) {
    307    return OTS_FAILURE_MSG("Failed to parse class definition table 2");
    308  }
    309 
    310  return true;
    311 }
    312 
    313 bool ParseAnchorArrayTable(const ots::Font *font,
    314                           const uint8_t *data, const size_t length,
    315                           const uint16_t class_count) {
    316  ots::Buffer subtable(data, length);
    317 
    318  uint16_t record_count = 0;
    319  if (!subtable.ReadU16(&record_count)) {
    320    return OTS_FAILURE_MSG("Can't read anchor array length");
    321  }
    322 
    323  const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) *
    324      static_cast<unsigned>(class_count) + 2;
    325  if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
    326    return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end);
    327  }
    328  for (unsigned i = 0; i < record_count; ++i) {
    329    for (unsigned j = 0; j < class_count; ++j) {
    330      uint16_t offset_record = 0;
    331      if (!subtable.ReadU16(&offset_record)) {
    332        return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i);
    333      }
    334      // |offset_record| could be NULL.
    335      if (offset_record) {
    336        if (offset_record < anchor_array_end || offset_record >= length) {
    337          return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i);
    338        }
    339        if (!ParseAnchorTable(font, data + offset_record,
    340                              length - offset_record)) {
    341          return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i);
    342        }
    343      }
    344    }
    345  }
    346  return true;
    347 }
    348 
    349 bool ParseLigatureArrayTable(const ots::Font *font,
    350                             const uint8_t *data, const size_t length,
    351                             const uint16_t class_count) {
    352  ots::Buffer subtable(data, length);
    353 
    354  uint16_t ligature_count = 0;
    355  if (!subtable.ReadU16(&ligature_count)) {
    356    return OTS_FAILURE_MSG("Failed to read ligature count");
    357  }
    358  for (unsigned i = 0; i < ligature_count; ++i) {
    359    uint16_t offset_ligature_attach = 0;
    360    if (!subtable.ReadU16(&offset_ligature_attach)) {
    361      return OTS_FAILURE_MSG("Can't read ligature offset %d", i);
    362    }
    363    if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
    364      return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i);
    365    }
    366    if (!ParseAnchorArrayTable(font, data + offset_ligature_attach,
    367                               length - offset_ligature_attach, class_count)) {
    368      return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i);
    369    }
    370  }
    371  return true;
    372 }
    373 
    374 // Common parser for Lookup Type 4, 5 and 6.
    375 bool ParseMarkToAttachmentSubtables(const ots::Font *font,
    376                                    const uint8_t *data, const size_t length,
    377                                    const GPOS_TYPE type) {
    378  ots::Buffer subtable(data, length);
    379 
    380  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
    381      font->GetTypedTable(OTS_TAG_MAXP));
    382  if (!maxp) {
    383    return OTS_FAILURE_MSG("Required maxp table missing");
    384  }
    385 
    386  uint16_t format = 0;
    387  uint16_t offset_coverage1 = 0;
    388  uint16_t offset_coverage2 = 0;
    389  uint16_t class_count = 0;
    390  uint16_t offset_mark_array = 0;
    391  uint16_t offset_type_specific_array = 0;
    392  if (!subtable.ReadU16(&format) ||
    393      !subtable.ReadU16(&offset_coverage1) ||
    394      !subtable.ReadU16(&offset_coverage2) ||
    395      !subtable.ReadU16(&class_count) ||
    396      !subtable.ReadU16(&offset_mark_array) ||
    397      !subtable.ReadU16(&offset_type_specific_array)) {
    398    return OTS_FAILURE_MSG("Failed to read mark attachment subtable header");
    399  }
    400 
    401  if (format != 1) {
    402    return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format);
    403  }
    404 
    405  const unsigned header_end = static_cast<unsigned>(subtable.offset());
    406  if (header_end > std::numeric_limits<uint16_t>::max()) {
    407    return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end);
    408  }
    409  if (offset_coverage1 < header_end || offset_coverage1 >= length) {
    410    return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1);
    411  }
    412  if (!ots::ParseCoverageTable(font, data + offset_coverage1,
    413                               length - offset_coverage1,
    414                               maxp->num_glyphs)) {
    415    return OTS_FAILURE_MSG("Failed to parse converge 1 table");
    416  }
    417  if (offset_coverage2 < header_end || offset_coverage2 >= length) {
    418    return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2);
    419  }
    420  if (!ots::ParseCoverageTable(font, data + offset_coverage2,
    421                               length - offset_coverage2,
    422                               maxp->num_glyphs)) {
    423    return OTS_FAILURE_MSG("Failed to parse coverage table 2");
    424  }
    425 
    426  if (offset_mark_array < header_end || offset_mark_array >= length) {
    427    return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array);
    428  }
    429  if (!ParseMarkArrayTable(font, data + offset_mark_array,
    430                           length - offset_mark_array)) {
    431    return OTS_FAILURE_MSG("Failed to parse mark array");
    432  }
    433 
    434  if (offset_type_specific_array < header_end ||
    435      offset_type_specific_array >= length) {
    436    return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array);
    437  }
    438  if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT ||
    439      type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) {
    440    if (!ParseAnchorArrayTable(font, data + offset_type_specific_array,
    441                               length - offset_type_specific_array,
    442                               class_count)) {
    443      return OTS_FAILURE_MSG("Failed to parse anchor array");
    444    }
    445  } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) {
    446    if (!ParseLigatureArrayTable(font, data + offset_type_specific_array,
    447                                 length - offset_type_specific_array,
    448                                 class_count)) {
    449      return OTS_FAILURE_MSG("Failed to parse ligature array");
    450    }
    451  } else {
    452    return OTS_FAILURE_MSG("Bad attachment type %d", type);
    453  }
    454 
    455  return true;
    456 }
    457 
    458 }  // namespace
    459 
    460 namespace ots {
    461 
    462 // Lookup Type 1:
    463 // Single Adjustment Positioning Subtable
    464 bool OpenTypeGPOS::ParseSingleAdjustment(const uint8_t *data,
    465                                         const size_t length) {
    466  Font* font = GetFont();
    467  Buffer subtable(data, length);
    468 
    469  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
    470      font->GetTypedTable(OTS_TAG_MAXP));
    471  if (!maxp) {
    472    return Error("Required maxp table missing");
    473  }
    474 
    475  uint16_t format = 0;
    476  uint16_t offset_coverage = 0;
    477  uint16_t value_format = 0;
    478  if (!subtable.ReadU16(&format) ||
    479      !subtable.ReadU16(&offset_coverage) ||
    480      !subtable.ReadU16(&value_format)) {
    481    return Error("Can't read single adjustment information");
    482  }
    483 
    484  if (format == 1) {
    485    // Format 1 exactly one value record.
    486    if (!ParseValueRecord(font, &subtable, value_format)) {
    487      return Error("Failed to parse format 1 single adjustment table");
    488    }
    489  } else if (format == 2) {
    490    uint16_t value_count = 0;
    491    if (!subtable.ReadU16(&value_count)) {
    492      return Error("Failed to parse format 2 single adjustment table");
    493    }
    494    for (unsigned i = 0; i < value_count; ++i) {
    495      if (!ParseValueRecord(font, &subtable, value_format)) {
    496        return Error("Failed to parse value record %d in format 2 single adjustment table", i);
    497      }
    498    }
    499  } else {
    500    return Error("Bad format %d in single adjustment table", format);
    501  }
    502 
    503  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
    504    return Error("Bad coverage offset %d in single adjustment table", offset_coverage);
    505  }
    506 
    507  if (!ots::ParseCoverageTable(font, data + offset_coverage,
    508                               length - offset_coverage,
    509                               maxp->num_glyphs)) {
    510    return Error("Failed to parse coverage table in single adjustment table");
    511  }
    512 
    513  return true;
    514 }
    515 
    516 // Lookup Type 2:
    517 // Pair Adjustment Positioning Subtable
    518 bool OpenTypeGPOS::ParsePairAdjustment(const uint8_t *data,
    519                                       const size_t length) {
    520  Font* font = GetFont();
    521  Buffer subtable(data, length);
    522 
    523  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
    524      font->GetTypedTable(OTS_TAG_MAXP));
    525  if (!maxp) {
    526    return Error("Required maxp table missing");
    527  }
    528 
    529  uint16_t format = 0;
    530  uint16_t offset_coverage = 0;
    531  uint16_t value_format1 = 0;
    532  uint16_t value_format2 = 0;
    533  if (!subtable.ReadU16(&format) ||
    534      !subtable.ReadU16(&offset_coverage) ||
    535      !subtable.ReadU16(&value_format1) ||
    536      !subtable.ReadU16(&value_format2)) {
    537    return Error("Failed to read pair adjustment structure");
    538  }
    539 
    540  if (format == 1) {
    541    if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2,
    542                             maxp->num_glyphs)) {
    543      return Error("Failed to parse pair pos format 1");
    544    }
    545  } else if (format == 2) {
    546    if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2,
    547                             maxp->num_glyphs)) {
    548      return Error("Failed to parse pair format 2");
    549    }
    550  } else {
    551    return Error("Bad pos pair format %d", format);
    552  }
    553 
    554  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
    555    return Error("Bad pair pos offset coverage %d", offset_coverage);
    556  }
    557  if (!ots::ParseCoverageTable(font, data + offset_coverage,
    558                               length - offset_coverage,
    559                               maxp->num_glyphs)) {
    560    return Error("Failed to parse coverage table");
    561  }
    562 
    563  return true;
    564 }
    565 
    566 // Lookup Type 3
    567 // Cursive Attachment Positioning Subtable
    568 bool OpenTypeGPOS::ParseCursiveAttachment(const uint8_t *data,
    569                                          const size_t length) {
    570  Font* font = GetFont();
    571  Buffer subtable(data, length);
    572 
    573  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
    574      font->GetTypedTable(OTS_TAG_MAXP));
    575  if (!maxp) {
    576    return Error("Required maxp table missing");
    577  }
    578 
    579  uint16_t format = 0;
    580  uint16_t offset_coverage = 0;
    581  uint16_t entry_exit_count = 0;
    582  if (!subtable.ReadU16(&format) ||
    583      !subtable.ReadU16(&offset_coverage) ||
    584      !subtable.ReadU16(&entry_exit_count)) {
    585    return Error("Failed to read cursive attachment structure");
    586  }
    587 
    588  if (format != 1) {
    589    return Error("Bad cursive attachment format %d", format);
    590  }
    591 
    592  // Check entry exit records.
    593  const unsigned entry_exit_records_end =
    594      2 * static_cast<unsigned>(entry_exit_count) + 6;
    595  if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
    596    return Error("Bad entry exit record end %d", entry_exit_records_end);
    597  }
    598  for (unsigned i = 0; i < entry_exit_count; ++i) {
    599    uint16_t offset_entry_anchor = 0;
    600    uint16_t offset_exit_anchor = 0;
    601    if (!subtable.ReadU16(&offset_entry_anchor) ||
    602        !subtable.ReadU16(&offset_exit_anchor)) {
    603      return Error("Can't read entry exit record %d", i);
    604    }
    605    // These offsets could be NULL.
    606    if (offset_entry_anchor) {
    607      if (offset_entry_anchor < entry_exit_records_end ||
    608          offset_entry_anchor >= length) {
    609        return Error("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i);
    610      }
    611      if (!ParseAnchorTable(font, data + offset_entry_anchor,
    612                            length - offset_entry_anchor)) {
    613        return Error("Failed to parse entry anchor table in entry exit record %d", i);
    614      }
    615    }
    616    if (offset_exit_anchor) {
    617      if (offset_exit_anchor < entry_exit_records_end ||
    618         offset_exit_anchor >= length) {
    619        return Error("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i);
    620      }
    621      if (!ParseAnchorTable(font, data + offset_exit_anchor,
    622                            length - offset_exit_anchor)) {
    623        return Error("Failed to parse exit anchor table in entry exit record %d", i);
    624      }
    625    }
    626  }
    627 
    628  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
    629    return Error("Bad coverage offset in cursive attachment %d", offset_coverage);
    630  }
    631  if (!ots::ParseCoverageTable(font, data + offset_coverage,
    632                               length - offset_coverage,
    633                               maxp->num_glyphs)) {
    634    return Error("Failed to parse coverage table in cursive attachment");
    635  }
    636 
    637  return true;
    638 }
    639 
    640 // Lookup Type 4:
    641 // MarkToBase Attachment Positioning Subtable
    642 bool OpenTypeGPOS::ParseMarkToBaseAttachment(const uint8_t *data,
    643                                             const size_t length) {
    644  return ParseMarkToAttachmentSubtables(GetFont(), data, length,
    645                                        GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
    646 }
    647 
    648 // Lookup Type 5:
    649 // MarkToLigature Attachment Positioning Subtable
    650 bool OpenTypeGPOS::ParseMarkToLigatureAttachment(const uint8_t *data,
    651                                                 const size_t length) {
    652  return ParseMarkToAttachmentSubtables(GetFont(), data, length,
    653                                        GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
    654 }
    655 
    656 // Lookup Type 6:
    657 // MarkToMark Attachment Positioning Subtable
    658 bool OpenTypeGPOS::ParseMarkToMarkAttachment(const uint8_t *data,
    659                                             const size_t length) {
    660  return ParseMarkToAttachmentSubtables(GetFont(), data, length,
    661                                        GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
    662 }
    663 
    664 // Lookup Type 7:
    665 // Contextual Positioning Subtables
    666 // OpenTypeLayoutTable::ParseContextSubtable()
    667 
    668 // Lookup Type 8:
    669 // Chaining Contexual Positioning Subtable
    670 // OpenTypeLayoutTable::ParseChainingContextSubtable()
    671 
    672 // Lookup Type 9:
    673 // Extension Positioning
    674 // OpenTypeLayoutTable::ParseExtensionSubtable
    675 
    676 
    677 bool OpenTypeGPOS::ValidLookupSubtableType(const uint16_t lookup_type,
    678                                           bool extension) const {
    679  if (extension && lookup_type == GPOS_TYPE_EXTENSION_POSITIONING)
    680    return false;
    681  return lookup_type >= GPOS_TYPE_SINGLE_ADJUSTMENT && lookup_type < GPOS_TYPE_RESERVED;
    682 }
    683 
    684 bool OpenTypeGPOS::ParseLookupSubtable(const uint8_t *data, const size_t length,
    685                                       const uint16_t lookup_type) {
    686  switch (lookup_type) {
    687    case GPOS_TYPE_SINGLE_ADJUSTMENT:
    688      return ParseSingleAdjustment(data, length);
    689    case GPOS_TYPE_PAIR_ADJUSTMENT:
    690      return ParsePairAdjustment(data, length);
    691    case GPOS_TYPE_CURSIVE_ATTACHMENT:
    692      return ParseCursiveAttachment(data, length);
    693    case GPOS_TYPE_MARK_TO_BASE_ATTACHMENT:
    694      return ParseMarkToBaseAttachment(data, length);
    695    case GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT:
    696      return ParseMarkToLigatureAttachment(data, length);
    697    case GPOS_TYPE_MARK_TO_MARK_ATTACHMENT:
    698      return ParseMarkToMarkAttachment(data, length);
    699    case GPOS_TYPE_CONTEXT_POSITIONING:
    700      return ParseContextSubtable(data, length);
    701    case GPOS_TYPE_CHAINED_CONTEXT_POSITIONING:
    702      return ParseChainingContextSubtable(data, length);
    703    case GPOS_TYPE_EXTENSION_POSITIONING:
    704      return ParseExtensionSubtable(data, length);
    705  }
    706  return false;
    707 }
    708 
    709 }  // namespace ots
    710 
    711 #undef TABLE_NAME