Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "gpos.h"
      6 
      7 #include <limits>
      8 #include <vector>
      9 
     10 #include "gdef.h"
     11 #include "gsub.h"
     12 #include "layout.h"
     13 #include "maxp.h"
     14 
     15 // GPOS - The Glyph Positioning Table
     16 // http://www.microsoft.com/typography/otspec/gpos.htm
     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 size of gpos header.
     34 const unsigned kGposHeaderSize = 10;
     35 // The maximum format number for anchor tables.
     36 const uint16_t kMaxAnchorFormat = 3;
     37 // The maximum number of class value.
     38 const uint16_t kMaxClassDefValue = 0xFFFF;
     39 
     40 // Lookup type parsers.
     41 bool ParseSingleAdjustment(const ots::OpenTypeFile *file,
     42                            const uint8_t *data, const size_t length);
     43 bool ParsePairAdjustment(const ots::OpenTypeFile *file,
     44                          const uint8_t *data, const size_t length);
     45 bool ParseCursiveAttachment(const ots::OpenTypeFile *file,
     46                             const uint8_t *data, const size_t length);
     47 bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
     48                                const uint8_t *data, const size_t length);
     49 bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
     50                                    const uint8_t *data, const size_t length);
     51 bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
     52                                const uint8_t *data, const size_t length);
     53 bool ParseContextPositioning(const ots::OpenTypeFile *file,
     54                              const uint8_t *data, const size_t length);
     55 bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
     56                                     const uint8_t *data, const size_t length);
     57 bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
     58                                const uint8_t *data, const size_t length);
     59 
     60 const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
     61   {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment},
     62   {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment},
     63   {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment},
     64   {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
     65   {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
     66   {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
     67   {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning},
     68   {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
     69   {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning}
     70 };
     71 
     72 const ots::LookupSubtableParser kGposLookupSubtableParser = {
     73   arraysize(kGposTypeParsers),
     74   GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
     75 };
     76 
     77 // Shared Tables: ValueRecord, Anchor Table, and MarkArray
     78 
     79 bool ParseValueRecord(ots::Buffer* subtable, const uint8_t *data,
     80                       const size_t length, const uint16_t value_format) {
     81   // Check existence of adjustment fields.
     82   for (unsigned i = 0; i < 4; ++i) {
     83     if ((value_format >> i) & 0x1) {
     84       // Just read the field since these fileds could take an arbitrary values.
     85       if (!subtable->Skip(2)) {
     86         return OTS_FAILURE();
     87       }
     88     }
     89   }
     90 
     91   // Check existence of offsets to device table.
     92   for (unsigned i = 0; i < 4; ++i) {
     93     if ((value_format >> (i + 4)) & 0x1) {
     94       uint16_t offset = 0;
     95       if (!subtable->ReadU16(&offset)) {
     96         return OTS_FAILURE();
     97       }
     98       if (offset) {
     99         // TODO(bashi): Is it possible that device tables locate before
    100         // this record? No fonts contain such offset AKAIF.
    101         if (offset >= length) {
    102           return OTS_FAILURE();
    103         }
    104         if (!ots::ParseDeviceTable(data + offset, length - offset)) {
    105           return OTS_FAILURE();
    106         }
    107       }
    108     }
    109   }
    110   return true;
    111 }
    112 
    113 bool ParseAnchorTable(const uint8_t *data, const size_t length) {
    114   ots::Buffer subtable(data, length);
    115 
    116   uint16_t format = 0;
    117   // Read format and skip 2 2-byte fields that could be arbitrary values.
    118   if (!subtable.ReadU16(&format) ||
    119       !subtable.Skip(4)) {
    120     return OTS_FAILURE();
    121   }
    122 
    123   if (format == 0 || format > kMaxAnchorFormat) {
    124     return OTS_FAILURE();
    125   }
    126 
    127   // Format 2 and 3 has additional fields.
    128   if (format == 2) {
    129     // Format 2 provides an index to a glyph contour point, which will take
    130     // arbitrary value.
    131     uint16_t anchor_point = 0;
    132     if (!subtable.ReadU16(&anchor_point)) {
    133       return OTS_FAILURE();
    134     }
    135   } else if (format == 3) {
    136     uint16_t offset_x_device = 0;
    137     uint16_t offset_y_device = 0;
    138     if (!subtable.ReadU16(&offset_x_device) ||
    139         !subtable.ReadU16(&offset_y_device)) {
    140       return OTS_FAILURE();
    141     }
    142     const unsigned format_end = static_cast<unsigned>(10);
    143     if (offset_x_device) {
    144       if (offset_x_device < format_end || offset_x_device >= length) {
    145         return OTS_FAILURE();
    146       }
    147       if (!ots::ParseDeviceTable(data + offset_x_device,
    148                                  length - offset_x_device)) {
    149         return OTS_FAILURE();
    150       }
    151     }
    152     if (offset_y_device) {
    153       if (offset_y_device < format_end || offset_y_device >= length) {
    154         return OTS_FAILURE();
    155       }
    156       if (!ots::ParseDeviceTable(data + offset_y_device,
    157                                  length - offset_y_device)) {
    158         return OTS_FAILURE();
    159       }
    160     }
    161   }
    162   return true;
    163 }
    164 
    165 bool ParseMarkArrayTable(const uint8_t *data, const size_t length,
    166                          const uint16_t class_count) {
    167   ots::Buffer subtable(data, length);
    168 
    169   uint16_t mark_count = 0;
    170   if (!subtable.ReadU16(&mark_count)) {
    171     return OTS_FAILURE();
    172   }
    173 
    174   // MarkRecord consists of 4-bytes.
    175   const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2;
    176   if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
    177     return OTS_FAILURE();
    178   }
    179   for (unsigned i = 0; i < mark_count; ++i) {
    180     uint16_t class_value = 0;
    181     uint16_t offset_mark_anchor = 0;
    182     if (!subtable.ReadU16(&class_value) ||
    183         !subtable.ReadU16(&offset_mark_anchor)) {
    184       return OTS_FAILURE();
    185     }
    186     // |class_value| may take arbitrary values including 0 here so we don't
    187     // check the value.
    188     if (offset_mark_anchor < mark_records_end ||
    189         offset_mark_anchor >= length) {
    190       return OTS_FAILURE();
    191     }
    192     if (!ParseAnchorTable(data + offset_mark_anchor,
    193                           length - offset_mark_anchor)) {
    194       return OTS_FAILURE();
    195     }
    196   }
    197 
    198   return true;
    199 }
    200 
    201 // Lookup Type 1:
    202 // Single Adjustment Positioning Subtable
    203 bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
    204                            const size_t length) {
    205   ots::Buffer subtable(data, length);
    206 
    207   uint16_t format = 0;
    208   uint16_t offset_coverage = 0;
    209   uint16_t value_format = 0;
    210   if (!subtable.ReadU16(&format) ||
    211       !subtable.ReadU16(&offset_coverage) ||
    212       !subtable.ReadU16(&value_format)) {
    213     return OTS_FAILURE();
    214   }
    215 
    216   if (format == 1) {
    217     // Format 1 exactly one value record.
    218     if (!ParseValueRecord(&subtable, data, length, value_format)) {
    219       return OTS_FAILURE();
    220     }
    221   } else if (format == 2) {
    222     uint16_t value_count = 0;
    223     if (!subtable.ReadU16(&value_count)) {
    224       return OTS_FAILURE();
    225     }
    226     for (unsigned i = 0; i < value_count; ++i) {
    227       if (!ParseValueRecord(&subtable, data, length, value_format)) {
    228         return OTS_FAILURE();
    229       }
    230     }
    231   } else {
    232     return OTS_FAILURE();
    233   }
    234 
    235   if (offset_coverage < subtable.offset() || offset_coverage >= length) {
    236     return OTS_FAILURE();
    237   }
    238 
    239   if (!ots::ParseCoverageTable(data + offset_coverage,
    240                                length - offset_coverage,
    241                                file->maxp->num_glyphs)) {
    242     return OTS_FAILURE();
    243   }
    244 
    245   return true;
    246 }
    247 
    248 bool ParsePairSetTable(const uint8_t *data, const size_t length,
    249                        const uint16_t value_format1,
    250                        const uint16_t value_format2,
    251                        const uint16_t num_glyphs) {
    252   ots::Buffer subtable(data, length);
    253 
    254   uint16_t value_count = 0;
    255   if (!subtable.ReadU16(&value_count)) {
    256     return OTS_FAILURE();
    257   }
    258   for (unsigned i = 0; i < value_count; ++i) {
    259     // Check pair value record.
    260     uint16_t glyph_id = 0;
    261     if (!subtable.ReadU16(&glyph_id)) {
    262       return OTS_FAILURE();
    263     }
    264     if (glyph_id >= num_glyphs) {
    265       return OTS_FAILURE();
    266     }
    267     if (!ParseValueRecord(&subtable, data, length, value_format1)) {
    268       return OTS_FAILURE();
    269     }
    270     if (!ParseValueRecord(&subtable, data, length, value_format2)) {
    271       return OTS_FAILURE();
    272     }
    273   }
    274   return true;
    275 }
    276 
    277 bool ParsePairPosFormat1(const uint8_t *data, const size_t length,
    278                          const uint16_t value_format1,
    279                          const uint16_t value_format2,
    280                          const uint16_t num_glyphs) {
    281   ots::Buffer subtable(data, length);
    282 
    283   // Skip 8 bytes that are already read before.
    284   if (!subtable.Skip(8)) {
    285     return OTS_FAILURE();
    286   }
    287 
    288   uint16_t pair_set_count = 0;
    289   if (!subtable.ReadU16(&pair_set_count)) {
    290     return OTS_FAILURE();
    291   }
    292 
    293   const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10;
    294   if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
    295     return OTS_FAILURE();
    296   }
    297   for (unsigned i = 0; i < pair_set_count; ++i) {
    298     uint16_t pair_set_offset = 0;
    299     if (!subtable.ReadU16(&pair_set_offset)) {
    300       return OTS_FAILURE();
    301     }
    302     if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
    303       return OTS_FAILURE();
    304     }
    305     // Check pair set tables
    306     if (!ParsePairSetTable(data + pair_set_offset, length - pair_set_offset,
    307                            value_format1, value_format2,
    308                            num_glyphs)) {
    309       return OTS_FAILURE();
    310     }
    311   }
    312 
    313   return true;
    314 }
    315 
    316 bool ParsePairPosFormat2(const uint8_t *data, const size_t length,
    317                          const uint16_t value_format1,
    318                          const uint16_t value_format2,
    319                          const uint16_t num_glyphs) {
    320   ots::Buffer subtable(data, length);
    321 
    322   // Skip 8 bytes that are already read before.
    323   if (!subtable.Skip(8)) {
    324     return OTS_FAILURE();
    325   }
    326 
    327   uint16_t offset_class_def1 = 0;
    328   uint16_t offset_class_def2 = 0;
    329   uint16_t class1_count = 0;
    330   uint16_t class2_count = 0;
    331   if (!subtable.ReadU16(&offset_class_def1) ||
    332       !subtable.ReadU16(&offset_class_def2) ||
    333       !subtable.ReadU16(&class1_count) ||
    334       !subtable.ReadU16(&class2_count)) {
    335     return OTS_FAILURE();
    336   }
    337 
    338   // Check class 1 records.
    339   for (unsigned i = 0; i < class1_count; ++i) {
    340     // Check class 2 records.
    341     for (unsigned j = 0; j < class2_count; ++j) {
    342       if (value_format1 && !ParseValueRecord(&subtable, data, length,
    343                                              value_format1)) {
    344         return OTS_FAILURE();
    345       }
    346       if (value_format2 && !ParseValueRecord(&subtable, data, length,
    347                                              value_format2)) {
    348         return OTS_FAILURE();
    349       }
    350     }
    351   }
    352 
    353   // Check class definition tables.
    354   if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
    355       offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
    356     return OTS_FAILURE();
    357   }
    358   if (!ots::ParseClassDefTable(data + offset_class_def1,
    359                                length - offset_class_def1,
    360                                num_glyphs, kMaxClassDefValue)) {
    361     return OTS_FAILURE();
    362   }
    363   if (!ots::ParseClassDefTable(data + offset_class_def2,
    364                                length - offset_class_def2,
    365                                num_glyphs, kMaxClassDefValue)) {
    366     return OTS_FAILURE();
    367   }
    368 
    369   return true;
    370 }
    371 
    372 // Lookup Type 2:
    373 // Pair Adjustment Positioning Subtable
    374 bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
    375                          const size_t length) {
    376   ots::Buffer subtable(data, length);
    377 
    378   uint16_t format = 0;
    379   uint16_t offset_coverage = 0;
    380   uint16_t value_format1 = 0;
    381   uint16_t value_format2 = 0;
    382   if (!subtable.ReadU16(&format) ||
    383       !subtable.ReadU16(&offset_coverage) ||
    384       !subtable.ReadU16(&value_format1) ||
    385       !subtable.ReadU16(&value_format2)) {
    386     return OTS_FAILURE();
    387   }
    388 
    389   if (format == 1) {
    390     if (!ParsePairPosFormat1(data, length, value_format1, value_format2,
    391                              file->maxp->num_glyphs)) {
    392       return OTS_FAILURE();
    393     }
    394   } else if (format == 2) {
    395     if (!ParsePairPosFormat2(data, length, value_format1, value_format2,
    396                              file->maxp->num_glyphs)) {
    397       return OTS_FAILURE();
    398     }
    399   } else {
    400     return OTS_FAILURE();
    401   }
    402 
    403   if (offset_coverage < subtable.offset() || offset_coverage >= length) {
    404     return OTS_FAILURE();
    405   }
    406   if (!ots::ParseCoverageTable(data + offset_coverage,
    407                                length - offset_coverage,
    408                                file->maxp->num_glyphs)) {
    409     return OTS_FAILURE();
    410   }
    411 
    412   return true;
    413 }
    414 
    415 // Lookup Type 3
    416 // Cursive Attachment Positioning Subtable
    417 bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data,
    418                             const size_t length) {
    419   ots::Buffer subtable(data, length);
    420 
    421   uint16_t format = 0;
    422   uint16_t offset_coverage = 0;
    423   uint16_t entry_exit_count = 0;
    424   if (!subtable.ReadU16(&format) ||
    425       !subtable.ReadU16(&offset_coverage) ||
    426       !subtable.ReadU16(&entry_exit_count)) {
    427     return OTS_FAILURE();
    428   }
    429 
    430   if (format != 1) {
    431     return OTS_FAILURE();
    432   }
    433 
    434   // Check entry exit records.
    435   const unsigned entry_exit_records_end =
    436       2 * static_cast<unsigned>(entry_exit_count) + 6;
    437   if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
    438     return OTS_FAILURE();
    439   }
    440   for (unsigned i = 0; i < entry_exit_count; ++i) {
    441     uint16_t offset_entry_anchor = 0;
    442     uint16_t offset_exit_anchor = 0;
    443     if (!subtable.ReadU16(&offset_entry_anchor) ||
    444         !subtable.ReadU16(&offset_exit_anchor)) {
    445       return OTS_FAILURE();
    446     }
    447     // These offsets could be NULL.
    448     if (offset_entry_anchor) {
    449       if (offset_entry_anchor < entry_exit_records_end ||
    450           offset_entry_anchor >= length) {
    451         return OTS_FAILURE();
    452       }
    453       if (!ParseAnchorTable(data + offset_entry_anchor,
    454                             length - offset_entry_anchor)) {
    455         return OTS_FAILURE();
    456       }
    457     }
    458     if (offset_exit_anchor) {
    459       if (offset_exit_anchor < entry_exit_records_end ||
    460          offset_exit_anchor >= length) {
    461         return OTS_FAILURE();
    462       }
    463       if (!ParseAnchorTable(data + offset_exit_anchor,
    464                             length - offset_exit_anchor)) {
    465         return OTS_FAILURE();
    466       }
    467     }
    468   }
    469 
    470   if (offset_coverage < subtable.offset() || offset_coverage >= length) {
    471     return OTS_FAILURE();
    472   }
    473   if (!ots::ParseCoverageTable(data + offset_coverage,
    474                                length - offset_coverage,
    475                                file->maxp->num_glyphs)) {
    476     return OTS_FAILURE();
    477   }
    478 
    479   return true;
    480 }
    481 
    482 bool ParseAnchorArrayTable(const uint8_t *data, const size_t length,
    483                            const uint16_t class_count) {
    484   ots::Buffer subtable(data, length);
    485 
    486   uint16_t record_count = 0;
    487   if (!subtable.ReadU16(&record_count)) {
    488     return OTS_FAILURE();
    489   }
    490 
    491   const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) *
    492       static_cast<unsigned>(class_count) + 2;
    493   if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
    494     return OTS_FAILURE();
    495   }
    496   for (unsigned i = 0; i < record_count; ++i) {
    497     for (unsigned j = 0; j < class_count; ++j) {
    498       uint16_t offset_record = 0;
    499       if (!subtable.ReadU16(&offset_record)) {
    500         return OTS_FAILURE();
    501       }
    502       // |offset_record| could be NULL.
    503       if (offset_record) {
    504         if (offset_record < anchor_array_end || offset_record >= length) {
    505           return OTS_FAILURE();
    506         }
    507         if (!ParseAnchorTable(data + offset_record,
    508                               length - offset_record)) {
    509           return OTS_FAILURE();
    510         }
    511       }
    512     }
    513   }
    514   return true;
    515 }
    516 
    517 bool ParseLigatureArrayTable(const uint8_t *data, const size_t length,
    518                              const uint16_t class_count) {
    519   ots::Buffer subtable(data, length);
    520 
    521   uint16_t ligature_count = 0;
    522   if (!subtable.ReadU16(&ligature_count)) {
    523     return OTS_FAILURE();
    524   }
    525   for (unsigned i = 0; i < ligature_count; ++i) {
    526     uint16_t offset_ligature_attach = 0;
    527     if (!subtable.ReadU16(&offset_ligature_attach)) {
    528       return OTS_FAILURE();
    529     }
    530     if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
    531       return OTS_FAILURE();
    532     }
    533     if (!ParseAnchorArrayTable(data + offset_ligature_attach,
    534                                length - offset_ligature_attach, class_count)) {
    535       return OTS_FAILURE();
    536     }
    537   }
    538   return true;
    539 }
    540 
    541 // Common parser for Lookup Type 4, 5 and 6.
    542 bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file,
    543                                     const uint8_t *data, const size_t length,
    544                                     const GPOS_TYPE type) {
    545   ots::Buffer subtable(data, length);
    546 
    547   uint16_t format = 0;
    548   uint16_t offset_coverage1 = 0;
    549   uint16_t offset_coverage2 = 0;
    550   uint16_t class_count = 0;
    551   uint16_t offset_mark_array = 0;
    552   uint16_t offset_type_specific_array = 0;
    553   if (!subtable.ReadU16(&format) ||
    554       !subtable.ReadU16(&offset_coverage1) ||
    555       !subtable.ReadU16(&offset_coverage2) ||
    556       !subtable.ReadU16(&class_count) ||
    557       !subtable.ReadU16(&offset_mark_array) ||
    558       !subtable.ReadU16(&offset_type_specific_array)) {
    559     return OTS_FAILURE();
    560   }
    561 
    562   if (format != 1) {
    563     return OTS_FAILURE();
    564   }
    565 
    566   const unsigned header_end = static_cast<unsigned>(subtable.offset());
    567   if (header_end > std::numeric_limits<uint16_t>::max()) {
    568     return OTS_FAILURE();
    569   }
    570   if (offset_coverage1 < header_end || offset_coverage1 >= length) {
    571     return OTS_FAILURE();
    572   }
    573   if (!ots::ParseCoverageTable(data + offset_coverage1,
    574                                length - offset_coverage1,
    575                                file->maxp->num_glyphs)) {
    576     return OTS_FAILURE();
    577   }
    578   if (offset_coverage2 < header_end || offset_coverage2 >= length) {
    579     return OTS_FAILURE();
    580   }
    581   if (!ots::ParseCoverageTable(data + offset_coverage2,
    582                                length - offset_coverage2,
    583                                file->maxp->num_glyphs)) {
    584     return OTS_FAILURE();
    585   }
    586 
    587   if (offset_mark_array < header_end || offset_mark_array >= length) {
    588     return OTS_FAILURE();
    589   }
    590   if (!ParseMarkArrayTable(data + offset_mark_array,
    591                            length - offset_mark_array, class_count)) {
    592     return OTS_FAILURE();
    593   }
    594 
    595   if (offset_type_specific_array < header_end ||
    596       offset_type_specific_array >= length) {
    597     return OTS_FAILURE();
    598   }
    599   if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT ||
    600       type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) {
    601     if (!ParseAnchorArrayTable(data + offset_type_specific_array,
    602                                length - offset_type_specific_array,
    603                                class_count)) {
    604       return OTS_FAILURE();
    605     }
    606   } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) {
    607     if (!ParseLigatureArrayTable(data + offset_type_specific_array,
    608                                  length - offset_type_specific_array,
    609                                  class_count)) {
    610       return OTS_FAILURE();
    611     }
    612   } else {
    613     return OTS_FAILURE();
    614   }
    615 
    616   return true;
    617 }
    618 
    619 // Lookup Type 4:
    620 // MarkToBase Attachment Positioning Subtable
    621 bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
    622                                const uint8_t *data, const size_t length) {
    623   return ParseMarkToAttachmentSubtables(file, data, length,
    624                                         GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
    625 }
    626 
    627 // Lookup Type 5:
    628 // MarkToLigature Attachment Positioning Subtable
    629 bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
    630                                    const uint8_t *data, const size_t length) {
    631   return ParseMarkToAttachmentSubtables(file, data, length,
    632                                         GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
    633 }
    634 
    635 // Lookup Type 6:
    636 // MarkToMark Attachment Positioning Subtable
    637 bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
    638                                const uint8_t *data, const size_t length) {
    639   return ParseMarkToAttachmentSubtables(file, data, length,
    640                                         GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
    641 }
    642 
    643 // Lookup Type 7:
    644 // Contextual Positioning Subtables
    645 bool ParseContextPositioning(const ots::OpenTypeFile *file,
    646                              const uint8_t *data, const size_t length) {
    647   return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs,
    648                                    file->gpos->num_lookups);
    649 }
    650 
    651 // Lookup Type 8:
    652 // Chaining Contexual Positioning Subtable
    653 bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
    654                                     const uint8_t *data, const size_t length) {
    655   return ots::ParseChainingContextSubtable(data, length,
    656                                            file->maxp->num_glyphs,
    657                                            file->gpos->num_lookups);
    658 }
    659 
    660 // Lookup Type 9:
    661 // Extension Positioning
    662 bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
    663                                const uint8_t *data, const size_t length) {
    664   return ots::ParseExtensionSubtable(file, data, length,
    665                                      &kGposLookupSubtableParser);
    666 }
    667 
    668 }  // namespace
    669 
    670 #define DROP_THIS_TABLE \
    671   do { file->gpos->data = 0; file->gpos->length = 0; } while (0)
    672 
    673 namespace ots {
    674 
    675 // As far as I checked, following fonts contain invalid GPOS table and
    676 // OTS will drop their GPOS table.
    677 //
    678 // # invalid delta format in device table
    679 // samanata.ttf
    680 //
    681 // # bad size range in device table
    682 // Sarai_07.ttf
    683 //
    684 // # bad offset to PairSetTable
    685 // chandas1-2.ttf
    686 //
    687 // # bad offset to FeatureTable
    688 // glrso12.ttf
    689 // gllr12.ttf
    690 // glbo12.ttf
    691 // glb12.ttf
    692 // glro12.ttf
    693 // glbso12.ttf
    694 // glrc12.ttf
    695 // glrsc12.ttf
    696 // glbs12.ttf
    697 // glrs12.ttf
    698 // glr12.ttf
    699 //
    700 // # ScriptRecords aren't sorted by tag
    701 // Garogier_unhinted.otf
    702 //
    703 // # bad start coverage index in CoverageFormat2
    704 // AndBasR.ttf
    705 // CharisSILB.ttf
    706 // CharisSILBI.ttf
    707 // CharisSILI.ttf
    708 // CharisSILR.ttf
    709 // DoulosSILR.ttf
    710 // GenBasBI.ttf
    711 // GenBasI.ttf
    712 // GenBkBasI.ttf
    713 // GenBkBasB.ttf
    714 // GenBkBasR.ttf
    715 // Padauk-Bold.ttf
    716 // Padauk.ttf
    717 //
    718 // # Contour point indexes aren't sorted
    719 // Arial Unicode.ttf
    720 
    721 bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
    722   // Parsing GPOS table requires num_glyphs which is contained in maxp table.
    723   if (!file->maxp) {
    724     return OTS_FAILURE();
    725   }
    726 
    727   Buffer table(data, length);
    728 
    729   OpenTypeGPOS *gpos = new OpenTypeGPOS;
    730   file->gpos = gpos;
    731 
    732   uint32_t version = 0;
    733   uint16_t offset_script_list = 0;
    734   uint16_t offset_feature_list = 0;
    735   uint16_t offset_lookup_list = 0;
    736   if (!table.ReadU32(&version) ||
    737       !table.ReadU16(&offset_script_list) ||
    738       !table.ReadU16(&offset_feature_list) ||
    739       !table.ReadU16(&offset_lookup_list)) {
    740     return OTS_FAILURE();
    741   }
    742 
    743   if (version != 0x00010000) {
    744     OTS_WARNING("bad GPOS version");
    745     DROP_THIS_TABLE;
    746     return true;
    747   }
    748   if ((offset_script_list < kGposHeaderSize ||
    749        offset_script_list >= length) ||
    750       (offset_feature_list < kGposHeaderSize ||
    751        offset_feature_list >= length) ||
    752       (offset_lookup_list < kGposHeaderSize ||
    753        offset_lookup_list >= length)) {
    754     OTS_WARNING("bad offset in GPOS header");
    755     DROP_THIS_TABLE;
    756     return true;
    757   }
    758 
    759   if (!ParseLookupListTable(file, data + offset_lookup_list,
    760                             length - offset_lookup_list,
    761                             &kGposLookupSubtableParser,
    762                             &gpos->num_lookups)) {
    763     OTS_WARNING("faild to parse lookup list table");
    764     DROP_THIS_TABLE;
    765     return true;
    766   }
    767 
    768   uint16_t num_features = 0;
    769   if (!ParseFeatureListTable(data + offset_feature_list,
    770                              length - offset_feature_list, gpos->num_lookups,
    771                              &num_features)) {
    772     OTS_WARNING("faild to parse feature list table");
    773     DROP_THIS_TABLE;
    774     return true;
    775   }
    776 
    777   if (!ParseScriptListTable(data + offset_script_list,
    778                             length - offset_script_list, num_features)) {
    779     OTS_WARNING("faild to parse script list table");
    780     DROP_THIS_TABLE;
    781     return true;
    782   }
    783 
    784   gpos->data = data;
    785   gpos->length = length;
    786   return true;
    787 }
    788 
    789 bool ots_gpos_should_serialise(OpenTypeFile *file) {
    790   const bool needed_tables_dropped =
    791       (file->gdef && file->gdef->data == NULL) ||
    792       (file->gsub && file->gsub->data == NULL);
    793   return file->gpos != NULL && file->gpos->data != NULL &&
    794       !needed_tables_dropped;
    795 }
    796 
    797 bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) {
    798   if (!out->Write(file->gpos->data, file->gpos->length)) {
    799     return OTS_FAILURE();
    800   }
    801 
    802   return true;
    803 }
    804 
    805 void ots_gpos_free(OpenTypeFile *file) {
    806   delete file->gpos;
    807 }
    808 
    809 }  // namespace ots
    810 
    811