Add GPOS table support.

- Added src/gpos.{cc,h} for supporting GPOS table.
- Some lookup type subtables (e.g. context positioning subtable and chaining
  contexual positioning subtable) have the same structure as those in GSUB
  table. So I implemented such lookup type subtable parsers in lookup.cc.
  These will also be used for parsing GSUB table when I add GSUB support.
- In parsing ClassDefFormat, we now allow class value is 0 because there is no
  reason to reject such value.
- Changed some interface/implementation of parsing lookup list table to make
  them more clear.

As far as I checked, some fonts contain invalid GPOS table and OTS will drop
GPOS table of them. See comments in gpos.cc for more detail.

BUG=27131
TEST=http://code.google.com/p/ots/wiki/HowToTestOts (Verified with ~4000 TrueType/OpenType fonts)


git-svn-id: http://ots.googlecode.com/svn/trunk@52 a4e77c2c-9104-11de-800e-5b313e0d2bf3
diff --git a/src/layout.cc b/src/layout.cc
index 74ca109..654d82f 100644
--- a/src/layout.cc
+++ b/src/layout.cc
@@ -24,6 +24,8 @@
 const uint16_t kMarkAttachmentTypeMask = 0xFF00;
 // The maximum type number of format for device tables.
 const uint16_t kMaxDeltaFormatType = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
 
 struct ScriptRecord {
   uint32_t tag;
@@ -165,14 +167,9 @@
   return true;
 }
 
-bool LookupTypeParserLess(const ots::LookupTypeParser& parser,
-                          const uint16_t type) {
-  return parser.type < type;
-}
-
 bool ParseLookupTable(ots::OpenTypeFile *file, const uint8_t *data,
-                      const size_t length, const size_t num_types,
-                      const ots::LookupTypeParser* parsers) {
+                      const size_t length,
+                      const ots::LookupSubtableParser* parser) {
   ots::Buffer subtable(data, length);
 
   uint16_t lookup_type = 0;
@@ -184,7 +181,7 @@
     return OTS_FAILURE();
   }
 
-  if (lookup_type == 0 || lookup_type > num_types) {
+  if (lookup_type == 0 || lookup_type > parser->num_types) {
     return OTS_FAILURE();
   }
 
@@ -233,14 +230,8 @@
 
   // Parse lookup subtables for this lookup type.
   for (unsigned i = 0; i < subtable_count; ++i) {
-    const ots::LookupTypeParser *parser =
-        std::lower_bound(parsers, parsers + num_types, lookup_type,
-                         LookupTypeParserLess);
-    if (parser == parsers + num_types || parser->type != lookup_type ||
-        !parser->parse) {
-      return OTS_FAILURE();
-    }
-    if (!parser->parse(file, data + subtables[i], length - subtables[i])) {
+    if (!parser->Parse(file, data + subtables[i], length - subtables[i],
+                       lookup_type)) {
       return OTS_FAILURE();
     }
   }
@@ -279,7 +270,7 @@
     if (!subtable.ReadU16(&class_value)) {
       return OTS_FAILURE();
     }
-    if (class_value == 0 || class_value > num_classes) {
+    if (class_value > num_classes) {
       OTS_WARNING("bad class value: %u", class_value);
       return OTS_FAILURE();
     }
@@ -321,7 +312,7 @@
       OTS_WARNING("glyph range is overlapping.");
       return OTS_FAILURE();
     }
-    if (class_value == 0 || class_value > num_classes) {
+    if (class_value > num_classes) {
       OTS_WARNING("bad class value: %u", class_value);
       return OTS_FAILURE();
     }
@@ -405,10 +396,745 @@
   return true;
 }
 
+// Parsers for Contextual subtables in GSUB/GPOS tables.
+
+bool ParseLookupRecord(ots::Buffer *subtable, const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  uint16_t sequence_index = 0;
+  uint16_t lookup_list_index = 0;
+  if (!subtable->ReadU16(&sequence_index) ||
+      !subtable->ReadU16(&lookup_list_index)) {
+    return OTS_FAILURE();
+  }
+  if (sequence_index >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  if (lookup_list_index >= num_lookups) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool ParseRuleSubtable(const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < glyph_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE();
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+bool ParseRuleSetTable(const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t rule_count = 0;
+  if (!subtable.ReadU16(&rule_count)) {
+    return OTS_FAILURE();
+  }
+  const unsigned rule_end = static_cast<unsigned>(2) +
+      rule_count * 2;
+  if (rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < rule_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE();
+    }
+    if (offset_rule < rule_end || offset_rule >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseRuleSubtable(data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat1(const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&rule_set_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned rule_set_end = static_cast<unsigned>(6) +
+      rule_set_count * 2;
+  if (rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage < rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < rule_set_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE();
+    }
+    if (offset_rule < rule_set_end || offset_rule >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseRuleSetTable(data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassRuleTable(const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+
+  // ClassRule table contains an array of classes. Each value of classes
+  // could take arbitrary values including zero so we don't check these value.
+  const unsigned num_classes = glyph_count - static_cast<unsigned>(1);
+  if (!subtable.Skip(2 * num_classes)) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+bool ParseClassSetTable(const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t class_rule_count = 0;
+  if (!subtable.ReadU16(&class_rule_count)) {
+    return OTS_FAILURE();
+  }
+  const unsigned class_rule_end = static_cast<unsigned>(2) +
+      class_rule_count * 2;
+  if (class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < class_rule_count; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE();
+    }
+    if (offset_class_rule < class_rule_end || offset_class_rule >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseClassRuleTable(data + offset_class_rule,
+                             length - offset_class_rule, num_glyphs,
+                             num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat2(const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_class_def = 0;
+  uint16_t class_set_cnt = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_class_def) ||
+      !subtable.ReadU16(&class_set_cnt)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned class_set_end = static_cast<unsigned>(8) +
+      class_set_cnt * 2;
+  if (class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage < class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_class_def < class_set_end || offset_class_def >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseClassDefTable(data + offset_class_def,
+                               length - offset_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < class_set_cnt; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE();
+    }
+    if (offset_class_rule) {
+      if (offset_class_rule < class_set_end || offset_class_rule >= length) {
+        return OTS_FAILURE();
+      }
+      if (!ParseClassSetTable(data + offset_class_rule,
+                              length - offset_class_rule, num_glyphs,
+                              num_lookups)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat3(const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (glyph_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  const unsigned lookup_record_end = static_cast<unsigned>(6) +
+      glyph_count * 2 + lookup_count * 4;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t offset_coverage = 0;
+    if (!subtable.ReadU16(&offset_coverage)) {
+      return OTS_FAILURE();
+    }
+    if (offset_coverage < lookup_record_end || offset_coverage >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseCoverageTable(data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+// Parsers for Chaning Contextual subtables in GSUB/GPOS tables.
+
+bool ParseChainRuleSubtable(const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE();
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE();
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE();
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < input_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE();
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE();
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE();
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainRuleSetTable(const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_rule_count = 0;
+  if (!subtable.ReadU16(&chain_rule_count)) {
+    return OTS_FAILURE();
+  }
+  const unsigned chain_rule_end = static_cast<unsigned>(2) +
+      chain_rule_count * 2;
+  if (chain_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < chain_rule_count; ++i) {
+    uint16_t offset_chain_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_rule)) {
+      return OTS_FAILURE();
+    }
+    if (offset_chain_rule < chain_rule_end || offset_chain_rule >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseChainRuleSubtable(data + offset_chain_rule,
+                                length - offset_chain_rule,
+                                num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat1(const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t chain_rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&chain_rule_set_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned chain_rule_set_end = static_cast<unsigned>(6) +
+      chain_rule_set_count * 2;
+  if (chain_rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage < chain_rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < chain_rule_set_count; ++i) {
+    uint16_t offset_chain_rule_set = 0;
+    if (!subtable.ReadU16(&offset_chain_rule_set)) {
+      return OTS_FAILURE();
+    }
+    if (offset_chain_rule_set < chain_rule_set_end ||
+        offset_chain_rule_set >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseChainRuleSetTable(data + offset_chain_rule_set,
+                                   length - offset_chain_rule_set,
+                                   num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassRuleSubtable(const uint8_t *data, const size_t length,
+                                 const uint16_t num_glyphs,
+                                 const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  // In this subtable, we don't check the value of classes for now since
+  // these could take arbitrary values.
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE();
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  if (!subtable.Skip(2 * backtrack_count)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE();
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  if (!subtable.Skip(2 * (input_count - 1))) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE();
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  if (!subtable.Skip(2 * lookahead_count)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassSetTable(const uint8_t *data, const size_t length,
+                             const uint16_t num_glyphs,
+                             const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_class_rule_count = 0;
+  if (!subtable.ReadU16(&chain_class_rule_count)) {
+    return OTS_FAILURE();
+  }
+  const unsigned chain_class_rule_end = static_cast<unsigned>(2) +
+      chain_class_rule_count * 2;
+  if (chain_class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < chain_class_rule_count; ++i) {
+    uint16_t offset_chain_class_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_class_rule)) {
+      return OTS_FAILURE();
+    }
+    if (offset_chain_class_rule < chain_class_rule_end ||
+        offset_chain_class_rule >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseChainClassRuleSubtable(data + offset_chain_class_rule,
+                                     length - offset_chain_class_rule,
+                                     num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat2(const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_backtrack_class_def = 0;
+  uint16_t offset_input_class_def = 0;
+  uint16_t offset_lookahead_class_def = 0;
+  uint16_t chain_class_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_backtrack_class_def) ||
+      !subtable.ReadU16(&offset_input_class_def) ||
+      !subtable.ReadU16(&offset_lookahead_class_def) ||
+      !subtable.ReadU16(&chain_class_set_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned chain_class_set_end = static_cast<unsigned>(12) +
+      chain_class_set_count * 2;
+  if (chain_class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage < chain_class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  // Classes for backtrack/lookahead sequences might not be defined.
+  if (offset_backtrack_class_def) {
+    if (offset_backtrack_class_def < chain_class_set_end ||
+        offset_backtrack_class_def >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseClassDefTable(data + offset_backtrack_class_def,
+                                 length - offset_backtrack_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (offset_input_class_def < chain_class_set_end ||
+      offset_input_class_def >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseClassDefTable(data + offset_input_class_def,
+                               length - offset_input_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_lookahead_class_def) {
+    if (offset_lookahead_class_def < chain_class_set_end ||
+        offset_lookahead_class_def >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseClassDefTable(data + offset_lookahead_class_def,
+                                 length - offset_lookahead_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  for (unsigned i = 0; i < chain_class_set_count; ++i) {
+    uint16_t offset_chain_class_set = 0;
+    if (!subtable.ReadU16(&offset_chain_class_set)) {
+      return OTS_FAILURE();
+    }
+    // |offset_chain_class_set| could be NULL.
+    if (offset_chain_class_set) {
+      if (offset_chain_class_set < chain_class_set_end ||
+          offset_chain_class_set >= length) {
+        return OTS_FAILURE();
+      }
+      if (!ParseChainClassSetTable(data + offset_chain_class_set,
+                                   length - offset_chain_class_set,
+                                   num_glyphs, num_lookups)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat3(const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_count);
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    if (!subtable.ReadU16(&offsets_backtrack[i])) {
+      return OTS_FAILURE();
+    }
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE();
+  }
+  if (input_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  std::vector<uint16_t> offsets_input;
+  offsets_input.reserve(input_count);
+  for (unsigned i = 0; i < input_count; ++i) {
+    if (!subtable.ReadU16(&offsets_input[i])) {
+      return OTS_FAILURE();
+    }
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE();
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE();
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_count);
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    if (!subtable.ReadU16(&offsets_lookahead[i])) {
+      return OTS_FAILURE();
+    }
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const unsigned lookup_record_end = static_cast<unsigned>(10) +
+      (backtrack_count + input_count + lookahead_count) * 2 + lookup_count * 4;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    if (offsets_backtrack[i] < lookup_record_end ||
+        offsets_backtrack[i] >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseCoverageTable(data + offsets_backtrack[i],
+                                 length - offsets_backtrack[i], num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  for (unsigned i = 0; i < input_count; ++i) {
+    if (offsets_input[i] < lookup_record_end || offsets_input[i] >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseCoverageTable(data + offsets_input[i],
+                                 length - offsets_input[i], num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    if (offsets_lookahead[i] < lookup_record_end ||
+        offsets_lookahead[i] >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseCoverageTable(data + offsets_lookahead[i],
+                                 length - offsets_lookahead[i], num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
 }  // namespace
 
 namespace ots {
 
+bool LookupSubtableParser::Parse(const OpenTypeFile *file, const uint8_t *data,
+                                 const size_t length,
+                                 const uint16_t lookup_type) const {
+  for (unsigned i = 0; i < num_types; ++i) {
+    if (parsers[i].type == lookup_type && parsers[i].parse) {
+      if (!parsers[i].parse(file, data, length)) {
+        return OTS_FAILURE();
+      }
+      return true;
+    }
+  }
+  return OTS_FAILURE();
+}
+
 // Parsing ScriptListTable requires number of features so we need to
 // parse FeatureListTable before calling this function.
 bool ParseScriptListTable(const uint8_t *data, const size_t length,
@@ -506,8 +1232,8 @@
 // obtain the number of lookups because parsing FeatureTableList requires
 // the number.
 bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
-                          const size_t length, const size_t num_types,
-                          const LookupTypeParser* parsers,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
                           uint16_t *num_lookups) {
   Buffer subtable(data, length);
 
@@ -533,7 +1259,7 @@
 
   for (unsigned i = 0; i < *num_lookups; ++i) {
     if (!ParseLookupTable(file, data + lookups[i], length - lookups[i],
-                          num_types, parsers)) {
+                          parser)) {
       return OTS_FAILURE();
     }
   }
@@ -607,5 +1333,101 @@
   return true;
 }
 
+bool ParseContextSubtable(const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE();
+  }
+
+  if (format == 1) {
+    if (!ParseContextFormat1(data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 2) {
+    if (!ParseContextFormat2(data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 3) {
+    if (!ParseContextFormat3(data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  } else {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseChainingContextSubtable(const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE();
+  }
+
+  if (format == 1) {
+    if (!ParseChainContextFormat1(data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 2) {
+    if (!ParseChainContextFormat2(data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 3) {
+    if (!ParseChainContextFormat3(data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  } else {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t lookup_type = 0;
+  uint32_t offset_extension = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU32(&offset_extension)) {
+    return OTS_FAILURE();
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE();
+  }
+  // |lookup_type| should be other than |parser->extension_type|.
+  if (lookup_type < 1 || lookup_type > parser->num_types ||
+      lookup_type == parser->extension_type) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned format_end = static_cast<unsigned>(8);
+  if (offset_extension < format_end ||
+      offset_extension >= length) {
+    return OTS_FAILURE();
+  }
+
+  // Parse the extension subtable of |lookup_type|.
+  if (!parser->Parse(file, data + offset_extension, length - offset_extension,
+                     lookup_type)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
 }  // namespace ots