bug 643460 pt 1 - import OTS r.62 from http://code.google.com/p/ots/. r=jfkthame
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Tue, 26 Apr 2011 16:29:36 +0100
changeset 68766 31b6ab15760edada63a494ea72269c46ce55e166
parent 68765 2d5bdea10000bd82e9b2ea014dab54593cf1da72
child 68767 32f760d04b38a52dfae7c6a9bc5d22430baf4d79
push idunknown
push userunknown
push dateunknown
reviewersjfkthame
bugs643460
milestone6.0a1
bug 643460 pt 1 - import OTS r.62 from http://code.google.com/p/ots/. r=jfkthame
gfx/ots/include/opentype-sanitiser.h
gfx/ots/include/ots-memory-stream.h
gfx/ots/src/cff.cc
gfx/ots/src/cff_type2_charstring.h
gfx/ots/src/cmap.cc
gfx/ots/src/cmap.h
gfx/ots/src/gasp.cc
gfx/ots/src/gdef.cc
gfx/ots/src/gdef.h
gfx/ots/src/glyf.cc
gfx/ots/src/gpos.cc
gfx/ots/src/gpos.h
gfx/ots/src/gsub.cc
gfx/ots/src/gsub.h
gfx/ots/src/head.cc
gfx/ots/src/hhea.cc
gfx/ots/src/hhea.h
gfx/ots/src/hmtx.cc
gfx/ots/src/hmtx.h
gfx/ots/src/kern.cc
gfx/ots/src/layout.cc
gfx/ots/src/layout.h
gfx/ots/src/loca.cc
gfx/ots/src/ltsh.cc
gfx/ots/src/maxp.cc
gfx/ots/src/metrics.cc
gfx/ots/src/metrics.h
gfx/ots/src/os2.cc
gfx/ots/src/ots.cc
gfx/ots/src/ots.h
gfx/ots/src/post.cc
gfx/ots/src/vdmx.cc
gfx/ots/src/vhea.cc
gfx/ots/src/vhea.h
gfx/ots/src/vmtx.cc
gfx/ots/src/vmtx.h
gfx/ots/src/vorg.cc
--- a/gfx/ots/include/opentype-sanitiser.h
+++ b/gfx/ots/include/opentype-sanitiser.h
@@ -1,34 +1,28 @@
 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OPENTYPE_SANITISER_H_
 #define OPENTYPE_SANITISER_H_
 
-#if defined(_MSC_VER)
+#if defined(_WIN32)
 typedef signed char int8_t;
 typedef unsigned char uint8_t;
 typedef short int16_t;
 typedef unsigned short uint16_t;
 typedef int int32_t;
 typedef unsigned int uint32_t;
 typedef __int64 int64_t;
 typedef unsigned __int64 uint64_t;
-#else
-#include <stdint.h>
-#endif
-
-#ifdef _WIN32
 #include <winsock2.h>  // for htons/ntohs
-#undef min
-#undef max
 #else
 #include <arpa/inet.h>
+#include <stdint.h>
 #endif
 
 #include <algorithm>  // for std::min
 #include <cassert>
 #include <cstring>
 
 namespace ots {
 
@@ -63,20 +57,18 @@ class OTSStream {
 
     if (chksum_buffer_offset_ == 4) {
       // TODO(yusukes): This cast breaks the strict-aliasing rule.
       chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(chksum_buffer_));
       chksum_buffer_offset_ = 0;
     }
 
     while (length >= 4) {
-      uint32_t tmp;
-      std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
-        sizeof(uint32_t));
-      chksum_ += ntohl(tmp);
+      chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(
+          reinterpret_cast<const uint8_t*>(data) + offset));
       length -= 4;
       offset += 4;
     }
 
     if (length) {
       if (chksum_buffer_offset_ != 0) return false;  // not reached
       if (length > 4) return false;  // not reached
       std::memcpy(chksum_buffer_,
@@ -99,26 +91,35 @@ class OTSStream {
     while (bytes) {
       static const uint8_t kZerob = 0;
       if (!Write(&kZerob, 1)) return false;
       bytes--;
     }
     return true;
   }
 
+  bool WriteU8(uint8_t v) {
+    return Write(&v, sizeof(v));
+  }
+
   bool WriteU16(uint16_t v) {
     v = htons(v);
     return Write(&v, sizeof(v));
   }
 
   bool WriteS16(int16_t v) {
     v = htons(v);
     return Write(&v, sizeof(v));
   }
 
+  bool WriteU24(uint32_t v) {
+    v = htonl(v);
+    return Write(reinterpret_cast<uint8_t*>(&v)+1, 3);
+  }
+
   bool WriteU32(uint32_t v) {
     v = htonl(v);
     return Write(&v, sizeof(v));
   }
 
   bool WriteS32(int32_t v) {
     v = htonl(v);
     return Write(&v, sizeof(v));
@@ -172,21 +173,18 @@ class OTSStream {
 
 // -----------------------------------------------------------------------------
 // Process a given OpenType file and write out a sanitised version
 //   output: a pointer to an object implementing the OTSStream interface. The
 //     sanitisied output will be written to this. In the even of a failure,
 //     partial output may have been written.
 //   input: the OpenType file
 //   length: the size, in bytes, of |input|
-//   preserve_otl_tables: whether to preserve OpenType Layout tables
-//                        (GDEF/GPOS/GSUB) without verification
 // -----------------------------------------------------------------------------
-bool Process(OTSStream *output, const uint8_t *input, size_t length,
-             bool preserve_otl_tables = false);
+bool Process(OTSStream *output, const uint8_t *input, size_t length);
 
 // Force to disable debug output even when the library is compiled with
 // -DOTS_DEBUG.
 void DisableDebugOutput();
 
 }  // namespace ots
 
 #endif  // OPENTYPE_SANITISER_H_
--- a/gfx/ots/include/ots-memory-stream.h
+++ b/gfx/ots/include/ots-memory-stream.h
@@ -13,34 +13,34 @@
 namespace ots {
 
 class MemoryStream : public OTSStream {
  public:
   MemoryStream(void *ptr, size_t length)
       : ptr_(ptr), length_(length), off_(0) {
   }
 
-  bool WriteRaw(const void *data, size_t length) {
+  virtual bool WriteRaw(const void *data, size_t length) {
     if ((off_ + length > length_) ||
         (length > std::numeric_limits<size_t>::max() - off_)) {
       return false;
     }
     std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
     off_ += length;
     return true;
   }
 
-  bool Seek(off_t position) {
+  virtual bool Seek(off_t position) {
     if (position < 0) return false;
     if (static_cast<size_t>(position) > length_) return false;
     off_ = position;
     return true;
   }
 
-  off_t Tell() const {
+  virtual off_t Tell() const {
     return off_;
   }
 
  private:
   void* const ptr_;
   size_t length_;
   off_t off_;
 };
--- a/gfx/ots/src/cff.cc
+++ b/gfx/ots/src/cff.cc
@@ -14,22 +14,22 @@
 // http://www.microsoft.com/opentype/otspec/cff.htm
 // http://www.microsoft.com/opentype/otspec/5176.CFF.pdf
 
 namespace {
 
 enum DICT_OPERAND_TYPE {
   DICT_OPERAND_INTEGER,
   DICT_OPERAND_REAL,
-  DICT_OPERATOR
+  DICT_OPERATOR,
 };
 
 enum DICT_DATA_TYPE {
   DICT_DATA_TOPLEVEL,
-  DICT_DATA_FDARRAY
+  DICT_DATA_FDARRAY,
 };
 
 // see Appendix. A
 const size_t kNStdString = 390;
 
 bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
   if (off_size > 4) {
     return OTS_FAILURE();
@@ -99,16 +99,18 @@ bool ParseIndex(ots::Buffer *table, ots:
       return OTS_FAILURE();
     }
 
     index->offsets.push_back(
         object_data_offset + (rel_offset - 1));  // less than length(), 1GB.
   }
 
   for (unsigned i = 1; i < index->offsets.size(); ++i) {
+    // We allow consecutive identical offsets here for zero-length strings.
+    // See http://crbug.com/69341 for more details.
     if (index->offsets[i] < index->offsets[i - 1]) {
       return OTS_FAILURE();
     }
   }
 
   index->offset_to_next = index->offsets.back();
   return true;
 }
@@ -970,17 +972,17 @@ bool ots_cff_parse(OpenTypeFile *file, c
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
 bool ots_cff_should_serialise(OpenTypeFile *file) {
-  return file->cff;
+  return file->cff != NULL;
 }
 
 bool ots_cff_serialise(OTSStream *out, OpenTypeFile *file) {
   // TODO(yusukes): would be better to transcode the data,
   //                rather than simple memcpy.
   if (!out->Write(file->cff->data, file->cff->length)) {
     return OTS_FAILURE();
   }
--- a/gfx/ots/src/cff_type2_charstring.h
+++ b/gfx/ots/src/cff_type2_charstring.h
@@ -85,16 +85,16 @@ enum Type2CharStringOperator {
   kSqrt = (12 << 8) + 26,
   kDup = (12 << 8) + 27,
   kExch = (12 << 8) + 28,
   kIndex = (12 << 8) + 29,
   kRoll = (12 << 8) + 30,
   kHFlex = (12 << 8) + 34,
   kFlex = (12 << 8) + 35,
   kHFlex1 = (12 << 8) + 36,
-  kFlex1 = (12 << 8) + 37
+  kFlex1 = (12 << 8) + 37,
   // Operators that are obsoleted or undocumented, such as 'blend', will be
   // rejected.
 };
 
 }  // namespace ots
 
 #endif  // OTS_CFF_TYPE2_CHARSTRING_H_
--- a/gfx/ots/src/cmap.cc
+++ b/gfx/ots/src/cmap.cc
@@ -27,32 +27,46 @@ struct CMAPSubtableHeader {
 struct Subtable314Range {
   uint16_t start_range;
   uint16_t end_range;
   int16_t id_delta;
   uint16_t id_range_offset;
   uint32_t id_range_offset_offset;
 };
 
-// The maximum number of groups in format 12 or 13 subtables.
+// The maximum number of groups in format 12, 13 or 14 subtables.
 // Note: 0xFFFF is the maximum number of glyphs in a single font file.
 const unsigned kMaxCMAPGroups = 0xFFFF;
 
 // Glyph array size for the Mac Roman (format 0) table.
 const size_t kFormat0ArraySize = 256;
 
 // The upper limit of the Unicode code point.
 const uint32_t kUnicodeUpperLimit = 0x10FFFF;
 
-// Parses either 3.0.4 or 3.1.4 tables.
-bool Parse3x4(ots::OpenTypeFile *file, int encoding,
+// The maximum number of UVS records (See below).
+const uint32_t kMaxCMAPSelectorRecords = 259;
+// The range of UVSes are:
+//   0x180B-0x180D (3 code points)
+//   0xFE00-0xFE0F (16 code points)
+//   0xE0100-0xE01EF (240 code points)
+const uint32_t kMongolianVSStart = 0x180B;
+const uint32_t kMongolianVSEnd = 0x180D;
+const uint32_t kVSStart = 0xFE00;
+const uint32_t kVSEnd = 0xFE0F;
+const uint32_t kIVSStart = 0xE0100;
+const uint32_t kIVSEnd = 0xE01EF;
+const uint32_t kUVSUpperLimit = 0xFFFFFF;
+
+// Parses Format 4 tables
+bool ParseFormat4(ots::OpenTypeFile *file, int platform, int encoding,
               const uint8_t *data, size_t length, uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
-  // 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
+  // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
   // whole thing and recompacting it, we validate it and include it verbatim
   // in the output.
 
   if (!file->os2) {
     return OTS_FAILURE();
   }
 
   if (!subtable.Skip(4)) {
@@ -224,22 +238,25 @@ bool Parse3x4(ots::OpenTypeFile *file, i
           return OTS_FAILURE();
         }
       }
     }
   }
 
   // We accept the table.
   // TODO(yusukes): transcode the subtable.
-  if (encoding == 0) {
+  if (platform == 3 && encoding == 0) {
     file->cmap->subtable_3_0_4_data = data;
     file->cmap->subtable_3_0_4_length = length;
-  } else if (encoding == 1) {
+  } else if (platform == 3 && encoding == 1) {
     file->cmap->subtable_3_1_4_data = data;
     file->cmap->subtable_3_1_4_length = length;
+  } else if (platform == 0 && encoding == 3) {
+    file->cmap->subtable_0_3_4_data = data;
+    file->cmap->subtable_0_3_4_length = length;
   } else {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
 bool Parse31012(ots::OpenTypeFile *file,
@@ -384,118 +401,147 @@ bool Parse31013(ots::OpenTypeFile *file,
     if (groups[i].start_range <= groups[i - 1].end_range) {
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
-// Parses 0.5.14 tables.
 bool Parse0514(ots::OpenTypeFile *file,
                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  // Unicode Variation Selector table
   ots::Buffer subtable(data, length);
 
-  // Format 14 subtables are a bit complex, so rather than rebuilding the
-  // entire thing, we validate it and then include it verbatim in the output.
+  // Format 14 tables are simple. We parse these and fully serialise them
+  // later.
 
-  const off_t offset_var_selector_records = 10;
-  const size_t size_of_var_selector_record = 11;
-  const size_t size_of_def_uvs_table = 4;
-  const size_t size_of_non_def_uvs_table = 5;
-
-  if (!subtable.Skip(6)) { // skip format and length
+  // Skip format (USHORT) and length (ULONG)
+  if (!subtable.Skip(6)) {
     return OTS_FAILURE();
   }
-  uint32_t num_var_selector_records = 0;
-  if (!subtable.ReadU32(&num_var_selector_records)) {
+
+  uint32_t num_records = 0;
+  if (!subtable.ReadU32(&num_records)) {
     return OTS_FAILURE();
   }
-  if ((length - offset_var_selector_records) / size_of_var_selector_record <
-      num_var_selector_records) {
+  if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
     return OTS_FAILURE();
   }
 
-  uint32_t prev_var_selector = 0;
-  for (uint32_t i = 0; i < num_var_selector_records; i++) {
-    uint32_t var_selector, def_uvs_offset, non_def_uvs_offset;
-    if (!subtable.ReadU24(&var_selector) ||
-        !subtable.ReadU32(&def_uvs_offset) ||
-        !subtable.ReadU32(&non_def_uvs_offset)) {
+  std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
+      = file->cmap->subtable_0_5_14;
+  records.resize(num_records);
+
+  for (unsigned i = 0; i < num_records; ++i) {
+    if (!subtable.ReadU24(&records[i].var_selector) ||
+        !subtable.ReadU32(&records[i].default_offset) ||
+        !subtable.ReadU32(&records[i].non_default_offset)) {
+      return OTS_FAILURE();
+    }
+    // Checks the value of variation selector
+    if (!((records[i].var_selector >= kMongolianVSStart &&
+           records[i].var_selector <= kMongolianVSEnd) ||
+          (records[i].var_selector >= kVSStart &&
+           records[i].var_selector <= kVSEnd) ||
+          (records[i].var_selector >= kIVSStart &&
+           records[i].var_selector <= kIVSEnd))) {
+      return OTS_FAILURE();
+    }
+    if (i > 0 &&
+        records[i-1].var_selector >= records[i].var_selector) {
       return OTS_FAILURE();
     }
-    if (var_selector <= prev_var_selector ||
-        var_selector > kUnicodeUpperLimit ||
-        def_uvs_offset > length - 4 ||
-        non_def_uvs_offset > length - 4) {
+
+    // Checks offsets
+    if (!records[i].default_offset && !records[i].non_default_offset) {
+      return OTS_FAILURE();
+    }
+    if (records[i].default_offset &&
+        records[i].default_offset >= length) {
       return OTS_FAILURE();
     }
-    prev_var_selector = var_selector;
+    if (records[i].non_default_offset &&
+        records[i].non_default_offset >= length) {
+      return OTS_FAILURE();
+    }
+  }
 
-    if (def_uvs_offset) {
-      uint32_t num_unicode_value_ranges;
-      memcpy(&num_unicode_value_ranges, data + def_uvs_offset, 4);
-      num_unicode_value_ranges = ntohl(num_unicode_value_ranges);
-      if ((length - def_uvs_offset) / size_of_def_uvs_table <
-           num_unicode_value_ranges) {
+  for (unsigned i = 0; i < num_records; ++i) {
+    // Checks default UVS table
+    if (records[i].default_offset) {
+      subtable.set_offset(records[i].default_offset);
+      uint32_t num_ranges = 0;
+      if (!subtable.ReadU32(&num_ranges)) {
+        return OTS_FAILURE();
+      }
+      if (!num_ranges || num_ranges > kMaxCMAPGroups) {
         return OTS_FAILURE();
       }
 
-      const uint8_t *tables = data + def_uvs_offset + 4;
-      uint32_t prev_end_unicode = 0;
-      for (uint32_t j = 0; j < num_unicode_value_ranges;
-           j++, tables += size_of_def_uvs_table) {
-        uint32_t start_unicode = 0, end_unicode;
-        memcpy(reinterpret_cast<uint8_t*>(&start_unicode) + 1, tables, 3);
-        start_unicode = ntohl(start_unicode);
-        end_unicode = start_unicode + *(tables + 3);
-        if ((j > 0 && start_unicode <= prev_end_unicode) ||
-            end_unicode > kUnicodeUpperLimit) {
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
+          = records[i].ranges;
+      ranges.resize(num_ranges);
+
+      for (unsigned j = 0; j < num_ranges; ++j) {
+        if (!subtable.ReadU24(&ranges[j].unicode_value) ||
+            !subtable.ReadU8(&ranges[j].additional_count)) {
           return OTS_FAILURE();
         }
-        prev_end_unicode = end_unicode;
+        const uint32_t check_value =
+            ranges[j].unicode_value + ranges[i].additional_count;
+        if (ranges[j].unicode_value == 0 ||
+            ranges[j].unicode_value > kUnicodeUpperLimit ||
+            check_value > kUVSUpperLimit ||
+            (last_unicode_value &&
+             ranges[j].unicode_value <= last_unicode_value)) {
+          return OTS_FAILURE();
+        }
+        last_unicode_value = check_value;
       }
     }
 
-    if (non_def_uvs_offset) {
-      uint32_t num_uvs_mappings;
-      memcpy(&num_uvs_mappings, data + non_def_uvs_offset, 4);
-      num_uvs_mappings = ntohl(num_uvs_mappings);
-      if ((length - non_def_uvs_offset) / size_of_non_def_uvs_table <
-          num_uvs_mappings) {
+    // Checks non default UVS table
+    if (records[i].non_default_offset) {
+      subtable.set_offset(records[i].non_default_offset);
+      uint32_t num_mappings = 0;
+      if (!subtable.ReadU32(&num_mappings)) {
+        return OTS_FAILURE();
+      }
+      if (!num_mappings || num_mappings > kMaxCMAPGroups) {
         return OTS_FAILURE();
       }
 
-      const uint8_t *tables = data + non_def_uvs_offset + 4;
-      uint32_t prev_unicode = 0;
-      for (uint32_t j = 0; j < num_uvs_mappings;
-           j++, tables += size_of_non_def_uvs_table) {
-        uint32_t unicode_value = 0;
-        memcpy(reinterpret_cast<uint8_t*>(&unicode_value) + 1, tables, 3);
-        unicode_value = ntohl(unicode_value);
-        if ((j > 0 && unicode_value <= prev_unicode) ||
-            unicode_value > kUnicodeUpperLimit) {
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
+          = records[i].mappings;
+      mappings.resize(num_mappings);
+
+      for (unsigned j = 0; j < num_mappings; ++j) {
+        if (!subtable.ReadU24(&mappings[j].unicode_value) ||
+            !subtable.ReadU16(&mappings[j].glyph_id)) {
           return OTS_FAILURE();
         }
-        uint16_t glyph;
-        memcpy(&glyph, tables + 3, 2);
-        glyph = ntohs(glyph);
-        if (glyph >= num_glyphs) {
+        if (mappings[j].glyph_id == 0 ||
+            mappings[j].unicode_value == 0 ||
+            mappings[j].unicode_value > kUnicodeUpperLimit ||
+            (last_unicode_value &&
+             mappings[j].unicode_value <= last_unicode_value)) {
           return OTS_FAILURE();
         }
-        prev_unicode = unicode_value;
+        last_unicode_value = mappings[j].unicode_value;
       }
     }
   }
 
-  // We accept the table.
-  // TODO: transcode the subtable.
-  file->cmap->subtable_0_5_14_data = data;
-  file->cmap->subtable_0_5_14_length = length;
-
+  if (subtable.offset() != length) {
+    return OTS_FAILURE();
+  }
+  file->cmap->subtable_0_5_14_length = subtable.offset();
   return true;
 }
 
 bool Parse100(ots::OpenTypeFile *file, const uint8_t *data, size_t length) {
   // Mac Roman table
   ots::Buffer subtable(data, length);
 
   if (!subtable.Skip(4)) {
@@ -680,40 +726,41 @@ bool ots_cmap_parse(OpenTypeFile *file, 
   //   3             0            4       (MS Symbol)
   //   3             1            4       (MS Unicode BMP)
   //   3             10           12      (MS Unicode UCS-4)
   //   3             10           13      (MS UCS-4 Fallback mapping)
   //
   // Note:
   //  * 0-0-4 table is (usually) written as a 3-1-4 table. If 3-1-4 table
   //    also exists, the 0-0-4 table is ignored.
-  //  * 0-3-4 table is written as a 3-1-4 table. If 3-1-4 table also exists,
-  //    the 0-3-4 table is ignored.
+  //  * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table.
+  //    Some fonts which include 0-5-14 table seems to be required 0-3-4
+  //    table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists.
   //  * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
   //    exists, the 0-3-12 table is ignored.
   //
 
   for (unsigned i = 0; i < num_tables; ++i) {
     if (subtable_headers[i].platform == 0) {
       // Unicode platform
 
       if ((subtable_headers[i].encoding == 0) &&
           (subtable_headers[i].format == 4)) {
         // parse and output the 0-0-4 table as 3-1-4 table. Sometimes the 0-0-4
         // table actually points to MS symbol data and thus should be parsed as
         // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
         // recovered in ots_cmap_serialise().
-        if (!Parse3x4(file, 1, data + subtable_headers[i].offset,
+        if (!ParseFormat4(file, 3, 1, data + subtable_headers[i].offset,
                       subtable_headers[i].length, num_glyphs)) {
           return OTS_FAILURE();
         }
       } else if ((subtable_headers[i].encoding == 3) &&
                  (subtable_headers[i].format == 4)) {
-        // parse and output the 0-3-4 table as 3-1-4 table.
-        if (!Parse3x4(file, 1, data + subtable_headers[i].offset,
+        // parse and output the 0-3-4 table as 0-3-4 table.
+        if (!ParseFormat4(file, 0, 3, data + subtable_headers[i].offset,
                       subtable_headers[i].length, num_glyphs)) {
           return OTS_FAILURE();
         }
       } else if ((subtable_headers[i].encoding == 3) &&
                  (subtable_headers[i].format == 12)) {
         // parse and output the 0-3-12 table as 3-10-12 table.
         if (!Parse31012(file, data + subtable_headers[i].offset,
                         subtable_headers[i].length, num_glyphs)) {
@@ -721,38 +768,37 @@ bool ots_cmap_parse(OpenTypeFile *file, 
         }
       } else if ((subtable_headers[i].encoding == 5) &&
                  (subtable_headers[i].format == 14)) {
         if (!Parse0514(file, data + subtable_headers[i].offset,
                        subtable_headers[i].length, num_glyphs)) {
           return OTS_FAILURE();
         }
       }
-
     } else if (subtable_headers[i].platform == 1) {
       // Mac platform
 
       if ((subtable_headers[i].encoding == 0) &&
           (subtable_headers[i].format == 0)) {
         // parse and output the 1-0-0 table.
         if (!Parse100(file, data + subtable_headers[i].offset,
                       subtable_headers[i].length)) {
           return OTS_FAILURE();
         }
       }
-
     } else if (subtable_headers[i].platform == 3) {
       // MS platform
 
       switch (subtable_headers[i].encoding) {
         case 0:
         case 1:
           if (subtable_headers[i].format == 4) {
             // parse 3-0-4 or 3-1-4 table.
-            if (!Parse3x4(file, subtable_headers[i].encoding,
+            if (!ParseFormat4(file, subtable_headers[i].platform,
+                          subtable_headers[i].encoding,
                           data + subtable_headers[i].offset,
                           subtable_headers[i].length, num_glyphs)) {
               return OTS_FAILURE();
             }
           }
           break;
         case 10:
           if (subtable_headers[i].format == 12) {
@@ -772,52 +818,113 @@ bool ots_cmap_parse(OpenTypeFile *file, 
       }
     }
   }
 
   return true;
 }
 
 bool ots_cmap_should_serialise(OpenTypeFile *file) {
-  return file->cmap;
+  return file->cmap != NULL;
 }
 
 bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
-  const bool have_0514 = file->cmap->subtable_0_5_14_data;
-  const bool have_100 = file->cmap->subtable_1_0_0.size();
-  const bool have_304 = file->cmap->subtable_3_0_4_data;
+  const bool have_034 = file->cmap->subtable_0_3_4_data != NULL;
+  const bool have_0514 = file->cmap->subtable_0_5_14.size() != 0;
+  const bool have_100 = file->cmap->subtable_1_0_0.size() != 0;
+  const bool have_304 = file->cmap->subtable_3_0_4_data != NULL;
   // MS Symbol and MS Unicode tables should not co-exist.
   // See the comment above in 0-0-4 parser.
   const bool have_314 = (!have_304) && file->cmap->subtable_3_1_4_data;
-  const bool have_31012 = file->cmap->subtable_3_10_12.size();
-  const bool have_31013 = file->cmap->subtable_3_10_13.size();
-  const unsigned num_subtables = static_cast<unsigned>(have_0514) +
+  const bool have_31012 = file->cmap->subtable_3_10_12.size() != 0;
+  const bool have_31013 = file->cmap->subtable_3_10_13.size() != 0;
+  const unsigned num_subtables = static_cast<unsigned>(have_034) +
+                                 static_cast<unsigned>(have_0514) +
                                  static_cast<unsigned>(have_100) +
                                  static_cast<unsigned>(have_304) +
                                  static_cast<unsigned>(have_314) +
                                  static_cast<unsigned>(have_31012) +
                                  static_cast<unsigned>(have_31013);
   const off_t table_start = out->Tell();
 
   // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
   // (e.g., old fonts for Mac). We don't support them.
-  if (!have_304 && !have_314) {
+  if (!have_304 && !have_314 && !have_034) {
     return OTS_FAILURE();
   }
 
   if (!out->WriteU16(0) ||
       !out->WriteU16(num_subtables)) {
     return OTS_FAILURE();
   }
 
   const off_t record_offset = out->Tell();
   if (!out->Pad(num_subtables * 8)) {
     return OTS_FAILURE();
   }
 
+  const off_t offset_034 = out->Tell();
+  if (have_034) {
+    if (!out->Write(file->cmap->subtable_0_3_4_data,
+                    file->cmap->subtable_0_3_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_0514 = out->Tell();
+  if (have_0514) {
+    const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
+        = file->cmap->subtable_0_5_14;
+    const unsigned num_records = records.size();
+    if (!out->WriteU16(14) ||
+        !out->WriteU32(file->cmap->subtable_0_5_14_length) ||
+        !out->WriteU32(num_records)) {
+      return OTS_FAILURE();
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (!out->WriteU24(records[i].var_selector) ||
+          !out->WriteU32(records[i].default_offset) ||
+          !out->WriteU32(records[i].non_default_offset)) {
+        return OTS_FAILURE();
+      }
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (records[i].default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges
+            = records[i].ranges;
+        const unsigned num_ranges = ranges.size();
+        if (!out->Seek(records[i].default_offset + offset_0514) ||
+            !out->WriteU32(num_ranges)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_ranges; ++j) {
+          if (!out->WriteU24(ranges[j].unicode_value) ||
+              !out->WriteU8(ranges[j].additional_count)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+      if (records[i].non_default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings
+            = records[i].mappings;
+        const unsigned num_mappings = mappings.size();
+        if (!out->Seek(records[i].non_default_offset + offset_0514) ||
+            !out->WriteU32(num_mappings)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_mappings; ++j) {
+          if (!out->WriteU24(mappings[j].unicode_value) ||
+              !out->WriteU16(mappings[j].glyph_id)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+    }
+  }
+
   const off_t offset_100 = out->Tell();
   if (have_100) {
     if (!out->WriteU16(0) ||  // format
         !out->WriteU16(6 + kFormat0ArraySize) ||  // length
         !out->WriteU16(0)) {  // language
       return OTS_FAILURE();
     }
     if (!out->Write(&(file->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
@@ -880,35 +987,35 @@ bool ots_cmap_serialise(OTSStream *out, 
       if (!out->WriteU32(groups[i].start_range) ||
           !out->WriteU32(groups[i].end_range) ||
           !out->WriteU32(groups[i].start_glyph_id)) {
         return OTS_FAILURE();
       }
     }
   }
 
-  const off_t offset_0514 = out->Tell();
-  if (have_0514) {
-    if (!out->Write(file->cmap->subtable_0_5_14_data,
-                    file->cmap->subtable_0_5_14_length)) {
-      return OTS_FAILURE();
-    }
-  }
-
   const off_t table_end = out->Tell();
   // We might have hanging bytes from the above's checksum which the OTSStream
   // then merges into the table of offsets.
   OTSStream::ChecksumState saved_checksum = out->SaveChecksumState();
   out->ResetChecksum();
 
   // Now seek back and write the table of offsets
   if (!out->Seek(record_offset)) {
     return OTS_FAILURE();
   }
 
+  if (have_034) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(3) ||
+        !out->WriteU32(offset_034 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
   if (have_0514) {
     if (!out->WriteU16(0) ||
         !out->WriteU16(5) ||
         !out->WriteU32(offset_0514 - table_start)) {
       return OTS_FAILURE();
     }
   }
 
--- a/gfx/ots/src/cmap.h
+++ b/gfx/ots/src/cmap.h
@@ -12,35 +12,59 @@
 namespace ots {
 
 struct OpenTypeCMAPSubtableRange {
   uint32_t start_range;
   uint32_t end_range;
   uint32_t start_glyph_id;
 };
 
+struct OpenTypeCMAPSubtableVSRange {
+  uint32_t unicode_value;
+  uint8_t additional_count;
+};
+
+struct OpenTypeCMAPSubtableVSMapping {
+  uint32_t unicode_value;
+  uint16_t glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRecord {
+  uint32_t var_selector;
+  uint32_t default_offset;
+  uint32_t non_default_offset;
+  std::vector<OpenTypeCMAPSubtableVSRange> ranges;
+  std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
+};
+
 struct OpenTypeCMAP {
   OpenTypeCMAP()
-      : subtable_3_0_4_data(NULL),
+      : subtable_0_3_4_data(NULL),
+        subtable_0_3_4_length(0),
+        subtable_0_5_14_length(0),
+        subtable_3_0_4_data(NULL),
         subtable_3_0_4_length(0),
         subtable_3_1_4_data(NULL),
-        subtable_3_1_4_length(0),
-        subtable_0_5_14_data(NULL),
-        subtable_0_5_14_length(0) {
+        subtable_3_1_4_length(0) {
   }
 
+  // Platform 0, Encoding 3, Format 4, Unicode BMP table.
+  const uint8_t *subtable_0_3_4_data;
+  size_t subtable_0_3_4_length;
+
+  // Platform 0, Encoding 5, Format 14, Unicode Variation Sequence table.
+  size_t subtable_0_5_14_length;
+  std::vector<OpenTypeCMAPSubtableVSRecord> subtable_0_5_14;
+
   // Platform 3, Encoding 0, Format 4, MS Symbol table.
   const uint8_t *subtable_3_0_4_data;
   size_t subtable_3_0_4_length;
   // Platform 3, Encoding 1, Format 4, MS Unicode BMP table.
   const uint8_t *subtable_3_1_4_data;
   size_t subtable_3_1_4_length;
-  // Platform 0, Encoding 5, Format 14, Unicode Variation Sequences table.
-  const uint8_t *subtable_0_5_14_data;
-  size_t subtable_0_5_14_length;
 
   // Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
   std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
   // Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
   std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
   // Platform 1, Encoding 0, Format 0, Mac Roman table.
   std::vector<uint8_t> subtable_1_0_0;
 };
--- a/gfx/ots/src/gasp.cc
+++ b/gfx/ots/src/gasp.cc
@@ -1,13 +1,12 @@
 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <new>
 #include "gasp.h"
 
 // gasp - Grid-fitting And Scan-conversion Procedure
 // http://www.microsoft.com/opentype/otspec/gasp.htm
 
 #define DROP_THIS_TABLE \
   do { delete file->gasp; file->gasp = 0; } while (0)
 
@@ -74,17 +73,17 @@ bool ots_gasp_parse(OpenTypeFile *file, 
 
     gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
   }
 
   return true;
 }
 
 bool ots_gasp_should_serialise(OpenTypeFile *file) {
-  return file->gasp;
+  return file->gasp != NULL;
 }
 
 bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypeGASP *gasp = file->gasp;
 
   if (!out->WriteU16(gasp->version) ||
       !out->WriteU16(gasp->gasp_ranges.size())) {
     return OTS_FAILURE();
--- a/gfx/ots/src/gdef.cc
+++ b/gfx/ots/src/gdef.cc
@@ -1,45 +1,393 @@
-// Copyright (c) 2010 Mozilla Foundation. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "gdef.h"
 
-// GDEF - Glyph Definition Table
+#include <limits>
+#include <vector>
+
+#include "gpos.h"
+#include "gsub.h"
+#include "layout.h"
+#include "maxp.h"
+
+// GDEF - The Glyph Definition Table
 // http://www.microsoft.com/typography/otspec/gdef.htm
 
+namespace {
+
+// The maximum class value in class definition tables.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+// The maximum class value in the glyph class definision table.
+const uint16_t kMaxGlyphClassDefValue = 4;
+// The maximum format number of caret value tables.
+// We don't support format 3 for now. See the comment in
+// ParseLigCaretListTable() for the reason.
+const uint16_t kMaxCaretValueFormat = 2;
+
+bool ParseGlyphClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                             size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(data, length, num_glyphs,
+                                 kMaxGlyphClassDefValue);
+}
+
+bool ParseAttachListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                          size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE();
+  }
+  const unsigned attach_points_end = static_cast<unsigned>(4) + 2*glyph_count;
+  if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < attach_points_end) {
+    return OTS_FAILURE();
+  }
+  if (glyph_count > num_glyphs) {
+    OTS_WARNING("bad glyph count: %u", glyph_count);
+    return OTS_FAILURE();
+  }
+
+  std::vector<uint16_t> attach_points;
+  attach_points.resize(glyph_count);
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    if (!subtable.ReadU16(&attach_points[i])) {
+      return OTS_FAILURE();
+    }
+    if (attach_points[i] >= length ||
+        attach_points[i] < attach_points_end) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  // Parse attach point table
+  for (unsigned i = 0; i < attach_points.size(); ++i) {
+    subtable.set_offset(attach_points[i]);
+    uint16_t point_count = 0;
+    if (!subtable.ReadU16(&point_count)) {
+      return OTS_FAILURE();
+    }
+    if (point_count == 0) {
+      return OTS_FAILURE();
+    }
+    uint16_t last_point_index = 0;
+    uint16_t point_index = 0;
+    for (unsigned j = 0; j < point_count; ++j) {
+      if (!subtable.ReadU16(&point_index)) {
+        return OTS_FAILURE();
+      }
+      // Contour point indeces are in increasing numerical order
+      if (last_point_index != 0 && last_point_index >= point_index) {
+        OTS_WARNING("bad contour indeces: %u >= %u",
+                    last_point_index, point_index);
+        return OTS_FAILURE();
+      }
+      last_point_index = point_index;
+    }
+  }
+  return true;
+}
+
+bool ParseLigCaretListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                            size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t offset_coverage = 0;
+  uint16_t lig_glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_glyph_count)) {
+    return OTS_FAILURE();
+  }
+  const unsigned lig_glyphs_end = static_cast<unsigned>(4) + 2*lig_glyph_count;
+  if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < lig_glyphs_end) {
+    return OTS_FAILURE();
+  }
+  if (lig_glyph_count > num_glyphs) {
+    OTS_WARNING("bad ligature glyph count: %u", lig_glyph_count);
+    return OTS_FAILURE();
+  }
+
+  std::vector<uint16_t> lig_glyphs;
+  lig_glyphs.resize(lig_glyph_count);
+  for (unsigned i = 0; i < lig_glyph_count; ++i) {
+    if (!subtable.ReadU16(&lig_glyphs[i])) {
+      return OTS_FAILURE();
+    }
+    if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  // Parse ligature glyph table
+  for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
+    subtable.set_offset(lig_glyphs[i]);
+    uint16_t caret_count = 0;
+    if (!subtable.ReadU16(&caret_count)) {
+      return OTS_FAILURE();
+    }
+    if (caret_count == 0) {
+      OTS_WARNING("bad caret value count: %u", caret_count);
+      return OTS_FAILURE();
+    }
+
+    std::vector<uint16_t> caret_values;
+    caret_values.resize(caret_count);
+    uint16_t last_offset_caret = 0;
+    unsigned caret_values_end = static_cast<unsigned>(2) * 2*caret_count;
+    for (unsigned j = 0; j < caret_count; ++j) {
+      if (!subtable.ReadU16(&caret_values[j])) {
+        return OTS_FAILURE();
+      }
+      if (caret_values[j] >= length || caret_values[j] < caret_values_end) {
+        return OTS_FAILURE();
+      }
+      // Caret offsets are in increasing coordinate order
+      if (last_offset_caret != 0 && last_offset_caret >= caret_values[j]) {
+        OTS_WARNING("offset isn't in increasing coordinate order: %u >= %u",
+                    last_offset_caret, caret_values[j]);
+        return OTS_FAILURE();
+      }
+      last_offset_caret = caret_values[j];
+    }
+
+    // Parse caret values table
+    for (unsigned j = 0; j < caret_count; ++j) {
+      subtable.set_offset(lig_glyphs[i] + caret_values[j]);
+      uint16_t caret_format = 0;
+      if (!subtable.ReadU16(&caret_format)) {
+        return OTS_FAILURE();
+      }
+      // TODO(bashi): We only support caret value format 1 and 2 for now
+      // because there are no fonts which contain caret value format 3
+      // as far as we investigated.
+      if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
+        OTS_WARNING("bad caret value format: %u", caret_format);
+        return OTS_FAILURE();
+      }
+      // CaretValueFormats contain a 2-byte field which could be
+      // arbitrary value.
+      if (!subtable.Skip(2)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkAttachClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                  size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(data, length, num_glyphs, kMaxClassDefValue);
+}
+
+bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t format = 0;
+  uint16_t mark_set_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&mark_set_count)) {
+    return OTS_FAILURE();
+  }
+  if (format != 1) {
+    OTS_WARNING("bad mark glyph set table format: %u", format);
+    return OTS_FAILURE();
+  }
+
+  const unsigned mark_sets_end = static_cast<unsigned>(4) + 2*mark_set_count;
+  if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < mark_set_count; ++i) {
+    uint32_t offset_coverage = 0;
+    if (!subtable.ReadU32(&offset_coverage)) {
+      return OTS_FAILURE();
+    }
+    if (offset_coverage >= length ||
+        offset_coverage < mark_sets_end) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseCoverageTable(data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  file->gdef->num_mark_glyph_sets = mark_set_count;
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE \
+  do { file->gdef->data = 0; file->gdef->length = 0; } while (0)
+
 namespace ots {
 
 bool ots_gdef_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Grab the number of glyphs in the file from the maxp table to check
+  // GlyphIDs in GDEF table.
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
   Buffer table(data, length);
 
   OpenTypeGDEF *gdef = new OpenTypeGDEF;
   file->gdef = gdef;
 
-  if (!table.Skip(length)) {
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  if (version < 0x00010000 || version == 0x00010001) {
+    OTS_WARNING("bad GDEF version");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  if (version >= 0x00010002) {
+    gdef->version_2 = true;
+  }
+
+  uint16_t offset_glyph_class_def = 0;
+  uint16_t offset_attach_list = 0;
+  uint16_t offset_lig_caret_list = 0;
+  uint16_t offset_mark_attach_class_def = 0;
+  if (!table.ReadU16(&offset_glyph_class_def) ||
+      !table.ReadU16(&offset_attach_list) ||
+      !table.ReadU16(&offset_lig_caret_list) ||
+      !table.ReadU16(&offset_mark_attach_class_def)) {
+    return OTS_FAILURE();
+  }
+  uint16_t offset_mark_glyph_sets_def = 0;
+  if (gdef->version_2) {
+    if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const unsigned gdef_header_end = static_cast<unsigned>(8) +
+      gdef->version_2 ? static_cast<unsigned>(2) : static_cast<unsigned>(0);
+  if (gdef_header_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE();
   }
 
+  // Parse subtables
+  if (offset_glyph_class_def) {
+    if (offset_glyph_class_def >= length ||
+        offset_glyph_class_def < gdef_header_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseGlyphClassDefTable(file, data + offset_glyph_class_def,
+                                 length - offset_glyph_class_def,
+                                 num_glyphs)) {
+      DROP_THIS_TABLE;
+      return true;
+    }
+    gdef->has_glyph_class_def = true;
+  }
+
+  if (offset_attach_list) {
+    if (offset_attach_list >= length ||
+        offset_attach_list < gdef_header_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseAttachListTable(file, data + offset_attach_list,
+                              length - offset_attach_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE;
+      return true;
+    }
+  }
+
+  if (offset_lig_caret_list) {
+    if (offset_lig_caret_list >= length ||
+        offset_lig_caret_list < gdef_header_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseLigCaretListTable(file, data + offset_lig_caret_list,
+                              length - offset_lig_caret_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE;
+      return true;
+    }
+  }
+
+  if (offset_mark_attach_class_def) {
+    if (offset_mark_attach_class_def >= length ||
+        offset_mark_attach_class_def < gdef_header_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseMarkAttachClassDefTable(file,
+                                      data + offset_mark_attach_class_def,
+                                      length - offset_mark_attach_class_def,
+                                      num_glyphs)) {
+      DROP_THIS_TABLE;
+      return true;
+    }
+    gdef->has_mark_attachment_class_def = true;
+  }
+
+  if (offset_mark_glyph_sets_def) {
+    if (offset_mark_glyph_sets_def >= length ||
+        offset_mark_glyph_sets_def < gdef_header_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseMarkGlyphSetsDefTable(file,
+                                    data + offset_mark_glyph_sets_def,
+                                    length - offset_mark_glyph_sets_def,
+                                    num_glyphs)) {
+      DROP_THIS_TABLE;
+      return true;
+    }
+    gdef->has_mark_glyph_sets_def = true;
+  }
   gdef->data = data;
   gdef->length = length;
   return true;
 }
 
 bool ots_gdef_should_serialise(OpenTypeFile *file) {
-  return file->preserve_otl && file->gdef;
+  const bool needed_tables_dropped =
+      (file->gsub && file->gsub->data == NULL) ||
+      (file->gpos && file->gpos->data == NULL);
+  return file->gdef != NULL && file->gdef->data != NULL &&
+      !needed_tables_dropped;
 }
 
 bool ots_gdef_serialise(OTSStream *out, OpenTypeFile *file) {
-  const OpenTypeGDEF *gdef = file->gdef;
-
-  if (!out->Write(gdef->data, gdef->length)) {
+  if (!out->Write(file->gdef->data, file->gdef->length)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
 void ots_gdef_free(OpenTypeFile *file) {
   delete file->gdef;
 }
 
 }  // namespace ots
+
--- a/gfx/ots/src/gdef.h
+++ b/gfx/ots/src/gdef.h
@@ -1,19 +1,36 @@
-// Copyright (c) 2010 Mozilla Foundation. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GDEF_H_
 #define OTS_GDEF_H_
 
 #include "ots.h"
 
 namespace ots {
 
 struct OpenTypeGDEF {
+  OpenTypeGDEF()
+      : version_2(false),
+        has_glyph_class_def(false),
+        has_mark_attachment_class_def(false),
+        has_mark_glyph_sets_def(false),
+        num_mark_glyph_sets(0),
+        data(NULL),
+        length(0) {
+  }
+
+  bool version_2;
+  bool has_glyph_class_def;
+  bool has_mark_attachment_class_def;
+  bool has_mark_glyph_sets_def;
+  uint16_t num_mark_glyph_sets;
+
   const uint8_t *data;
-  uint32_t length;
+  size_t length;
 };
 
 }  // namespace ots
 
-#endif  // OTS_GDEF_H_
+#endif
+
--- a/gfx/ots/src/glyf.cc
+++ b/gfx/ots/src/glyf.cc
@@ -1,13 +1,12 @@
 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <new>
 #include "glyf.h"
 
 #include <algorithm>
 #include <limits>
 
 #include "head.h"
 #include "loca.h"
 #include "maxp.h"
@@ -274,17 +273,17 @@ bool ots_glyf_parse(OpenTypeFile *file, 
     file->head->index_to_loc_format = 1;
   }
 
   file->loca->offsets = resulting_offsets;
   return true;
 }
 
 bool ots_glyf_should_serialise(OpenTypeFile *file) {
-  return file->glyf;
+  return file->glyf != NULL;
 }
 
 bool ots_glyf_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypeGLYF *glyf = file->glyf;
 
   for (unsigned i = 0; i < glyf->iov.size(); ++i) {
     if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
       return OTS_FAILURE();
--- a/gfx/ots/src/gpos.cc
+++ b/gfx/ots/src/gpos.cc
@@ -1,45 +1,810 @@
-// Copyright (c) 2010 Mozilla Foundation. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "gpos.h"
 
-// GPOS - Glyph Positioning Table
+#include <limits>
+#include <vector>
+
+#include "gdef.h"
+#include "gsub.h"
+#include "layout.h"
+#include "maxp.h"
+
+// GPOS - The Glyph Positioning Table
 // http://www.microsoft.com/typography/otspec/gpos.htm
 
+namespace {
+
+enum GPOS_TYPE {
+  GPOS_TYPE_SINGLE_ADJUSTMENT = 1,
+  GPOS_TYPE_PAIR_ADJUSTMENT = 2,
+  GPOS_TYPE_CURSIVE_ATTACHMENT = 3,
+  GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4,
+  GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5,
+  GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6,
+  GPOS_TYPE_CONTEXT_POSITIONING = 7,
+  GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8,
+  GPOS_TYPE_EXTENSION_POSITIONING = 9,
+  GPOS_TYPE_RESERVED = 10
+};
+
+// The size of gpos header.
+const unsigned kGposHeaderSize = 10;
+// The maximum format number for anchor tables.
+const uint16_t kMaxAnchorFormat = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+
+// Lookup type parsers.
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length);
+bool ParsePairAdjustment(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length);
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length);
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length);
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length);
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
+  {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment},
+  {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment},
+  {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment},
+  {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
+  {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
+  {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
+  {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning},
+  {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
+  {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning}
+};
+
+const ots::LookupSubtableParser kGposLookupSubtableParser = {
+  GPOS_TYPE_RESERVED, GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
+};
+
+// Shared Tables: ValueRecord, Anchor Table, and MarkArray
+
+bool ParseValueRecord(ots::Buffer* subtable, const uint8_t *data,
+                      const size_t length, const uint16_t value_format) {
+  // Check existence of adjustment fields.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> i) & 0x1) {
+      // Just read the field since these fileds could take an arbitrary values.
+      if (!subtable->Skip(2)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  // Check existence of offsets to device table.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> (i + 4)) & 0x1) {
+      uint16_t offset = 0;
+      if (!subtable->ReadU16(&offset)) {
+        return OTS_FAILURE();
+      }
+      if (offset) {
+        // TODO(bashi): Is it possible that device tables locate before
+        // this record? No fonts contain such offset AKAIF.
+        if (offset >= length) {
+          return OTS_FAILURE();
+        }
+        if (!ots::ParseDeviceTable(data + offset, length - offset)) {
+          return OTS_FAILURE();
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseAnchorTable(const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  // Read format and skip 2 2-byte fields that could be arbitrary values.
+  if (!subtable.ReadU16(&format) ||
+      !subtable.Skip(4)) {
+    return OTS_FAILURE();
+  }
+
+  if (format == 0 || format > kMaxAnchorFormat) {
+    return OTS_FAILURE();
+  }
+
+  // Format 2 and 3 has additional fields.
+  if (format == 2) {
+    // Format 2 provides an index to a glyph contour point, which will take
+    // arbitrary value.
+    uint16_t anchor_point = 0;
+    if (!subtable.ReadU16(&anchor_point)) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 3) {
+    uint16_t offset_x_device = 0;
+    uint16_t offset_y_device = 0;
+    if (!subtable.ReadU16(&offset_x_device) ||
+        !subtable.ReadU16(&offset_y_device)) {
+      return OTS_FAILURE();
+    }
+    const unsigned format_end = static_cast<unsigned>(10);
+    if (offset_x_device) {
+      if (offset_x_device < format_end || offset_x_device >= length) {
+        return OTS_FAILURE();
+      }
+      if (!ots::ParseDeviceTable(data + offset_x_device,
+                                 length - offset_x_device)) {
+        return OTS_FAILURE();
+      }
+    }
+    if (offset_y_device) {
+      if (offset_y_device < format_end || offset_y_device >= length) {
+        return OTS_FAILURE();
+      }
+      if (!ots::ParseDeviceTable(data + offset_y_device,
+                                 length - offset_y_device)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkArrayTable(const uint8_t *data, const size_t length,
+                         const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t mark_count = 0;
+  if (!subtable.ReadU16(&mark_count)) {
+    return OTS_FAILURE();
+  }
+
+  // MarkRecord consists of 4-bytes.
+  const unsigned mark_records_end = static_cast<unsigned>(2) + mark_count * 4;
+  if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < mark_count; ++i) {
+    uint16_t class_value = 0;
+    uint16_t offset_mark_anchor = 0;
+    if (!subtable.ReadU16(&class_value) ||
+        !subtable.ReadU16(&offset_mark_anchor)) {
+      return OTS_FAILURE();
+    }
+    // |class_value| may take arbitrary values including 0 here so we don't
+    // check the value.
+    if (offset_mark_anchor < mark_records_end ||
+        offset_mark_anchor >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseAnchorTable(data + offset_mark_anchor,
+                          length - offset_mark_anchor)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 1:
+// Single Adjustment Positioning Subtable
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                           const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format)) {
+    return OTS_FAILURE();
+  }
+
+  if (format == 1) {
+    // Format 1 exactly one value record.
+    if (!ParseValueRecord(&subtable, data, length, value_format)) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 2) {
+    uint16_t value_count = 0;
+    if (!subtable.ReadU16(&value_count)) {
+      return OTS_FAILURE();
+    }
+    for (unsigned i = 0; i < value_count; ++i) {
+      if (!ParseValueRecord(&subtable, data, length, value_format)) {
+        return OTS_FAILURE();
+      }
+    }
+  } else {
+    return OTS_FAILURE();
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParsePairSetTable(const uint8_t *data, const size_t length,
+                       const uint16_t value_format1,
+                       const uint16_t value_format2,
+                       const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t value_count = 0;
+  if (!subtable.ReadU16(&value_count)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < value_count; ++i) {
+    // Check pair value record.
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE();
+    }
+    if (glyph_id >= num_glyphs) {
+      return OTS_FAILURE();
+    }
+    if (!ParseValueRecord(&subtable, data, length, value_format1)) {
+      return OTS_FAILURE();
+    }
+    if (!ParseValueRecord(&subtable, data, length, value_format2)) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+bool ParsePairPosFormat1(const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t pair_set_count = 0;
+  if (!subtable.ReadU16(&pair_set_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned pair_pos_end = static_cast<unsigned>(10) +
+      pair_set_count * 2;
+  if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < pair_set_count; ++i) {
+    uint16_t pair_set_offset = 0;
+    if (!subtable.ReadU16(&pair_set_offset)) {
+      return OTS_FAILURE();
+    }
+    if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
+      return OTS_FAILURE();
+    }
+    // Check pair set tables
+    if (!ParsePairSetTable(data + pair_set_offset, length - pair_set_offset,
+                           value_format1, value_format2,
+                           num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParsePairPosFormat2(const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t offset_class_def1 = 0;
+  uint16_t offset_class_def2 = 0;
+  uint16_t class1_count = 0;
+  uint16_t class2_count = 0;
+  if (!subtable.ReadU16(&offset_class_def1) ||
+      !subtable.ReadU16(&offset_class_def2) ||
+      !subtable.ReadU16(&class1_count) ||
+      !subtable.ReadU16(&class2_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check class 1 records.
+  for (unsigned i = 0; i < class1_count; ++i) {
+    // Check class 2 records.
+    for (unsigned j = 0; j < class2_count; ++j) {
+      if (value_format1 && !ParseValueRecord(&subtable, data, length,
+                                             value_format1)) {
+        return OTS_FAILURE();
+      }
+      if (value_format2 && !ParseValueRecord(&subtable, data, length,
+                                             value_format2)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  // Check class definition tables.
+  if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
+      offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseClassDefTable(data + offset_class_def1,
+                               length - offset_class_def1,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseClassDefTable(data + offset_class_def2,
+                               length - offset_class_def2,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Pair Adjustment Positioning Subtable
+bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                         const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format1 = 0;
+  uint16_t value_format2 = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format1) ||
+      !subtable.ReadU16(&value_format2)) {
+    return OTS_FAILURE();
+  }
+
+  if (format == 1) {
+    if (!ParsePairPosFormat1(data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 2) {
+    if (!ParsePairPosFormat2(data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  } else {
+    return OTS_FAILURE();
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+// Lookup Type 3
+// Cursive Attachment Positioning Subtable
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data,
+                            const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t entry_exit_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&entry_exit_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE();
+  }
+
+  // Check entry exit records.
+  const unsigned entry_exit_records_end = static_cast<unsigned>(6) +
+      entry_exit_count * 2;
+  if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < entry_exit_count; ++i) {
+    uint16_t offset_entry_anchor = 0;
+    uint16_t offset_exit_anchor = 0;
+    if (!subtable.ReadU16(&offset_entry_anchor) ||
+        !subtable.ReadU16(&offset_exit_anchor)) {
+      return OTS_FAILURE();
+    }
+    // These offsets could be NULL.
+    if (offset_entry_anchor) {
+      if (offset_entry_anchor < entry_exit_records_end ||
+          offset_entry_anchor >= length) {
+        return OTS_FAILURE();
+      }
+      if (!ParseAnchorTable(data + offset_entry_anchor,
+                            length - offset_entry_anchor)) {
+        return OTS_FAILURE();
+      }
+    }
+    if (offset_exit_anchor) {
+      if (offset_exit_anchor < entry_exit_records_end ||
+         offset_exit_anchor >= length) {
+        return OTS_FAILURE();
+      }
+      if (!ParseAnchorTable(data + offset_exit_anchor,
+                            length - offset_exit_anchor)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseAnchorArrayTable(const uint8_t *data, const size_t length,
+                           const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t record_count = 0;
+  if (!subtable.ReadU16(&record_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned anchor_array_end = static_cast<unsigned>(2) +
+      record_count * class_count * 2;
+  if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < record_count; ++i) {
+    for (unsigned j = 0; j < class_count; ++j) {
+      uint16_t offset_record = 0;
+      if (!subtable.ReadU16(&offset_record)) {
+        return OTS_FAILURE();
+      }
+      // |offset_record| could be NULL.
+      if (offset_record) {
+        if (offset_record < anchor_array_end || offset_record >= length) {
+          return OTS_FAILURE();
+        }
+        if (!ParseAnchorTable(data + offset_record,
+                              length - offset_record)) {
+          return OTS_FAILURE();
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseLigatureArrayTable(const uint8_t *data, const size_t length,
+                             const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature_attach = 0;
+    if (!subtable.ReadU16(&offset_ligature_attach)) {
+      return OTS_FAILURE();
+    }
+    if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseAnchorArrayTable(data + offset_ligature_attach,
+                               length - offset_ligature_attach, class_count)) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+// Common parser for Lookup Type 4, 5 and 6.
+bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length,
+                                    const GPOS_TYPE type) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage1 = 0;
+  uint16_t offset_coverage2 = 0;
+  uint16_t class_count = 0;
+  uint16_t offset_mark_array = 0;
+  uint16_t offset_type_specific_array = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage1) ||
+      !subtable.ReadU16(&offset_coverage2) ||
+      !subtable.ReadU16(&class_count) ||
+      !subtable.ReadU16(&offset_mark_array) ||
+      !subtable.ReadU16(&offset_type_specific_array)) {
+    return OTS_FAILURE();
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned header_end = static_cast<unsigned>(subtable.offset());
+  if (header_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage1 < header_end || offset_coverage1 >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage1,
+                               length - offset_coverage1,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE();
+  }
+  if (offset_coverage2 < header_end || offset_coverage2 >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage2,
+                               length - offset_coverage2,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_mark_array < header_end || offset_mark_array >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ParseMarkArrayTable(data + offset_mark_array,
+                           length - offset_mark_array, class_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_type_specific_array < header_end ||
+      offset_type_specific_array >= length) {
+    return OTS_FAILURE();
+  }
+  if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT ||
+      type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) {
+    if (!ParseAnchorArrayTable(data + offset_type_specific_array,
+                               length - offset_type_specific_array,
+                               class_count)) {
+      return OTS_FAILURE();
+    }
+  } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) {
+    if (!ParseLigatureArrayTable(data + offset_type_specific_array,
+                                 length - offset_type_specific_array,
+                                 class_count)) {
+      return OTS_FAILURE();
+    }
+  } else {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// MarkToBase Attachment Positioning Subtable
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
+}
+
+// Lookup Type 5:
+// MarkToLigature Attachment Positioning Subtable
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
+}
+
+// Lookup Type 6:
+// MarkToMark Attachment Positioning Subtable
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
+}
+
+// Lookup Type 7:
+// Contextual Positioning Subtables
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs,
+                                   file->gpos->num_lookups);
+}
+
+// Lookup Type 8:
+// Chaining Contexual Positioning Subtable
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length) {
+  return ots::ParseChainingContextSubtable(data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gpos->num_lookups);
+}
+
+// Lookup Type 9:
+// Extension Positioning
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGposLookupSubtableParser);
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE \
+  do { file->gpos->data = 0; file->gpos->length = 0; } while (0)
+
 namespace ots {
 
+// As far as I checked, following fonts contain invalid GPOS table and
+// OTS will drop their GPOS table.
+//
+// # invalid delta format in device table
+// samanata.ttf
+//
+// # bad size range in device table
+// Sarai_07.ttf
+//
+// # bad offset to PairSetTable
+// chandas1-2.ttf
+//
+// # bad offset to FeatureTable
+// glrso12.ttf
+// gllr12.ttf
+// glbo12.ttf
+// glb12.ttf
+// glro12.ttf
+// glbso12.ttf
+// glrc12.ttf
+// glrsc12.ttf
+// glbs12.ttf
+// glrs12.ttf
+// glr12.ttf
+//
+// # ScriptRecords aren't sorted by tag
+// Garogier_unhinted.otf
+//
+// # bad start coverage index in CoverageFormat2
+// AndBasR.ttf
+// CharisSILB.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILR.ttf
+// DoulosSILR.ttf
+// GenBasBI.ttf
+// GenBasI.ttf
+// GenBkBasI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// Padauk-Bold.ttf
+// Padauk.ttf
+//
+// # Contour point indexes aren't sorted
+// Arial Unicode.ttf
+
 bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing GPOS table requires num_glyphs which is contained in maxp table.
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+
   Buffer table(data, length);
 
   OpenTypeGPOS *gpos = new OpenTypeGPOS;
   file->gpos = gpos;
 
-  if (!table.Skip(length)) {
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
     return OTS_FAILURE();
   }
 
+  if (version != 0x00010000) {
+    OTS_WARNING("bad GPOS version");
+    DROP_THIS_TABLE;
+    return true;
+  }
+  if ((offset_script_list < kGposHeaderSize ||
+       offset_script_list >= length) ||
+      (offset_feature_list < kGposHeaderSize ||
+       offset_feature_list >= length) ||
+      (offset_lookup_list < kGposHeaderSize ||
+       offset_lookup_list >= length)) {
+    OTS_WARNING("bad offset in GPOS header");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  if (!ParseLookupListTable(file, data + offset_lookup_list,
+                            length - offset_lookup_list,
+                            &kGposLookupSubtableParser,
+                            &gpos->num_lookups)) {
+    OTS_WARNING("faild to parse lookup list table");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  uint16_t num_features = 0;
+  if (!ParseFeatureListTable(data + offset_feature_list,
+                             length - offset_feature_list, gpos->num_lookups,
+                             &num_features)) {
+    OTS_WARNING("faild to parse feature list table");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  if (!ParseScriptListTable(data + offset_script_list,
+                            length - offset_script_list, num_features)) {
+    OTS_WARNING("faild to parse script list table");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
   gpos->data = data;
   gpos->length = length;
   return true;
 }
 
 bool ots_gpos_should_serialise(OpenTypeFile *file) {
-  return file->preserve_otl && file->gpos;
+  const bool needed_tables_dropped =
+      (file->gdef && file->gdef->data == NULL) ||
+      (file->gsub && file->gsub->data == NULL);
+  return file->gpos != NULL && file->gpos->data != NULL &&
+      !needed_tables_dropped;
 }
 
 bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) {
-  const OpenTypeGPOS *gpos = file->gpos;
-
-  if (!out->Write(gpos->data, gpos->length)) {
+  if (!out->Write(file->gpos->data, file->gpos->length)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
 void ots_gpos_free(OpenTypeFile *file) {
   delete file->gpos;
 }
 
 }  // namespace ots
+
--- a/gfx/ots/src/gpos.h
+++ b/gfx/ots/src/gpos.h
@@ -1,19 +1,29 @@
-// Copyright (c) 2010 Mozilla Foundation. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GPOS_H_
 #define OTS_GPOS_H_
 
 #include "ots.h"
 
 namespace ots {
 
 struct OpenTypeGPOS {
+  OpenTypeGPOS()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPOS table
+  uint16_t num_lookups;
+
   const uint8_t *data;
-  uint32_t length;
+  size_t length;
 };
 
 }  // namespace ots
 
-#endif  // OTS_GPOS_H_
+#endif
+
--- a/gfx/ots/src/gsub.cc
+++ b/gfx/ots/src/gsub.cc
@@ -1,45 +1,691 @@
-// Copyright (c) 2010 Mozilla Foundation. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "gsub.h"
 
-// GSUB - Glyph Substitution Table
+#include <limits>
+#include <vector>
+
+#include "gdef.h"
+#include "gpos.h"
+#include "layout.h"
+#include "maxp.h"
+
+// GSUB - The Glyph Substitution Table
 // http://www.microsoft.com/typography/otspec/gsub.htm
 
+namespace {
+
+// The GSUB header size
+const size_t kGsubHeaderSize = 8;
+
+enum GSUB_TYPE {
+  GSUB_TYPE_SINGLE = 1,
+  GSUB_TYPE_MULTIPLE = 2,
+  GSUB_TYPE_ALTERNATE = 3,
+  GSUB_TYPE_LIGATURE = 4,
+  GSUB_TYPE_CONTEXT = 5,
+  GSUB_TYPE_CHANGING_CONTEXT = 6,
+  GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
+  GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
+  GSUB_TYPE_RESERVED = 9
+};
+
+// Lookup type parsers.
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+      const uint8_t *data, const size_t length);
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length);
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
+  {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
+  {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
+  {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
+  {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
+  {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
+  {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
+  {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
+  {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
+    ParseReverseChainingContextSingleSubstitution}
+};
+
+const ots::LookupSubtableParser kGsubLookupSubtableParser = {
+  GSUB_TYPE_RESERVED, GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
+};
+
+// Lookup Type 1:
+// Single Substitution Subtable
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  if (format == 1) {
+    // Parse SingleSubstFormat1
+    int16_t delta_glyph_id = 0;
+    if (!subtable.ReadS16(&delta_glyph_id)) {
+      return OTS_FAILURE();
+    }
+    if (std::abs(delta_glyph_id) >= num_glyphs) {
+      return OTS_FAILURE();
+    }
+  } else if (format == 2) {
+    // Parse SingleSubstFormat2
+    uint16_t glyph_count = 0;
+    if (!subtable.ReadU16(&glyph_count)) {
+      return OTS_FAILURE();
+    }
+    if (glyph_count > num_glyphs) {
+      return OTS_FAILURE();
+    }
+    for (unsigned i = 0; i < glyph_count; ++i) {
+      uint16_t substitute = 0;
+      if (!subtable.ReadU16(&substitute)) {
+        return OTS_FAILURE();
+      }
+      if (substitute >= num_glyphs) {
+        OTS_WARNING("too large substitute: %u", substitute);
+        return OTS_FAILURE();
+      }
+    }
+  } else {
+    return OTS_FAILURE();
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseSequenceTable(const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE();
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE();
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Multiple Substitution Subtable
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned sequence_end = static_cast<unsigned>(6) +
+      sequence_count * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    uint16_t offset_sequence = 0;
+    if (!subtable.ReadU16(&offset_sequence)) {
+      return OTS_FAILURE();
+    }
+    if (offset_sequence < sequence_end || offset_sequence >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseSequenceTable(data + offset_sequence, length - offset_sequence,
+                            num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseAlternateSetTable(const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE();
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t alternate = 0;
+    if (!subtable.ReadU16(&alternate)) {
+      return OTS_FAILURE();
+    }
+    if (alternate >= num_glyphs) {
+      OTS_WARNING("too arge alternate: %u", alternate);
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+// Lookup Type 3:
+// Alternate Substitution Subtable
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t alternate_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&alternate_set_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned alternate_set_end = static_cast<unsigned>(6) +
+      alternate_set_count * 2;
+  if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < alternate_set_count; ++i) {
+    uint16_t offset_alternate_set = 0;
+    if (!subtable.ReadU16(&offset_alternate_set)) {
+      return OTS_FAILURE();
+    }
+    if (offset_alternate_set < alternate_set_end ||
+        offset_alternate_set >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseAlternateSetTable(data + offset_alternate_set,
+                                length - offset_alternate_set,
+                                num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (offset_coverage < alternate_set_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseLigatureTable(const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lig_glyph = 0;
+  uint16_t comp_count = 0;
+
+  if (!subtable.ReadU16(&lig_glyph) ||
+      !subtable.ReadU16(&comp_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (lig_glyph >= num_glyphs) {
+    OTS_WARNING("too large lig_glyph: %u", lig_glyph);
+    return OTS_FAILURE();
+  }
+  if (comp_count == 0 || comp_count > num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
+    uint16_t component = 0;
+    if (!subtable.ReadU16(&component)) {
+      return OTS_FAILURE();
+    }
+    if (component >= num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseLigatureSetTable(const uint8_t *data, const size_t length,
+                           const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
+  if (ligature_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature = 0;
+    if (!subtable.ReadU16(&offset_ligature)) {
+      return OTS_FAILURE();
+    }
+    if (offset_ligature < ligature_end || offset_ligature >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseLigatureTable(data + offset_ligature, length - offset_ligature,
+                            num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// Ligature Substitution Subtable
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t lig_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_set_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned ligature_set_end = static_cast<unsigned>(6) +
+      lig_set_count * 2;
+  if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < lig_set_count; ++i) {
+    uint16_t offset_ligature_set = 0;
+    if (!subtable.ReadU16(&offset_ligature_set)) {
+      return OTS_FAILURE();
+    }
+    if (offset_ligature_set < ligature_set_end ||
+        offset_ligature_set >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ParseLigatureSetTable(data + offset_ligature_set,
+                               length - offset_ligature_set, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (offset_coverage < ligature_set_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseSubstLookupRecord(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;
+}
+
+// Lookup Type 5:
+// Contextual Substitution Subtable
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs,
+                                   file->gsub->num_lookups);
+}
+
+// Lookup Type 6:
+// Chaining Contextual Substitution Subtable
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length) {
+  return ots::ParseChainingContextSubtable(data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gsub->num_lookups);
+}
+
+// Lookup Type 7:
+// Extension Substition
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGsubLookupSubtableParser);
+}
+
+// Lookup Type 8:
+// Reverse Chaining Contexual Single Substitution Subtable
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  uint16_t backtrack_glyph_count = 0;
+  if (!subtable.ReadU16(&backtrack_glyph_count)) {
+    return OTS_FAILURE();
+  }
+  if (backtrack_glyph_count > num_glyphs) {
+    return OTS_FAILURE();
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_glyph_count);
+  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE();
+    }
+    offsets_backtrack.push_back(offset);
+  }
+
+  uint16_t lookahead_glyph_count = 0;
+  if (!subtable.ReadU16(&lookahead_glyph_count)) {
+    return OTS_FAILURE();
+  }
+  if (lookahead_glyph_count > num_glyphs) {
+    return OTS_FAILURE();
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_glyph_count);
+  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE();
+    }
+    offsets_lookahead.push_back(offset);
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE();
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE();
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const unsigned substitute_end = static_cast<unsigned>(10) +
+      (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
+  if (substitute_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_coverage < substitute_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 < backtrack_glyph_count; ++i) {
+    if (offsets_backtrack[i] < substitute_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 < lookahead_glyph_count; ++i) {
+    if (offsets_lookahead[i] < substitute_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
+
+#define DROP_THIS_TABLE \
+  do { file->gsub->data = 0; file->gsub->length = 0; } while (0)
+
 namespace ots {
 
+// As far as I checked, following fonts contain invalid values in GSUB table.
+// OTS will drop their GSUB table.
+//
+// # too large substitute (value is 0xFFFF)
+// kaiu.ttf
+// mingliub2.ttf
+// mingliub1.ttf
+// mingliub0.ttf
+// GraublauWeb.otf
+// GraublauWebBold.otf
+//
+// # too large alternate (value is 0xFFFF)
+// ManchuFont.ttf
+//
+// # bad offset to lang sys table (NULL offset)
+// DejaVuMonoSansBold.ttf
+// DejaVuMonoSansBoldOblique.ttf
+// DejaVuMonoSansOblique.ttf
+// DejaVuSansMono-BoldOblique.ttf
+// DejaVuSansMono-Oblique.ttf
+// DejaVuSansMono-Bold.ttf
+//
+// # bad start coverage index
+// GenBasBI.ttf
+// GenBasI.ttf
+// AndBasR.ttf
+// GenBkBasI.ttf
+// CharisSILR.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILB.ttf
+// DoulosSILR.ttf
+// CharisSILBI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// GenBkBasBI.ttf
+// GenBasB.ttf
+// GenBasR.ttf
+//
+// # glyph range is overlapping
+// KacstTitleL.ttf
+// KacstDecorative.ttf
+// KacstTitle.ttf
+// KacstArt.ttf
+// KacstPoster.ttf
+// KacstQurn.ttf
+// KacstDigital.ttf
+// KacstBook.ttf
+// KacstFarsi.ttf
+
 bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing gsub table requires |file->maxp->num_glyphs|
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+
   Buffer table(data, length);
 
   OpenTypeGSUB *gsub = new OpenTypeGSUB;
   file->gsub = gsub;
 
-  if (!table.Skip(length)) {
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
     return OTS_FAILURE();
   }
 
+  if (version != 0x00010000) {
+    OTS_WARNING("bad GSUB version");
+    DROP_THIS_TABLE;
+    return true;
+  }
+  if ((offset_script_list < kGsubHeaderSize ||
+       offset_script_list >= length) ||
+      (offset_feature_list < kGsubHeaderSize ||
+       offset_feature_list >= length) ||
+      (offset_lookup_list < kGsubHeaderSize ||
+       offset_lookup_list >= length)) {
+    OTS_WARNING("bad offset in GSUB header");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  if (!ParseLookupListTable(file, data + offset_lookup_list,
+                            length - offset_lookup_list,
+                            &kGsubLookupSubtableParser,
+                            &gsub->num_lookups)) {
+    OTS_WARNING("faild to parse lookup list table");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  uint16_t num_features = 0;
+  if (!ParseFeatureListTable(data + offset_feature_list,
+                             length - offset_feature_list, gsub->num_lookups,
+                             &num_features)) {
+    OTS_WARNING("faild to parse feature list table");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  if (!ParseScriptListTable(data + offset_script_list,
+                            length - offset_script_list, num_features)) {
+    OTS_WARNING("faild to parse script list table");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
   gsub->data = data;
   gsub->length = length;
   return true;
 }
 
 bool ots_gsub_should_serialise(OpenTypeFile *file) {
-  return file->preserve_otl && file->gsub;
+  const bool needed_tables_dropped =
+      (file->gdef && file->gdef->data == NULL) ||
+      (file->gpos && file->gpos->data == NULL);
+  return file->gsub != NULL && file->gsub->data != NULL
+      && !needed_tables_dropped;
 }
 
 bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
-  const OpenTypeGSUB *gsub = file->gsub;
-
-  if (!out->Write(gsub->data, gsub->length)) {
+  if (!out->Write(file->gsub->data, file->gsub->length)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
 void ots_gsub_free(OpenTypeFile *file) {
   delete file->gsub;
 }
 
 }  // namespace ots
+
--- a/gfx/ots/src/gsub.h
+++ b/gfx/ots/src/gsub.h
@@ -1,19 +1,29 @@
-// Copyright (c) 2010 Mozilla Foundation. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GSUB_H_
 #define OTS_GSUB_H_
 
 #include "ots.h"
 
 namespace ots {
 
 struct OpenTypeGSUB {
+  OpenTypeGSUB()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPSUB table
+  uint16_t num_lookups;
+
   const uint8_t *data;
-  uint32_t length;
+  size_t length;
 };
 
 }  // namespace ots
 
 #endif  // OTS_GSUB_H_
+
--- a/gfx/ots/src/head.cc
+++ b/gfx/ots/src/head.cc
@@ -110,17 +110,17 @@ bool ots_head_parse(OpenTypeFile *file, 
       glyph_data_format) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
 bool ots_head_should_serialise(OpenTypeFile *file) {
-  return file->head;
+  return file->head != NULL;
 }
 
 bool ots_head_serialise(OTSStream *out, OpenTypeFile *file) {
   if (!out->WriteU32(0x00010000) ||
       !out->WriteU32(file->head->revision) ||
       !out->WriteU32(0) ||  // check sum not filled in yet
       !out->WriteU32(0x5F0F3CF5) ||
       !out->WriteU16(file->head->flags) ||
--- a/gfx/ots/src/hhea.cc
+++ b/gfx/ots/src/hhea.cc
@@ -4,147 +4,46 @@
 
 #include "hhea.h"
 
 #include "head.h"
 #include "maxp.h"
 
 // hhea - Horizontal Header
 // http://www.microsoft.com/opentype/otspec/hhea.htm
-// vhea - Vertical Header
-// http://www.microsoft.com/opentype/otspec/vhea.htm
-// This file is used for both tables because they share the same structures.
 
 namespace ots {
 
-bool ots_Xhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length,
-                    OpenTypeHHEA **out_hhea) {
+bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
   Buffer table(data, length);
   OpenTypeHHEA *hhea = new OpenTypeHHEA;
-  *out_hhea = hhea;
+  file->hhea = hhea;
 
-  if (!table.ReadU32(&hhea->version)) {
-    return OTS_FAILURE();
-  }
-  if (hhea->version >> 16 != 1) {
+  if (!table.ReadU32(&hhea->header.version)) {
     return OTS_FAILURE();
   }
-
-  if (!table.ReadS16(&hhea->ascent) ||
-      !table.ReadS16(&hhea->descent) ||
-      !table.ReadS16(&hhea->linegap) ||
-      !table.ReadU16(&hhea->adv_width_max) ||
-      !table.ReadS16(&hhea->min_lsb) ||
-      !table.ReadS16(&hhea->min_rsb) ||
-      !table.ReadS16(&hhea->x_max_extent) ||
-      !table.ReadS16(&hhea->caret_slope_rise) ||
-      !table.ReadS16(&hhea->caret_slope_run) ||
-      !table.ReadS16(&hhea->caret_offset)) {
+  if (hhea->header.version >> 16 != 1) {
     return OTS_FAILURE();
   }
 
-  if (hhea->ascent < 0) {
-    OTS_WARNING("bad ascent: %d", hhea->ascent);
-    hhea->ascent = 0;
-  }
-  if (hhea->linegap < 0) {
-    OTS_WARNING("bad linegap: %d", hhea->linegap);
-    hhea->linegap = 0;
-  }
-
-  if (!file->head) {
-    return OTS_FAILURE();
-  }
-
-  // if the font is non-slanted, caret_offset should be zero.
-  if (!(file->head->mac_style & 2) &&
-      (hhea->caret_offset != 0)) {
-    OTS_WARNING("bad caret offset: %d", hhea->caret_offset);
-    hhea->caret_offset = 0;
-  }
-
-  // skip the reserved bytes
-  if (!table.Skip(8)) {
-    return OTS_FAILURE();
-  }
-
-  int16_t data_format;
-  if (!table.ReadS16(&data_format)) {
-    return OTS_FAILURE();
-  }
-  if (data_format) {
-    return OTS_FAILURE();
-  }
-
-  if (!table.ReadU16(&hhea->num_hmetrics)) {
-    return OTS_FAILURE();
-  }
-
-  if (!file->maxp) {
-    return OTS_FAILURE();
-  }
-
-  if (hhea->num_hmetrics > file->maxp->num_glyphs) {
+  if (!ParseMetricsHeader(file, &table, &hhea->header)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
-bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
-  return ots_Xhea_parse(file, data, length, &file->hhea);
-}
-
-bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
-  return ots_Xhea_parse(file, data, length, &file->vhea);
-}
-
 bool ots_hhea_should_serialise(OpenTypeFile *file) {
-  return file->hhea;
-}
-
-bool ots_vhea_should_serialise(OpenTypeFile *file) {
-  return file->preserve_otl && file->vhea;
-}
-
-bool ots_Xhea_serialise(OTSStream *out, OpenTypeFile *file, const OpenTypeHHEA *hhea) {
-  if (!out->WriteS16(hhea->ascent) ||
-      !out->WriteS16(hhea->descent) ||
-      !out->WriteS16(hhea->linegap) ||
-      !out->WriteU16(hhea->adv_width_max) ||
-      !out->WriteS16(hhea->min_lsb) ||
-      !out->WriteS16(hhea->min_rsb) ||
-      !out->WriteS16(hhea->x_max_extent) ||
-      !out->WriteS16(hhea->caret_slope_rise) ||
-      !out->WriteS16(hhea->caret_slope_run) ||
-      !out->WriteS16(hhea->caret_offset) ||
-      !out->WriteR64(0) ||  // reserved
-      !out->WriteS16(0) ||  // metric data format
-      !out->WriteU16(hhea->num_hmetrics)) {
-    return OTS_FAILURE();
-  }
-
-  return true;
+  return file->hhea != NULL;
 }
 
 bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
-  if (!out->WriteU32(0x00010000)) {
+  if (!SerialiseMetricsHeader(out, &file->hhea->header)) {
     return OTS_FAILURE();
   }
-  return ots_Xhea_serialise(out, file, file->hhea);
-}
-
-bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) {
-  if (!out->WriteU32(file->vhea->version)) {
-    return OTS_FAILURE();
-  }
-  return ots_Xhea_serialise(out, file, file->vhea);
+  return true;
 }
 
 void ots_hhea_free(OpenTypeFile *file) {
   delete file->hhea;
 }
 
-void ots_vhea_free(OpenTypeFile *file) {
-  delete file->vhea;
-}
-
 }  // namespace ots
--- a/gfx/ots/src/hhea.h
+++ b/gfx/ots/src/hhea.h
@@ -1,29 +1,19 @@
 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_HHEA_H_
 #define OTS_HHEA_H_
 
+#include "metrics.h"
 #include "ots.h"
 
 namespace ots {
 
 struct OpenTypeHHEA {
-  uint32_t version;
-  int16_t ascent;
-  int16_t descent;
-  int16_t linegap;
-  uint16_t adv_width_max;
-  int16_t min_lsb;
-  int16_t min_rsb;
-  int16_t x_max_extent;
-  int16_t caret_slope_rise;
-  int16_t caret_slope_run;
-  int16_t caret_offset;
-  uint16_t num_hmetrics;
+  OpenTypeMetricsHeader header;
 };
 
 }  // namespace ots
 
 #endif  // OTS_HHEA_H_
--- a/gfx/ots/src/hmtx.cc
+++ b/gfx/ots/src/hmtx.cc
@@ -1,134 +1,47 @@
 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <new>
 #include "hmtx.h"
 
 #include "hhea.h"
 #include "maxp.h"
 
 // hmtx - Horizontal Metrics
 // http://www.microsoft.com/opentype/otspec/hmtx.htm
-// vmtx - Vertical Metrics
-// http://www.microsoft.com/opentype/otspec/vmtx.htm
-// This file is used for both tables because they share the same structures.
 
 namespace ots {
 
-bool ots_Xmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length,
-                    const OpenTypeHHEA *hhea, OpenTypeHMTX **out_hmtx) {
+bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
   Buffer table(data, length);
   OpenTypeHMTX *hmtx = new OpenTypeHMTX;
-  *out_hmtx = hmtx;
+  file->hmtx = hmtx;
 
-  if (!hhea || !file->maxp) {
+  if (!file->hhea || !file->maxp) {
     return OTS_FAILURE();
   }
 
-  // |num_hmetrics| is a uint16_t, so it's bounded < 65536. This limits that
-  // amount of memory that we'll allocate for this to a sane amount.
-  const unsigned num_hmetrics = hhea->num_hmetrics;
-
-  if (num_hmetrics > file->maxp->num_glyphs) {
-    return OTS_FAILURE();
-  }
-  if (!num_hmetrics) {
+  if (!ParseMetricsTable(&table, file->maxp->num_glyphs,
+                         &file->hhea->header, &hmtx->metrics)) {
     return OTS_FAILURE();
   }
-  const unsigned num_lsbs = file->maxp->num_glyphs - num_hmetrics;
-
-  hmtx->metrics.reserve(num_hmetrics);
-  for (unsigned i = 0; i < num_hmetrics; ++i) {
-    uint16_t adv = 0;
-    int16_t lsb = 0;
-    if (!table.ReadU16(&adv) || !table.ReadS16(&lsb)) {
-      return OTS_FAILURE();
-    }
-
-    // Since so many fonts don't have proper value on |adv| and |lsb|,
-    // we should not call ots_failure() here. For example, about 20% of fonts
-    // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
-    if (adv > hhea->adv_width_max) {
-      OTS_WARNING("bad adv: %u > %u", adv, hhea->adv_width_max);
-      adv = hhea->adv_width_max;
-    }
-    if (lsb < hhea->min_lsb) {
-      OTS_WARNING("bad lsb: %d < %d", lsb, hhea->min_lsb);
-      lsb = hhea->min_lsb;
-    }
-
-    hmtx->metrics.push_back(std::make_pair(adv, lsb));
-  }
-
-  hmtx->lsbs.reserve(num_lsbs);
-  for (unsigned i = 0; i < num_lsbs; ++i) {
-    int16_t lsb;
-    if (!table.ReadS16(&lsb)) {
-      // Some Japanese fonts (e.g., mona.ttf) fail this test.
-      return OTS_FAILURE();
-    }
-
-    if (lsb < hhea->min_lsb) {
-      // The same as above. Three fonts in http://www.fontsquirrel.com/fontface
-      // (e.g., Notice2Std.otf) have weird lsb values.
-      OTS_WARNING("bad lsb: %d < %d", lsb, hhea->min_lsb);
-      lsb = hhea->min_lsb;
-    }
-
-    hmtx->lsbs.push_back(lsb);
-  }
 
   return true;
 }
 
-bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
-  return ots_Xmtx_parse(file, data, length, file->hhea, &file->hmtx);
-}
-
-bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
-  return ots_Xmtx_parse(file, data, length, file->vhea, &file->vmtx);
-}
-
 bool ots_hmtx_should_serialise(OpenTypeFile *file) {
-  return file->hmtx;
-}
-
-bool ots_vmtx_should_serialise(OpenTypeFile *file) {
-  return file->preserve_otl && file->vmtx;
-}
-
-bool ots_Xmtx_serialise(OTSStream *out, OpenTypeFile *file, const OpenTypeHMTX *hmtx) {
-  for (unsigned i = 0; i < hmtx->metrics.size(); ++i) {
-    if (!out->WriteU16(hmtx->metrics[i].first) ||
-        !out->WriteS16(hmtx->metrics[i].second)) {
-      return OTS_FAILURE();
-    }
-  }
-
-  for (unsigned i = 0; i < hmtx->lsbs.size(); ++i) {
-    if (!out->WriteS16(hmtx->lsbs[i])) {
-      return OTS_FAILURE();
-    }
-  }
-
-  return true;
+  return file->hmtx != NULL;
 }
 
 bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
-  return ots_Xmtx_serialise(out, file, file->hmtx);
-}
-
-bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) {
-  return ots_Xmtx_serialise(out, file, file->vmtx);
+  if (!SerialiseMetricsTable(out, &file->hmtx->metrics)) {
+    return OTS_FAILURE();
+  }
+  return true;
 }
 
 void ots_hmtx_free(OpenTypeFile *file) {
   delete file->hmtx;
 }
 
-void ots_vmtx_free(OpenTypeFile *file) {
-  delete file->vmtx;
-}
-
 }  // namespace ots
--- a/gfx/ots/src/hmtx.h
+++ b/gfx/ots/src/hmtx.h
@@ -1,22 +1,19 @@
 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_HMTX_H_
 #define OTS_HMTX_H_
 
-#include <utility>  // std::pair
-#include <vector>
-
+#include "metrics.h"
 #include "ots.h"
 
 namespace ots {
 
 struct OpenTypeHMTX {
-  std::vector<std::pair<uint16_t, int16_t> > metrics;
-  std::vector<int16_t> lsbs;
+  OpenTypeMetricsTable metrics;
 };
 
 }  // namespace ots
 
 #endif  // OTS_HMTX_H_
--- a/gfx/ots/src/kern.cc
+++ b/gfx/ots/src/kern.cc
@@ -150,17 +150,17 @@ bool ots_kern_parse(OpenTypeFile *file, 
     return true;
   }
 
   return true;
 }
 
 bool ots_kern_should_serialise(OpenTypeFile *file) {
   if (!file->glyf) return false;  // this table is not for CFF fonts.
-  return file->kern;
+  return file->kern != NULL;
 }
 
 bool ots_kern_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypeKERN *kern = file->kern;
 
   if (!out->WriteU16(kern->version) ||
       !out->WriteU16(kern->subtables.size())) {
     return OTS_FAILURE();
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/layout.cc
@@ -0,0 +1,1477 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "layout.h"
+
+#include <limits>
+#include <vector>
+
+#include "gdef.h"
+
+// OpenType Layout Common Table Formats
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+namespace {
+
+// The 'DFLT' tag of script table.
+const uint32_t kScriptTableTagDflt = 0x44464c54;
+// The value which represents there is no required feature index.
+const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
+// The lookup flag bit which indicates existence of MarkFilteringSet.
+const uint16_t kUseMarkFilteringSetBit = 0x0010;
+// The lookup flags which require GDEF table.
+const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008;
+// The mask for MarkAttachmentType.
+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;
+  uint16_t offset;
+};
+
+struct LangSysRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+struct FeatureRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+bool ParseLangSysTable(ots::Buffer *subtable, const uint32_t tag,
+                       const uint16_t num_features) {
+  uint16_t offset_lookup_order = 0;
+  uint16_t req_feature_index = 0;
+  uint16_t feature_count = 0;
+  if (!subtable->ReadU16(&offset_lookup_order) ||
+      !subtable->ReadU16(&req_feature_index) ||
+      !subtable->ReadU16(&feature_count)) {
+    return OTS_FAILURE();
+  }
+  // |offset_lookup_order| is reserved and should be NULL.
+  if (offset_lookup_order != 0) {
+    return OTS_FAILURE();
+  }
+  if (req_feature_index != kNoRequiredFeatureIndexDefined &&
+      req_feature_index >= num_features) {
+    return OTS_FAILURE();
+  }
+  if (feature_count > num_features) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    uint16_t feature_index = 0;
+    if (!subtable->ReadU16(&feature_index)) {
+      return OTS_FAILURE();
+    }
+    if (feature_index >= num_features) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+bool ParseScriptTable(const uint8_t *data, const size_t length,
+                      const uint32_t tag, const uint16_t num_features) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_default_lang_sys = 0;
+  uint16_t lang_sys_count = 0;
+  if (!subtable.ReadU16(&offset_default_lang_sys) ||
+      !subtable.ReadU16(&lang_sys_count)) {
+    return OTS_FAILURE();
+  }
+
+  // The spec requires a script table for 'DFLT' tag must contain non-NULL
+  // |offset_default_lang_sys| and |lang_sys_count| == 0
+  if (tag == kScriptTableTagDflt &&
+      (offset_default_lang_sys == 0 || lang_sys_count != 0)) {
+    OTS_WARNING("DFLT table doesn't satisfy the spec.");
+    return OTS_FAILURE();
+  }
+
+  const unsigned lang_sys_record_end = static_cast<unsigned>(4) +
+      lang_sys_count * 6;
+  if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<LangSysRecord> lang_sys_records;
+  lang_sys_records.resize(lang_sys_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    if (!subtable.ReadU32(&lang_sys_records[i].tag) ||
+        !subtable.ReadU16(&lang_sys_records[i].offset)) {
+      return OTS_FAILURE();
+    }
+    // The record array must store the records alphabetically by tag
+    if (last_tag != 0 && last_tag > lang_sys_records[i].tag) {
+      return OTS_FAILURE();
+    }
+    if (lang_sys_records[i].offset < lang_sys_record_end ||
+        lang_sys_records[i].offset >= length) {
+      OTS_WARNING("bad offset to lang sys table: %x",
+                  lang_sys_records[i].offset);
+      return OTS_FAILURE();
+    }
+    last_tag = lang_sys_records[i].tag;
+  }
+
+  // Check lang sys tables
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    subtable.set_offset(lang_sys_records[i].offset);
+    if (!ParseLangSysTable(&subtable, lang_sys_records[i].tag, num_features)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseFeatureTable(const uint8_t *data, const size_t length,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_feature_params = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&offset_feature_params) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned feature_table_end = static_cast<unsigned>(4) +
+      num_lookups * 2;
+  if (feature_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  // |offset_feature_params| is generally set to NULL.
+  if (offset_feature_params != 0 &&
+      (offset_feature_params < feature_table_end ||
+       offset_feature_params >= length)) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    uint16_t lookup_index = 0;
+    if (!subtable.ReadU16(&lookup_index)) {
+      return OTS_FAILURE();
+    }
+    // lookup index starts with 0.
+    if (lookup_index >= num_lookups) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+bool ParseLookupTable(ots::OpenTypeFile *file, const uint8_t *data,
+                      const size_t length,
+                      const ots::LookupSubtableParser* parser) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lookup_type = 0;
+  uint16_t lookup_flag = 0;
+  uint16_t subtable_count = 0;
+  if (!subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU16(&lookup_flag) ||
+      !subtable.ReadU16(&subtable_count)) {
+    return OTS_FAILURE();
+  }
+
+  if (lookup_type == 0 || lookup_type > parser->num_types) {
+    return OTS_FAILURE();
+  }
+
+  // Check lookup flags.
+  if ((lookup_flag & kGdefRequiredFlags) &&
+      (!file->gdef || !file->gdef->has_glyph_class_def)) {
+    return OTS_FAILURE();
+  }
+  if ((lookup_flag & kMarkAttachmentTypeMask) &&
+      (!file->gdef || !file->gdef->has_mark_attachment_class_def)) {
+    return OTS_FAILURE();
+  }
+  bool use_mark_filtering_set = false;
+  if (lookup_flag & kUseMarkFilteringSetBit) {
+    if (!file->gdef || !file->gdef->has_mark_glyph_sets_def) {
+      return OTS_FAILURE();
+    }
+    use_mark_filtering_set = true;
+  }
+
+  std::vector<uint16_t> subtables;
+  subtables.reserve(subtable_count);
+  // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set,
+  // extra 2 bytes will follow after subtable offset array.
+  const unsigned lookup_table_end =
+      static_cast<unsigned>(use_mark_filtering_set ? 8 : 6) +
+      subtable_count * 2;
+  if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    uint16_t offset_subtable = 0;
+    if (!subtable.ReadU16(&offset_subtable)) {
+      return OTS_FAILURE();
+    }
+    if (offset_subtable < lookup_table_end ||
+        offset_subtable >= length) {
+      return OTS_FAILURE();
+    }
+    subtables.push_back(offset_subtable);
+  }
+  if (subtables.size() != subtable_count) {
+    return OTS_FAILURE();
+  }
+
+  if (use_mark_filtering_set) {
+    uint16_t mark_filtering_set = 0;
+    if (!subtable.ReadU16(&mark_filtering_set)) {
+      return OTS_FAILURE();
+    }
+    if (file->gdef->num_mark_glyph_sets == 0 ||
+        mark_filtering_set >= file->gdef->num_mark_glyph_sets) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Parse lookup subtables for this lookup type.
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    if (!parser->Parse(file, data + subtables[i], length - subtables[i],
+                       lookup_type)) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+bool ParseClassDefFormat1(const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t start_glyph = 0;
+  if (!subtable.ReadU16(&start_glyph)) {
+    return OTS_FAILURE();
+  }
+  if (start_glyph > num_glyphs) {
+    OTS_WARNING("bad start glyph ID: %u", start_glyph);
+    return OTS_FAILURE();
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE();
+  }
+  if (glyph_count > num_glyphs) {
+    OTS_WARNING("bad glyph count: %u", glyph_count);
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE();
+    }
+    if (class_value > num_classes) {
+      OTS_WARNING("bad class value: %u", class_value);
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefFormat2(const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE();
+  }
+  if (range_count > num_glyphs) {
+    OTS_WARNING("bad range count: %u", range_count);
+    return OTS_FAILURE();
+  }
+
+  uint16_t last_end = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE();
+    }
+    if (start > end || (last_end && start <= last_end)) {
+      OTS_WARNING("glyph range is overlapping.");
+      return OTS_FAILURE();
+    }
+    if (class_value > num_classes) {
+      OTS_WARNING("bad class value: %u", class_value);
+      return OTS_FAILURE();
+    }
+    last_end = end;
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat1(const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE();
+  }
+  if (glyph_count > num_glyphs) {
+    OTS_WARNING("bad glyph count: %u", glyph_count);
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t glyph = 0;
+    if (!subtable.ReadU16(&glyph)) {
+      return OTS_FAILURE();
+    }
+    if (glyph > num_glyphs) {
+      OTS_WARNING("bad glyph ID: %u", glyph);
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat2(const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE();
+  }
+  if (range_count > num_glyphs) {
+    OTS_WARNING("bad range count: %u", range_count);
+    return OTS_FAILURE();
+  }
+  uint16_t last_end = 0;
+  uint16_t last_start_coverage_index = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t start_coverage_index = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&start_coverage_index)) {
+      return OTS_FAILURE();
+    }
+    if (start > end || (last_end && start <= last_end)) {
+      OTS_WARNING("glyph range is overlapping.");
+      return OTS_FAILURE();
+    }
+    if (start_coverage_index != last_start_coverage_index) {
+      OTS_WARNING("bad start coverage index.");
+      return OTS_FAILURE();
+    }
+    last_end = end;
+    last_start_coverage_index += end - start + 1;
+  }
+
+  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) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE();
+    }
+    offsets_backtrack.push_back(offset);
+  }
+  if (offsets_backtrack.size() != backtrack_count) {
+    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) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE();
+    }
+    offsets_input.push_back(offset);
+  }
+  if (offsets_input.size() != input_count) {
+    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) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE();
+    }
+    offsets_lookahead.push_back(offset);
+  }
+  if (offsets_lookahead.size() != 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();
+    }
+  }
+
+  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,
+                          const uint16_t num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t script_count = 0;
+  if (!subtable.ReadU16(&script_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned script_record_end = static_cast<unsigned>(2) +
+      script_count * 6;
+  if (script_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  std::vector<ScriptRecord> script_list;
+  script_list.reserve(script_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < script_count; ++i) {
+    ScriptRecord record;
+    if (!subtable.ReadU32(&record.tag) ||
+        !subtable.ReadU16(&record.offset)) {
+      return OTS_FAILURE();
+    }
+    // Script tags should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > record.tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = record.tag;
+    if (record.offset < script_record_end || record.offset >= length) {
+      return OTS_FAILURE();
+    }
+    script_list.push_back(record);
+  }
+  if (script_list.size() != script_count) {
+    return OTS_FAILURE();
+  }
+
+  // Check script records.
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!ParseScriptTable(data + script_list[i].offset,
+                          length - script_list[i].offset,
+                          script_list[i].tag, num_features)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+// Parsing FeatureListTable requires number of lookups so we need to parse
+// LookupListTable before calling this function.
+bool ParseFeatureListTable(const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t* num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t feature_count = 0;
+  if (!subtable.ReadU16(&feature_count)) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<FeatureRecord> feature_records;
+  feature_records.resize(feature_count);
+  const unsigned feature_record_end = static_cast<unsigned>(2) +
+      feature_count * 6;
+  if (feature_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!subtable.ReadU32(&feature_records[i].tag) ||
+        !subtable.ReadU16(&feature_records[i].offset)) {
+      return OTS_FAILURE();
+    }
+    // Feature record array should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > feature_records[i].tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = feature_records[i].tag;
+    if (feature_records[i].offset < feature_record_end ||
+        feature_records[i].offset >= length) {
+      return OTS_FAILURE();
+    }
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!ParseFeatureTable(data + feature_records[i].offset,
+                           length - feature_records[i].offset, num_lookups)) {
+      return OTS_FAILURE();
+    }
+  }
+  *num_features = feature_count;
+  return true;
+}
+
+// For parsing GPOS/GSUB tables, this function should be called at first to
+// obtain the number of lookups because parsing FeatureTableList requires
+// the number.
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t *num_lookups) {
+  Buffer subtable(data, length);
+
+  if (!subtable.ReadU16(num_lookups)) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<uint16_t> lookups;
+  lookups.reserve(*num_lookups);
+  const unsigned lookup_end = static_cast<unsigned>(2) +
+      (*num_lookups) * 2;
+  if (lookup_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE();
+    }
+    if (offset < lookup_end || offset >= length) {
+      return OTS_FAILURE();
+    }
+    lookups.push_back(offset);
+  }
+  if (lookups.size() != *num_lookups) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    if (!ParseLookupTable(file, data + lookups[i], length - lookups[i],
+                          parser)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefTable(const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE();
+  }
+  if (format == 1) {
+    return ParseClassDefFormat1(data, length, num_glyphs, num_classes);
+  } else if (format == 2) {
+    return ParseClassDefFormat2(data, length, num_glyphs, num_classes);
+  }
+
+  return OTS_FAILURE();
+}
+
+bool ParseCoverageTable(const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE();
+  }
+  if (format == 1) {
+    return ParseCoverageFormat1(data, length, num_glyphs);
+  } else if (format == 2) {
+    return ParseCoverageFormat2(data, length, num_glyphs);
+  }
+
+  return OTS_FAILURE();
+}
+
+bool ParseDeviceTable(const uint8_t *data, size_t length) {
+  Buffer subtable(data, length);
+
+  uint16_t start_size = 0;
+  uint16_t end_size = 0;
+  uint16_t delta_format = 0;
+  if (!subtable.ReadU16(&start_size) ||
+      !subtable.ReadU16(&end_size) ||
+      !subtable.ReadU16(&delta_format)) {
+    return OTS_FAILURE();
+  }
+  if (start_size > end_size) {
+    OTS_WARNING("bad size range: %u > %u", start_size, end_size);
+    return OTS_FAILURE();
+  }
+  if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
+    OTS_WARNING("bad delta format: %u", delta_format);
+    return OTS_FAILURE();
+  }
+  // The number of delta values per uint16. The device table should contain
+  // at least |num_units| * 2 bytes compressed data.
+  const unsigned num_units = (end_size - start_size) /
+      (1 << (4 - delta_format)) + 1;
+  // Just skip |num_units| * 2 bytes since the compressed data could take
+  // arbitrary values.
+  if (!subtable.Skip(num_units * 2)) {
+    return OTS_FAILURE();
+  }
+  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
+
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/layout.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LAYOUT_H_
+#define OTS_LAYOUT_H_
+
+#include "ots.h"
+
+// Utility functions for OpenType layout common table formats.
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+namespace ots {
+
+
+struct LookupSubtableParser {
+  struct TypeParser {
+    uint16_t type;
+    bool (*parse)(const OpenTypeFile *file, const uint8_t *data,
+                  const size_t length);
+  };
+  size_t num_types;
+  uint16_t extension_type;
+  const TypeParser *parsers;
+
+  bool Parse(const OpenTypeFile *file, const uint8_t *data,
+             const size_t length, const uint16_t lookup_type) const;
+};
+
+bool ParseScriptListTable(const uint8_t *data, const size_t length,
+                          const uint16_t num_features);
+
+bool ParseFeatureListTable(const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t *num_features);
+
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t* num_lookups);
+
+bool ParseClassDefTable(const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes);
+
+bool ParseCoverageTable(const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs);
+
+bool ParseDeviceTable(const uint8_t *data, size_t length);
+
+// Parser for 'Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseContextSubtable(const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups);
+
+// Parser for 'Chaining Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseChainingContextSubtable(const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups);
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser);
+
+}  // namespace ots
+
+#endif  // OTS_LAYOUT_H_
+
--- a/gfx/ots/src/loca.cc
+++ b/gfx/ots/src/loca.cc
@@ -58,17 +58,17 @@ bool ots_loca_parse(OpenTypeFile *file, 
       loca->offsets[i] = offset;
     }
   }
 
   return true;
 }
 
 bool ots_loca_should_serialise(OpenTypeFile *file) {
-  return file->loca;
+  return file->loca != NULL;
 }
 
 bool ots_loca_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypeLOCA *loca = file->loca;
   const OpenTypeHEAD *head = file->head;
 
   if (!head) {
     return OTS_FAILURE();
--- a/gfx/ots/src/ltsh.cc
+++ b/gfx/ots/src/ltsh.cc
@@ -51,17 +51,17 @@ bool ots_ltsh_parse(OpenTypeFile *file, 
     ltsh->ypels.push_back(pel);
   }
 
   return true;
 }
 
 bool ots_ltsh_should_serialise(OpenTypeFile *file) {
   if (!file->glyf) return false;  // this table is not for CFF fonts.
-  return file->ltsh;
+  return file->ltsh != NULL;
 }
 
 bool ots_ltsh_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypeLTSH *ltsh = file->ltsh;
 
   if (!out->WriteU16(ltsh->version) ||
       !out->WriteU16(ltsh->ypels.size())) {
     return OTS_FAILURE();
--- a/gfx/ots/src/maxp.cc
+++ b/gfx/ots/src/maxp.cc
@@ -66,17 +66,17 @@ bool ots_maxp_parse(OpenTypeFile *file, 
   } else {
     maxp->version_1 = false;
   }
 
   return true;
 }
 
 bool ots_maxp_should_serialise(OpenTypeFile *file) {
-  return file->maxp;
+  return file->maxp != NULL;
 }
 
 bool ots_maxp_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypeMAXP *maxp = file->maxp;
 
   if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
       !out->WriteU16(maxp->num_glyphs)) {
     return OTS_FAILURE();
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/metrics.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "metrics.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// OpenType horizontal and vertical common header format
+// http://www.microsoft.com/opentype/otspec/hhea.htm
+// http://www.microsoft.com/opentype/otspec/vhea.htm
+
+namespace ots {
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header) {
+  if (!table->ReadS16(&header->ascent) ||
+      !table->ReadS16(&header->descent) ||
+      !table->ReadS16(&header->linegap) ||
+      !table->ReadU16(&header->adv_width_max) ||
+      !table->ReadS16(&header->min_sb1) ||
+      !table->ReadS16(&header->min_sb2) ||
+      !table->ReadS16(&header->max_extent) ||
+      !table->ReadS16(&header->caret_slope_rise) ||
+      !table->ReadS16(&header->caret_slope_run) ||
+      !table->ReadS16(&header->caret_offset)) {
+    return OTS_FAILURE();
+  }
+
+  if (header->ascent < 0) {
+    OTS_WARNING("bad ascent: %d", header->ascent);
+    header->ascent = 0;
+  }
+  if (header->linegap < 0) {
+    OTS_WARNING("bad linegap: %d", header->linegap);
+    header->linegap = 0;
+  }
+
+  if (!file->head) {
+    return OTS_FAILURE();
+  }
+
+  // if the font is non-slanted, caret_offset should be zero.
+  if (!(file->head->mac_style & 2) &&
+      (header->caret_offset != 0)) {
+    OTS_WARNING("bad caret offset: %d", header->caret_offset);
+    header->caret_offset = 0;
+  }
+
+  // skip the reserved bytes
+  if (!table->Skip(8)) {
+    return OTS_FAILURE();
+  }
+
+  int16_t data_format;
+  if (!table->ReadS16(&data_format)) {
+    return OTS_FAILURE();
+  }
+  if (data_format) {
+    return OTS_FAILURE();
+  }
+
+  if (!table->ReadU16(&header->num_metrics)) {
+    return OTS_FAILURE();
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+
+  if (header->num_metrics > file->maxp->num_glyphs) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsHeader(OTSStream *out,
+                            const OpenTypeMetricsHeader *header) {
+  if (!out->WriteU32(header->version) ||
+      !out->WriteS16(header->ascent) ||
+      !out->WriteS16(header->descent) ||
+      !out->WriteS16(header->linegap) ||
+      !out->WriteU16(header->adv_width_max) ||
+      !out->WriteS16(header->min_sb1) ||
+      !out->WriteS16(header->min_sb2) ||
+      !out->WriteS16(header->max_extent) ||
+      !out->WriteS16(header->caret_slope_rise) ||
+      !out->WriteS16(header->caret_slope_run) ||
+      !out->WriteS16(header->caret_offset) ||
+      !out->WriteR64(0) ||  // reserved
+      !out->WriteS16(0) ||  // metric data format
+      !out->WriteU16(header->num_metrics)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseMetricsTable(Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics) {
+  // |num_metrics| is a uint16_t, so it's bounded < 65536. This limits that
+  // amount of memory that we'll allocate for this to a sane amount.
+  const unsigned num_metrics = header->num_metrics;
+
+  if (num_metrics > num_glyphs) {
+    return OTS_FAILURE();
+  }
+  if (!num_metrics) {
+    return OTS_FAILURE();
+  }
+  const unsigned num_sbs = num_glyphs - num_metrics;
+
+  metrics->entries.reserve(num_metrics);
+  for (unsigned i = 0; i < num_metrics; ++i) {
+    uint16_t adv = 0;
+    int16_t sb = 0;
+    if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
+      return OTS_FAILURE();
+    }
+
+    // Since so many fonts don't have proper value on |adv| and |sb|,
+    // we should not call ots_failure() here. For example, about 20% of fonts
+    // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
+    if (adv > header->adv_width_max) {
+      OTS_WARNING("bad adv: %u > %u", adv, header->adv_width_max);
+      adv = header->adv_width_max;
+    }
+
+    if (sb < header->min_sb1) {
+      OTS_WARNING("bad sb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+
+    metrics->entries.push_back(std::make_pair(adv, sb));
+  }
+
+  metrics->sbs.reserve(num_sbs);
+  for (unsigned i = 0; i < num_sbs; ++i) {
+    int16_t sb;
+    if (!table->ReadS16(&sb)) {
+      // Some Japanese fonts (e.g., mona.ttf) fail this test.
+      return OTS_FAILURE();
+    }
+
+    if (sb < header->min_sb1) {
+      // The same as above. Three fonts in http://www.fontsquirrel.com/fontface
+      // (e.g., Notice2Std.otf) have weird lsb values.
+      OTS_WARNING("bad lsb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+
+    metrics->sbs.push_back(sb);
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsTable(OTSStream *out,
+                           const OpenTypeMetricsTable *metrics) {
+  for (unsigned i = 0; i < metrics->entries.size(); ++i) {
+    if (!out->WriteU16(metrics->entries[i].first) ||
+        !out->WriteS16(metrics->entries[i].second)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
+    if (!out->WriteS16(metrics->sbs[i])) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+}  // namespace ots
+
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/metrics.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_METRICS_H_
+#define OTS_METRICS_H_
+
+#include <utility>  // std::pair
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMetricsHeader {
+  uint32_t version;
+  int16_t ascent;
+  int16_t descent;
+  int16_t linegap;
+  uint16_t adv_width_max;
+  int16_t min_sb1;
+  int16_t min_sb2;
+  int16_t max_extent;
+  int16_t caret_slope_rise;
+  int16_t caret_slope_run;
+  int16_t caret_offset;
+  uint16_t num_metrics;
+};
+
+struct OpenTypeMetricsTable {
+  std::vector<std::pair<uint16_t, int16_t> > entries;
+  std::vector<int16_t> sbs;
+};
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header);
+bool SerialiseMetricsHeader(OTSStream *out,
+                            const OpenTypeMetricsHeader *header);
+
+bool ParseMetricsTable(Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics);
+bool SerialiseMetricsTable(OTSStream *out,
+                           const OpenTypeMetricsTable *metrics);
+
+}  // namespace ots
+
+#endif  // OTS_METRICS_H_
+
--- a/gfx/ots/src/os2.cc
+++ b/gfx/ots/src/os2.cc
@@ -1,14 +1,12 @@
 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstddef>
-
 #include "os2.h"
 
 #include "head.h"
 
 // OS/2 - OS/2 and Windows Metrics
 // http://www.microsoft.com/opentype/otspec/os2.htm
 
 namespace ots {
@@ -209,17 +207,17 @@ bool ots_os2_parse(OpenTypeFile *file, c
     OTS_WARNING("bad cap_height: %d", os2->cap_height);
     os2->cap_height = 0;
   }
 
   return true;
 }
 
 bool ots_os2_should_serialise(OpenTypeFile *file) {
-  return file->os2;
+  return file->os2 != NULL;
 }
 
 bool ots_os2_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypeOS2 *os2 = file->os2;
 
   if (!out->WriteU16(os2->version) ||
       !out->WriteS16(os2->avg_char_width) ||
       !out->WriteU16(os2->weight_class) ||
--- a/gfx/ots/src/ots.cc
+++ b/gfx/ots/src/ots.cc
@@ -9,28 +9,17 @@
 
 #include <algorithm>
 #include <cstdlib>
 #include <cstring>
 #include <map>
 #include <vector>
 
 // The OpenType Font File
-// http://www.microsoft.com/opentype/otspec/otff.htm
-
-#define F(name, capname) \
-  namespace ots { \
-  bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
-  bool ots_##name##_should_serialise(OpenTypeFile *f); \
-  bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
-  void ots_##name##_free(OpenTypeFile *f); \
-  }
-  // TODO(yusukes): change these function names to follow Chromium coding rule.
-FOR_EACH_TABLE_TYPE
-#undef F
+// http://www.microsoft.com/typography/otspec/cmap.htm
 
 namespace {
 
 bool g_debug_output = true;
 
 struct OpenTypeTable {
   uint32_t tag;
   uint32_t chksum;
@@ -137,28 +126,30 @@ const struct {
   { Tag("prep"), ots::ots_prep_parse, ots::ots_prep_serialise,
     ots::ots_prep_should_serialise, ots::ots_prep_free, false },
   { Tag("LTSH"), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
     ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
   { Tag("VORG"), ots::ots_vorg_parse, ots::ots_vorg_serialise,
     ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
   { Tag("kern"), ots::ots_kern_parse, ots::ots_kern_serialise,
     ots::ots_kern_should_serialise, ots::ots_kern_free, false },
-  { Tag("vhea"), ots::ots_vhea_parse, ots::ots_vhea_serialise,
-    ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
-  { Tag("vmtx"), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
-    ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
+  // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
+  // because they could refer GDEF table.
   { Tag("GDEF"), ots::ots_gdef_parse, ots::ots_gdef_serialise,
     ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
   { Tag("GPOS"), ots::ots_gpos_parse, ots::ots_gpos_serialise,
     ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
   { Tag("GSUB"), ots::ots_gsub_parse, ots::ots_gsub_serialise,
     ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
-  // TODO(yusukes): Support GDEF, GPOS, GSUB, mort, base, and jstf tables.
-  { 0, NULL, NULL, NULL, NULL, false }
+  { Tag("vhea"), ots::ots_vhea_parse, ots::ots_vhea_serialise,
+    ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
+  { Tag("vmtx"), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+    ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
+  // TODO(bashi): Support mort, base, and jstf tables.
+  { 0, NULL, NULL, NULL, NULL, false },
 };
 
 bool IsValidVersionTag(uint32_t tag) {
   return tag == Tag("\x00\x01\x00\x00") ||
          // OpenType fonts with CFF data have 'OTTO' tag.
          tag == Tag("OTTO") ||
          // Older Mac fonts might have 'true' or 'typ1' tag.
          tag == Tag("true") ||
@@ -374,17 +365,20 @@ bool ProcessGeneric(ots::OpenTypeFile *h
         return OTS_FAILURE();
       }
 
       uncompressed_sum += tables[i].uncompressed_length;
     }
     // since we required that the file be < 1GB in length, and that the table
     // length is < 1GB, the following addtion doesn't overflow
     const uint32_t end_byte = Round4(tables[i].offset + tables[i].length);
-    if (!end_byte || end_byte > length) {
+    // Some fonts which are automatically generated by a font generator
+    // called TTX seems not to add 0-padding to the final table. It might be
+    // ok to accept these fonts so we round up the length of the font file.
+    if (!end_byte || end_byte > Round4(length)) {
       return OTS_FAILURE();
     }
   }
 
   // All decompressed tables uncompressed must be <= 30MB.
   if (uncompressed_sum > 30 * 1024 * 1024) {
     return OTS_FAILURE();
   }
@@ -586,24 +580,22 @@ bool ProcessGeneric(ots::OpenTypeFile *h
 }  // namespace
 
 namespace ots {
 
 void DisableDebugOutput() {
   g_debug_output = false;
 }
 
-bool Process(OTSStream *output, const uint8_t *data, size_t length, bool preserveOTL) {
+bool Process(OTSStream *output, const uint8_t *data, size_t length) {
   OpenTypeFile header;
   if (length < 4) {
     return OTS_FAILURE();
   }
 
-  header.preserve_otl = preserveOTL;
-
   bool result;
   if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
     result = ProcessWOFF(&header, output, data, length);
   } else {
     result = ProcessTTF(&header, output, data, length);
   }
 
   for (unsigned i = 0; ; ++i) {
--- a/gfx/ots/src/ots.h
+++ b/gfx/ots/src/ots.h
@@ -97,23 +97,22 @@ class Buffer {
     return true;
   }
 
   bool ReadS16(int16_t *value) {
     return ReadU16(reinterpret_cast<uint16_t*>(value));
   }
 
   bool ReadU24(uint32_t *value) {
-    if (offset_ + 4 > length_) {
+    if (offset_ + 3 > length_) {
       return OTS_FAILURE();
     }
-    *value = 0;
-    std::memcpy(reinterpret_cast<uint8_t*>(value) + 1,
-                buffer_ + offset_, 3);
-    *value = ntohl(*value);
+    *value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
+        static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
+        static_cast<uint32_t>(buffer_[offset_ + 2]);
     offset_ += 3;
     return true;
   }
 
   bool ReadU32(uint32_t *value) {
     if (offset_ + 4 > length_) {
       return OTS_FAILURE();
     }
@@ -158,61 +157,63 @@ class Buffer {
 };
 
 #define FOR_EACH_TABLE_TYPE \
   F(cff, CFF) \
   F(cmap, CMAP) \
   F(cvt, CVT) \
   F(fpgm, FPGM) \
   F(gasp, GASP) \
+  F(gdef, GDEF) \
   F(glyf, GLYF) \
+  F(gpos, GPOS) \
+  F(gsub, GSUB) \
   F(hdmx, HDMX) \
   F(head, HEAD) \
   F(hhea, HHEA) \
   F(hmtx, HMTX) \
   F(kern, KERN) \
   F(loca, LOCA) \
   F(ltsh, LTSH) \
   F(maxp, MAXP) \
   F(name, NAME) \
   F(os2, OS2) \
   F(post, POST) \
   F(prep, PREP) \
   F(vdmx, VDMX) \
+  F(vorg, VORG) \
   F(vhea, VHEA) \
-  F(vmtx, VMTX) \
-  F(vorg, VORG) \
-  F(gdef, GDEF) \
-  F(gpos, GPOS) \
-  F(gsub, GSUB)
+  F(vmtx, VMTX)
 
 #define F(name, capname) struct OpenType##capname;
 FOR_EACH_TABLE_TYPE
 #undef F
 
-#define OpenTypeVHEA OpenTypeHHEA
-#define OpenTypeVMTX OpenTypeHMTX
-
 struct OpenTypeFile {
   OpenTypeFile() {
 #define F(name, capname) name = NULL;
     FOR_EACH_TABLE_TYPE
 #undef F
   }
 
   uint32_t version;
   uint16_t num_tables;
   uint16_t search_range;
   uint16_t entry_selector;
   uint16_t range_shift;
 
-  // This is used to tell the GDEF/GPOS/GSUB parsers whether to preserve the
-  // OpenType Layout tables (**without** any checking).
-  bool preserve_otl;
-
 #define F(name, capname) OpenType##capname *name;
 FOR_EACH_TABLE_TYPE
 #undef F
 };
 
+#define F(name, capname) \
+bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
+bool ots_##name##_should_serialise(OpenTypeFile *f); \
+bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
+void ots_##name##_free(OpenTypeFile *f);
+// TODO(yusukes): change these function names to follow Chromium coding rule.
+FOR_EACH_TABLE_TYPE
+#undef F
+
 }  // namespace ots
 
 #endif  // OTS_H_
--- a/gfx/ots/src/post.cc
+++ b/gfx/ots/src/post.cc
@@ -115,17 +115,17 @@ bool ots_post_parse(OpenTypeFile *file, 
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
 bool ots_post_should_serialise(OpenTypeFile *file) {
-  return file->post;
+  return file->post != NULL;
 }
 
 bool ots_post_serialise(OTSStream *out, OpenTypeFile *file) {
   const OpenTypePOST *post = file->post;
 
   // OpenType with CFF glyphs must have v3 post table.
   if (file->post && file->cff && file->post->version != 0x00030000) {
     return OTS_FAILURE();
--- a/gfx/ots/src/vdmx.cc
+++ b/gfx/ots/src/vdmx.cc
@@ -117,17 +117,17 @@ bool ots_vdmx_parse(OpenTypeFile *file, 
     vdmx->groups.push_back(group);
   }
 
   return true;
 }
 
 bool ots_vdmx_should_serialise(OpenTypeFile *file) {
   if (!file->glyf) return false;  // this table is not for CFF fonts.
-  return file->vdmx;
+  return file->vdmx != NULL;
 }
 
 bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) {
   OpenTypeVDMX * const vdmx = file->vdmx;
 
   if (!out->WriteU16(vdmx->version) ||
       !out->WriteU16(vdmx->num_recs) ||
       !out->WriteU16(vdmx->num_ratios)) {
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/vhea.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vhea.h"
+
+#include "gsub.h"
+#include "head.h"
+#include "maxp.h"
+
+// vhea - Vertical Header Table
+// http://www.microsoft.com/opentype/otspec/vhea.htm
+
+namespace ots {
+
+bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVHEA *vhea = new OpenTypeVHEA;
+  file->vhea = vhea;
+
+  if (!table.ReadU32(&vhea->header.version)) {
+    return OTS_FAILURE();
+  }
+  if (vhea->header.version != 0x00010000 &&
+      vhea->header.version != 0x00011000) {
+    return OTS_FAILURE();
+  }
+
+  if (!ParseMetricsHeader(file, &table, &vhea->header)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ots_vhea_should_serialise(OpenTypeFile *file) {
+  // vhea should'nt serialise when vmtx doesn't exist.
+  // Firefox developer pointed out that vhea/vmtx should serialise iff GSUB is
+  // preserved. See http://crbug.com/77386
+  return file->vhea != NULL && file->vmtx != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsHeader(out, &file->vhea->header)) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+void ots_vhea_free(OpenTypeFile *file) {
+  delete file->vhea;
+}
+
+}  // namespace ots
+
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/vhea.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VHEA_H_
+#define OTS_VHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVHEA {
+  OpenTypeMetricsHeader header;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VHEA_H_
+
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/vmtx.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vmtx.h"
+
+#include "gsub.h"
+#include "maxp.h"
+#include "vhea.h"
+
+// vmtx - Vertical Metrics Table
+// http://www.microsoft.com/opentype/otspec/vmtx.htm
+
+namespace ots {
+
+bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVMTX *vmtx = new OpenTypeVMTX;
+  file->vmtx = vmtx;
+
+  if (!file->vhea || !file->maxp) {
+    return OTS_FAILURE();
+  }
+
+  if (!ParseMetricsTable(&table, file->maxp->num_glyphs,
+                         &file->vhea->header, &vmtx->metrics)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ots_vmtx_should_serialise(OpenTypeFile *file) {
+  // vmtx should serialise when vhea and GSUB are preserved.
+  // See the comment in ots_vhea_should_serialise().
+  return file->vmtx != NULL && file->vhea != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsTable(out, &file->vmtx->metrics)) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+void ots_vmtx_free(OpenTypeFile *file) {
+  delete file->vmtx;
+}
+
+}  // namespace ots
+
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/vmtx.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VMTX_H_
+#define OTS_VMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVMTX {
+  OpenTypeMetricsTable metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VMTX_H_
+
--- a/gfx/ots/src/vorg.cc
+++ b/gfx/ots/src/vorg.cc
@@ -61,17 +61,17 @@ bool ots_vorg_parse(OpenTypeFile *file, 
     vorg->metrics.push_back(rec);
   }
 
   return true;
 }
 
 bool ots_vorg_should_serialise(OpenTypeFile *file) {
   if (!file->cff) return false;  // this table is not for fonts with TT glyphs.
-  return file->vorg;
+  return file->vorg != NULL;
 }
 
 bool ots_vorg_serialise(OTSStream *out, OpenTypeFile *file) {
   OpenTypeVORG * const vorg = file->vorg;
 
   if (!out->WriteU16(vorg->major_version) ||
       !out->WriteU16(vorg->minor_version) ||
       !out->WriteS16(vorg->default_vert_origin_y) ||