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 68523 31b6ab15760edada63a494ea72269c46ce55e166
parent 68522 2d5bdea10000bd82e9b2ea014dab54593cf1da72
child 68524 32f760d04b38a52dfae7c6a9bc5d22430baf4d79
push id19679
push userjkew@mozilla.com
push dateTue, 26 Apr 2011 15:31:05 +0000
treeherdermozilla-central@3007eb62abc4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs643460
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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) ||