Bug 1577799 - Update OTS to 8.0.0. r=jfkthame
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 13 Sep 2019 09:47:50 +0000
changeset 494965 81c77136eb578e19f946df1660e2ab2d3010df02
parent 494964 d3fedf958d87aed33fb811571db9b25853181659
child 494966 a7180808eaaeb815467d7794477f69a71b62a93b
push id114131
push userdluca@mozilla.com
push dateThu, 26 Sep 2019 09:47:34 +0000
treeherdermozilla-inbound@1dc1a755079a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1577799
milestone71.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 1577799 - Update OTS to 8.0.0. r=jfkthame Differential Revision: https://phabricator.services.mozilla.com/D44703
gfx/ots/README.mozilla
gfx/ots/ots-lz4.patch
gfx/ots/src/cff.cc
gfx/ots/src/cff.h
gfx/ots/src/cff_charstring.cc
gfx/ots/src/cff_charstring.h
gfx/ots/src/cff_type2_charstring.cc
gfx/ots/src/cff_type2_charstring.h
gfx/ots/src/gdef.cc
gfx/ots/src/glat.cc
gfx/ots/src/hvar.cc
gfx/ots/src/layout.cc
gfx/ots/src/moz.build
gfx/ots/src/mvar.cc
gfx/ots/src/name.cc
gfx/ots/src/ots.cc
gfx/ots/src/ots.h
gfx/ots/src/silf.cc
gfx/ots/src/stat.cc
gfx/ots/src/variations.cc
gfx/ots/src/variations.h
gfx/ots/src/vvar.cc
gfx/ots/tests/cff_charstring_test.cc
gfx/ots/tests/cff_type2_charstring_test.cc
gfx/tests/gtest/moz.build
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,12 +1,12 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
 Our reference repository is https://github.com/khaledhosny/ots/.
 
-Current revision: 3f26ef1579e2f5bc9627bd852952f42986ccf1af (7.1.9)
+Current revision: 8bba749d9d5401726a7d7609ab914fdb5e92bfbe (8.0.0)
 
 Upstream files included: LICENSE, src/, include/, tests/*.cc
 
 Additional files: README.mozilla, src/moz.build
 
 Additional patch: ots-visibility.patch (bug 711079).
 Additional patch: ots-lz4.patch
--- a/gfx/ots/ots-lz4.patch
+++ b/gfx/ots/ots-lz4.patch
@@ -7,21 +7,21 @@ diff --git a/gfx/ots/src/glat.cc b/gfx/o
  
  #include "gloc.h"
 -#include "lz4.h"
 +#include "mozilla/Compression.h"
  #include <list>
  
  namespace ots {
  
-@@ -200,16 +200,17 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
-       if (prevent_decompression) {
-         return DropGraphite("Illegal nested compression");
+@@ -212,16 +212,17 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
+         return DropGraphite("Decompressed size exceeds 30MB: %gMB",
+                             decompressed_size / (1024.0 * 1024.0));
        }
-       std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
+       std::vector<uint8_t> decompressed(decompressed_size);
 -      int ret = LZ4_decompress_safe_partial(
 +      size_t outputSize = 0;
 +      bool ret = mozilla::Compression::LZ4::decompressPartial(
            reinterpret_cast<const char*>(data + table.offset()),
 -          reinterpret_cast<char*>(decompressed.data()),
            table.remaining(),  // input buffer size (input size + padding)
 +          reinterpret_cast<char*>(decompressed.data()),
            decompressed.size(),  // target output size
@@ -44,21 +44,21 @@ diff --git a/gfx/ots/src/silf.cc b/gfx/o
  
  #include "name.h"
 -#include "lz4.h"
 +#include "mozilla/Compression.h"
  #include <cmath>
  
  namespace ots {
  
-@@ -38,16 +38,17 @@ bool OpenTypeSILF::Parse(const uint8_t* 
-         if (prevent_decompression) {
-           return DropGraphite("Illegal nested compression");
+@@ -50,16 +50,17 @@ bool OpenTypeSILF::Parse(const uint8_t* 
+           return DropGraphite("Decompressed size exceeds 30MB: %gMB",
+                               decompressed_size / (1024.0 * 1024.0));
          }
-         std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
+         std::vector<uint8_t> decompressed(decompressed_size);
 -        int ret = LZ4_decompress_safe_partial(
 +        size_t outputSize = 0;
 +        bool ret = mozilla::Compression::LZ4::decompressPartial(
              reinterpret_cast<const char*>(data + table.offset()),
 -            reinterpret_cast<char*>(decompressed.data()),
              table.remaining(),  // input buffer size (input size + padding)
 +            reinterpret_cast<char*>(decompressed.data()),
              decompressed.size(),  // target output size
--- a/gfx/ots/src/cff.cc
+++ b/gfx/ots/src/cff.cc
@@ -4,17 +4,18 @@
 
 #include "cff.h"
 
 #include <cstring>
 #include <utility>
 #include <vector>
 
 #include "maxp.h"
-#include "cff_type2_charstring.h"
+#include "cff_charstring.h"
+#include "variations.h"
 
 // CFF - PostScript font program (Compact Font Format) table
 // http://www.microsoft.com/typography/otspec/cff.htm
 // http://www.microsoft.com/typography/otspec/cffspec.htm
 
 #define TABLE_NAME "CFF"
 
 namespace {
@@ -23,194 +24,197 @@ enum DICT_OPERAND_TYPE {
   DICT_OPERAND_INTEGER,
   DICT_OPERAND_REAL,
   DICT_OPERATOR,
 };
 
 enum DICT_DATA_TYPE {
   DICT_DATA_TOPLEVEL,
   DICT_DATA_FDARRAY,
+  DICT_DATA_PRIVATE,
 };
 
 enum FONT_FORMAT {
   FORMAT_UNKNOWN,
   FORMAT_CID_KEYED,
   FORMAT_OTHER,  // Including synthetic fonts
 };
 
 // see Appendix. A
 const size_t kNStdString = 390;
 
-bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
+typedef std::pair<uint32_t, DICT_OPERAND_TYPE> Operand;
+
+bool ReadOffset(ots::Buffer &table, uint8_t off_size, uint32_t *offset) {
   if (off_size > 4) {
     return OTS_FAILURE();
   }
 
   uint32_t tmp32 = 0;
   for (unsigned i = 0; i < off_size; ++i) {
     uint8_t tmp8 = 0;
-    if (!table->ReadU8(&tmp8)) {
+    if (!table.ReadU8(&tmp8)) {
       return OTS_FAILURE();
     }
     tmp32 <<= 8;
     tmp32 += tmp8;
   }
   *offset = tmp32;
   return true;
 }
 
-bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) {
-  index->off_size = 0;
-  index->offsets.clear();
+bool ParseIndex(ots::Buffer &table, ots::CFFIndex &index, bool cff2 = false) {
+  index.off_size = 0;
+  index.offsets.clear();
 
-  if (!table->ReadU16(&(index->count))) {
-    return OTS_FAILURE();
+  if (cff2) {
+    if (!table.ReadU32(&(index.count))) {
+      return OTS_FAILURE();
+    }
+  } else {
+    uint16_t count;
+    if (!table.ReadU16(&count)) {
+      return OTS_FAILURE();
+    }
+    index.count = count;
   }
-  if (index->count == 0) {
+
+  if (index.count == 0) {
     // An empty INDEX.
-    index->offset_to_next = table->offset();
+    index.offset_to_next = table.offset();
     return true;
   }
 
-  if (!table->ReadU8(&(index->off_size))) {
+  if (!table.ReadU8(&(index.off_size))) {
     return OTS_FAILURE();
   }
-  if ((index->off_size == 0) ||
-      (index->off_size > 4)) {
+  if (index.off_size < 1 || index.off_size > 4) {
     return OTS_FAILURE();
   }
 
-  const size_t array_size = (index->count + 1) * index->off_size;
+  const size_t array_size = (index.count + 1) * index.off_size;
   // less than ((64k + 1) * 4), thus does not overflow.
-  const size_t object_data_offset = table->offset() + array_size;
+  const size_t object_data_offset = table.offset() + array_size;
   // does not overflow too, since offset() <= 1GB.
 
-  if (object_data_offset >= table->length()) {
+  if (object_data_offset >= table.length()) {
     return OTS_FAILURE();
   }
 
-  for (unsigned i = 0; i <= index->count; ++i) {  // '<=' is not a typo.
+  for (unsigned i = 0; i <= index.count; ++i) {  // '<=' is not a typo.
     uint32_t rel_offset = 0;
-    if (!ReadOffset(table, index->off_size, &rel_offset)) {
+    if (!ReadOffset(table, index.off_size, &rel_offset)) {
       return OTS_FAILURE();
     }
     if (rel_offset < 1) {
       return OTS_FAILURE();
     }
     if (i == 0 && rel_offset != 1) {
       return OTS_FAILURE();
     }
 
-    if (rel_offset > table->length()) {
+    if (rel_offset > table.length()) {
       return OTS_FAILURE();
     }
 
     // does not underflow.
-    if (object_data_offset > table->length() - (rel_offset - 1)) {
+    if (object_data_offset > table.length() - (rel_offset - 1)) {
       return OTS_FAILURE();
     }
 
-    index->offsets.push_back(
+    index.offsets.push_back(
         object_data_offset + (rel_offset - 1));  // less than length(), 1GB.
   }
 
-  for (unsigned i = 1; i < index->offsets.size(); ++i) {
+  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]) {
+    if (index.offsets[i] < index.offsets[i - 1]) {
       return OTS_FAILURE();
     }
   }
 
-  index->offset_to_next = index->offsets.back();
+  index.offset_to_next = index.offsets.back();
   return true;
 }
 
 bool ParseNameData(
     ots::Buffer *table, const ots::CFFIndex &index, std::string* out_name) {
   uint8_t name[256] = {0};
-  if (index.offsets.size() == 0) {  // just in case.
+
+  const size_t length = index.offsets[1] - index.offsets[0];
+  // font names should be no longer than 127 characters.
+  if (length > 127) {
     return OTS_FAILURE();
   }
-  for (unsigned i = 1; i < index.offsets.size(); ++i) {
-    const size_t length = index.offsets[i] - index.offsets[i - 1];
-    // font names should be no longer than 127 characters.
-    if (length > 127) {
+
+  table->set_offset(index.offsets[0]);
+  if (!table->Read(name, length)) {
+    return OTS_FAILURE();
+  }
+
+  for (size_t i = 0; i < length; ++i) {
+    // setting the first byte to NUL is allowed.
+    if (i == 0 && name[i] == 0) continue;
+    // non-ASCII characters are not recommended (except the first character).
+    if (name[i] < 33 || name[i] > 126) {
       return OTS_FAILURE();
     }
-
-    table->set_offset(index.offsets[i - 1]);
-    if (!table->Read(name, length)) {
+    // [, ], ... are not allowed.
+    if (std::strchr("[](){}<>/% ", name[i])) {
       return OTS_FAILURE();
     }
-
-    for (size_t j = 0; j < length; ++j) {
-      // setting the first byte to NUL is allowed.
-      if (j == 0 && name[j] == 0) continue;
-      // non-ASCII characters are not recommended (except the first character).
-      if (name[j] < 33 || name[j] > 126) {
-        return OTS_FAILURE();
-      }
-      // [, ], ... are not allowed.
-      if (std::strchr("[](){}<>/% ", name[j])) {
-        return OTS_FAILURE();
-      }
-    }
   }
 
   *out_name = reinterpret_cast<char *>(name);
   return true;
 }
 
-bool CheckOffset(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
-                 size_t table_length) {
+bool CheckOffset(const Operand& operand, size_t table_length) {
   if (operand.second != DICT_OPERAND_INTEGER) {
     return OTS_FAILURE();
   }
   if (operand.first >= table_length) {
     return OTS_FAILURE();
   }
   return true;
 }
 
-bool CheckSid(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
-              size_t sid_max) {
+bool CheckSid(const Operand& operand, size_t sid_max) {
   if (operand.second != DICT_OPERAND_INTEGER) {
     return OTS_FAILURE();
   }
   if (operand.first > sid_max) {
     return OTS_FAILURE();
   }
   return true;
 }
 
-bool ParseDictDataBcd(
-    ots::Buffer *table,
-    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+bool ParseDictDataBcd(ots::Buffer &table, std::vector<Operand> &operands) {
   bool read_decimal_point = false;
   bool read_e = false;
 
   uint8_t nibble = 0;
   size_t count = 0;
   while (true) {
-    if (!table->ReadU8(&nibble)) {
+    if (!table.ReadU8(&nibble)) {
       return OTS_FAILURE();
     }
     if ((nibble & 0xf0) == 0xf0) {
       if ((nibble & 0xf) == 0xf) {
         // TODO(yusukes): would be better to store actual double value,
         // rather than the dummy integer.
-        operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+        operands.push_back(std::make_pair(static_cast<uint32_t>(0),
                                            DICT_OPERAND_REAL));
         return true;
       }
       return OTS_FAILURE();
     }
     if ((nibble & 0x0f) == 0x0f) {
-      operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+      operands.push_back(std::make_pair(static_cast<uint32_t>(0),
                                          DICT_OPERAND_REAL));
       return true;
     }
 
     // check number format
     uint8_t nibbles[2];
     nibbles[0] = (nibble & 0xf0) >> 8;
     nibbles[1] = (nibble & 0x0f);
@@ -237,148 +241,200 @@ bool ParseDictDataBcd(
           return OTS_FAILURE();  // two or more E's.
         }
       }
     }
     ++count;
   }
 }
 
-bool ParseDictDataEscapedOperator(
-    ots::Buffer *table,
-    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+bool ParseDictDataEscapedOperator(ots::Buffer &table,
+                                  std::vector<Operand> &operands) {
   uint8_t op = 0;
-  if (!table->ReadU8(&op)) {
+  if (!table.ReadU8(&op)) {
     return OTS_FAILURE();
   }
 
   if ((op <= 14) ||
       (op >= 17 && op <= 23) ||
       (op >= 30 && op <= 38)) {
-    operands->push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR));
+    operands.push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR));
     return true;
   }
 
   // reserved area.
   return OTS_FAILURE();
 }
 
-bool ParseDictDataNumber(
-    ots::Buffer *table, uint8_t b0,
-    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+bool ParseDictDataNumber(ots::Buffer &table, uint8_t b0,
+                         std::vector<Operand> &operands) {
   uint8_t b1 = 0;
   uint8_t b2 = 0;
   uint8_t b3 = 0;
   uint8_t b4 = 0;
 
   switch (b0) {
     case 28:  // shortint
-      if (!table->ReadU8(&b1) ||
-          !table->ReadU8(&b2)) {
+      if (!table.ReadU8(&b1) ||
+          !table.ReadU8(&b2)) {
         return OTS_FAILURE();
       }
-      operands->push_back(std::make_pair(
+      operands.push_back(std::make_pair(
           static_cast<uint32_t>((b1 << 8) + b2), DICT_OPERAND_INTEGER));
       return true;
 
     case 29:  // longint
-      if (!table->ReadU8(&b1) ||
-          !table->ReadU8(&b2) ||
-          !table->ReadU8(&b3) ||
-          !table->ReadU8(&b4)) {
+      if (!table.ReadU8(&b1) ||
+          !table.ReadU8(&b2) ||
+          !table.ReadU8(&b3) ||
+          !table.ReadU8(&b4)) {
         return OTS_FAILURE();
       }
-      operands->push_back(std::make_pair(
+      operands.push_back(std::make_pair(
           static_cast<uint32_t>((b1 << 24) + (b2 << 16) + (b3 << 8) + b4),
           DICT_OPERAND_INTEGER));
       return true;
 
     case 30:  // binary coded decimal
       return ParseDictDataBcd(table, operands);
 
     default:
       break;
   }
 
   uint32_t result;
   if (b0 >=32 && b0 <=246) {
     result = b0 - 139;
   } else if (b0 >=247 && b0 <= 250) {
-    if (!table->ReadU8(&b1)) {
+    if (!table.ReadU8(&b1)) {
       return OTS_FAILURE();
     }
     result = (b0 - 247) * 256 + b1 + 108;
   } else if (b0 >= 251 && b0 <= 254) {
-    if (!table->ReadU8(&b1)) {
+    if (!table.ReadU8(&b1)) {
       return OTS_FAILURE();
     }
     result = -(b0 - 251) * 256 + b1 - 108;
   } else {
     return OTS_FAILURE();
   }
 
-  operands->push_back(std::make_pair(result, DICT_OPERAND_INTEGER));
+  operands.push_back(std::make_pair(result, DICT_OPERAND_INTEGER));
   return true;
 }
 
-bool ParseDictDataReadNext(
-    ots::Buffer *table,
-    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+bool ParseDictDataReadNext(ots::Buffer &table,
+                           std::vector<Operand> &operands) {
   uint8_t op = 0;
-  if (!table->ReadU8(&op)) {
+  if (!table.ReadU8(&op)) {
     return OTS_FAILURE();
   }
-  if (op <= 21) {
+  if (op <= 24) {
     if (op == 12) {
       return ParseDictDataEscapedOperator(table, operands);
     }
-    operands->push_back(std::make_pair(
+    operands.push_back(std::make_pair(
         static_cast<uint32_t>(op), DICT_OPERATOR));
     return true;
   } else if (op <= 27 || op == 31 || op == 255) {
     // reserved area.
     return OTS_FAILURE();
   }
 
   return ParseDictDataNumber(table, op, operands);
 }
 
+bool OperandsOverflow(std::vector<Operand>& operands, bool cff2) {
+  // An operator may be preceded by up to a maximum of 48 operands in CFF1 and
+  // 513 operands in CFF2.
+  if ((cff2 && operands.size() > ots::kMaxCFF2ArgumentStack) ||
+      (!cff2 && operands.size() > ots::kMaxCFF1ArgumentStack)) {
+    return true;
+  }
+  return false;
+}
+
+bool ParseDictDataReadOperands(ots::Buffer& dict,
+                               std::vector<Operand>& operands,
+                               bool cff2) {
+  if (!ParseDictDataReadNext(dict, operands)) {
+    return OTS_FAILURE();
+  }
+  if (operands.empty()) {
+    return OTS_FAILURE();
+  }
+  if (OperandsOverflow(operands, cff2)) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool ValidCFF2DictOp(uint32_t op, DICT_DATA_TYPE type) {
+  if (type == DICT_DATA_TOPLEVEL) {
+    switch (op) {
+      case (12U << 8) + 7:  // FontMatrix
+      case 17:              // CharStrings
+      case (12U << 8) + 36: // FDArray
+      case (12U << 8) + 37: // FDSelect
+      case 24:              // vstore
+        return true;
+      default:
+        return false;
+    }
+  } else if (type == DICT_DATA_FDARRAY) {
+    if (op == 18) // Private DICT
+      return true;
+  } else if (type == DICT_DATA_PRIVATE) {
+    switch (op) {
+      case (12U << 8) + 14: // ForceBold
+      case (12U << 8) + 19: // initialRandomSeed
+      case 20:              // defaultWidthX
+      case 21:              // nominalWidthX
+        return false;
+      default:
+        return true;
+    }
+  }
+
+  return false;
+}
+
 bool ParsePrivateDictData(
-    const uint8_t *data,
-    size_t table_length, size_t offset, size_t dict_length,
+    ots::Buffer &table, size_t offset, size_t dict_length,
     DICT_DATA_TYPE type, ots::OpenTypeCFF *out_cff) {
-  ots::Buffer table(data + offset, dict_length);
-  std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+  ots::Buffer dict(table.buffer() + offset, dict_length);
+  std::vector<Operand> operands;
+  bool cff2 = (out_cff->major == 2);
+  bool blend_seen = false;
+  uint32_t vsindex = 0;
 
   // Since a Private DICT for FDArray might not have a Local Subr (e.g. Hiragino
   // Kaku Gothic Std W8), we create an empty Local Subr here to match the size
   // of FDArray the size of |local_subrs_per_font|.
   if (type == DICT_DATA_FDARRAY) {
     out_cff->local_subrs_per_font.push_back(new ots::CFFIndex);
   }
 
-  while (table.offset() < dict_length) {
-    if (!ParseDictDataReadNext(&table, &operands)) {
-      return OTS_FAILURE();
-    }
-    if (operands.empty()) {
-      return OTS_FAILURE();
-    }
-    if (operands.size() > 48) {
-      // An operator may be preceded by up to a maximum of 48 operands.
+  while (dict.offset() < dict.length()) {
+    if (!ParseDictDataReadOperands(dict, operands, cff2)) {
       return OTS_FAILURE();
     }
     if (operands.back().second != DICT_OPERATOR) {
       continue;
     }
 
     // got operator
     const uint32_t op = operands.back().first;
     operands.pop_back();
 
+    if (cff2 && !ValidCFF2DictOp(op, DICT_DATA_PRIVATE)) {
+      return OTS_FAILURE();
+    }
+
+    bool clear_operands = true;
     switch (op) {
       // hints
       case 6:  // BlueValues
       case 7:  // OtherBlues
       case 8:  // FamilyBlues
       case 9:  // FamilyOtherBlues
         if ((operands.size() % 2) != 0) {
           return OTS_FAILURE();
@@ -415,36 +471,35 @@ bool ParsePrivateDictData(
           return OTS_FAILURE();
         }
         if (operands.back().second != DICT_OPERAND_INTEGER) {
           return OTS_FAILURE();
         }
         if (operands.back().first >= 1024 * 1024 * 1024) {
           return OTS_FAILURE();
         }
-        if (operands.back().first + offset >= table_length) {
+        if (operands.back().first + offset >= table.length()) {
           return OTS_FAILURE();
         }
         // parse "16. Local Subrs INDEX"
-        ots::Buffer cff_table(data, table_length);
-        cff_table.set_offset(operands.back().first + offset);
+        table.set_offset(operands.back().first + offset);
         ots::CFFIndex *local_subrs_index = NULL;
         if (type == DICT_DATA_FDARRAY) {
           if (out_cff->local_subrs_per_font.empty()) {
             return OTS_FAILURE();  // not reached.
           }
           local_subrs_index = out_cff->local_subrs_per_font.back();
         } else { // type == DICT_DATA_TOPLEVEL
           if (out_cff->local_subrs) {
             return OTS_FAILURE();  // two or more local_subrs?
           }
           local_subrs_index = new ots::CFFIndex;
           out_cff->local_subrs = local_subrs_index;
         }
-        if (!ParseIndex(&cff_table, local_subrs_index)) {
+        if (!ParseIndex(table, *local_subrs_index, cff2)) {
           return OTS_FAILURE();
         }
         break;
       }
 
       // boolean
       case (12U << 8) + 14:  // ForceBold
         if (operands.size() != 1) {
@@ -453,590 +508,858 @@ bool ParsePrivateDictData(
         if (operands.back().second != DICT_OPERAND_INTEGER) {
           return OTS_FAILURE();
         }
         if (operands.back().first >= 2) {
           return OTS_FAILURE();
         }
         break;
 
+      case 22: { // vsindex
+        if (!cff2) {
+          return OTS_FAILURE();
+        }
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        if (blend_seen) {
+          return OTS_FAILURE();
+        }
+        vsindex = operands.back().first;
+        if (vsindex >= out_cff->region_index_count.size()) {
+          return OTS_FAILURE();
+        }
+        break;
+      }
+
+      case 23: { // blend
+        if (!cff2) {
+          return OTS_FAILURE();
+        }
+        if (operands.size() < 1) {
+          return OTS_FAILURE();
+        }
+        if (vsindex >= out_cff->region_index_count.size()) {
+          return OTS_FAILURE();
+        }
+        uint16_t k = out_cff->region_index_count.at(vsindex);
+        uint16_t n = operands.back().first;
+        if (operands.size() < n * (k + 1) + 1) {
+          return OTS_FAILURE();
+        }
+        size_t operands_size = operands.size();
+        // Keep the 1st n operands on the stack for the next operator to use
+        // and pop the rest. There can be multiple consecutive blend operator,
+        // so this makes sure the operands of all of them are kept on the
+        // stack.
+        while (operands.size() > operands_size - ((n * k) + 1))
+          operands.pop_back();
+        clear_operands = false;
+        blend_seen = true;
+        break;
+      }
+
       default:
         return OTS_FAILURE();
     }
-    operands.clear();
+    if (clear_operands) {
+      operands.clear();
+    }
   }
 
   return true;
 }
 
-bool ParseDictData(const uint8_t *data, size_t table_length,
-                   const ots::CFFIndex &index, uint16_t glyphs,
-                   size_t sid_max, DICT_DATA_TYPE type,
+bool ParseVariationStore(ots::OpenTypeCFF& out_cff, ots::Buffer& table) {
+  uint16_t length;
+
+  if (!table.ReadU16(&length)) {
+    return OTS_FAILURE();
+  }
+
+  // Empty VariationStore is allowed.
+  if (!length) {
+    return true;
+  }
+
+  if (length > table.remaining()) {
+    return OTS_FAILURE();
+  }
+
+  if (!ParseItemVariationStore(out_cff.GetFont(),
+                               table.buffer() + table.offset(), length,
+                               &(out_cff.region_index_count))) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseDictData(ots::Buffer& table, ots::Buffer& dict,
+                   uint16_t glyphs, size_t sid_max, DICT_DATA_TYPE type,
+                   ots::OpenTypeCFF *out_cff);
+
+bool ParseDictData(ots::Buffer& table, const ots::CFFIndex &index,
+                   uint16_t glyphs, size_t sid_max, DICT_DATA_TYPE type,
                    ots::OpenTypeCFF *out_cff) {
   for (unsigned i = 1; i < index.offsets.size(); ++i) {
-    if (type == DICT_DATA_TOPLEVEL) {
-      out_cff->char_strings_array.push_back(new ots::CFFIndex);
+    size_t dict_length = index.offsets[i] - index.offsets[i - 1];
+    ots::Buffer dict(table.buffer() + index.offsets[i - 1], dict_length);
+
+    if (!ParseDictData(table, dict, glyphs, sid_max, type, out_cff)) {
+      return OTS_FAILURE();
     }
-    size_t dict_length = index.offsets[i] - index.offsets[i - 1];
-    ots::Buffer table(data + index.offsets[i - 1], dict_length);
-
-    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+  }
+  return true;
+}
 
-    FONT_FORMAT font_format = FORMAT_UNKNOWN;
-    bool have_ros = false;
-    uint16_t charstring_glyphs = 0;
-    size_t charset_offset = 0;
+bool ParseDictData(ots::Buffer& table, ots::Buffer& dict,
+                   uint16_t glyphs, size_t sid_max, DICT_DATA_TYPE type,
+                   ots::OpenTypeCFF *out_cff) {
+  bool cff2 = (out_cff->major == 2);
+  std::vector<Operand> operands;
 
-    while (table.offset() < dict_length) {
-      if (!ParseDictDataReadNext(&table, &operands)) {
-        return OTS_FAILURE();
-      }
-      if (operands.empty()) {
-        return OTS_FAILURE();
-      }
-      if (operands.size() > 48) {
-        // An operator may be preceded by up to a maximum of 48 operands.
+  FONT_FORMAT font_format = FORMAT_UNKNOWN;
+  bool have_ros = false;
+  bool have_charstrings = false;
+  bool have_vstore = false;
+  size_t charset_offset = 0;
+
+  if (cff2) {
+    // Parse VariationStore first, since it might be referenced in other places
+    // (e.g. FDArray) that might be parsed after it.
+    size_t dict_offset = dict.offset();
+    while (dict.offset() < dict.length()) {
+      if (!ParseDictDataReadOperands(dict, operands, cff2)) {
         return OTS_FAILURE();
       }
       if (operands.back().second != DICT_OPERATOR) continue;
 
       // got operator
       const uint32_t op = operands.back().first;
       operands.pop_back();
 
-      switch (op) {
-        // SID
-        case 0:   // version
-        case 1:   // Notice
-        case 2:   // Copyright
-        case 3:   // FullName
-        case 4:   // FamilyName
-        case (12U << 8) + 0:   // Copyright
-        case (12U << 8) + 21:  // PostScript
-        case (12U << 8) + 22:  // BaseFontName
-        case (12U << 8) + 38:  // FontName
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (!CheckSid(operands.back(), sid_max)) {
-            return OTS_FAILURE();
-          }
-          break;
+      if (op == 24) {  // vstore
+        if (type != DICT_DATA_TOPLEVEL) {
+          return OTS_FAILURE();
+        }
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (!CheckOffset(operands.back(), table.length())) {
+          return OTS_FAILURE();
+        }
+        // parse "VariationStore Data Contents"
+        table.set_offset(operands.back().first);
+        if (!ParseVariationStore(*out_cff, table)) {
+          return OTS_FAILURE();
+        }
+        break;
+      }
+      operands.clear();
+    }
+    operands.clear();
+    dict.set_offset(dict_offset);
+  }
+
+  while (dict.offset() < dict.length()) {
+    if (!ParseDictDataReadOperands(dict, operands, cff2)) {
+      return OTS_FAILURE();
+    }
+    if (operands.back().second != DICT_OPERATOR) continue;
+
+    // got operator
+    const uint32_t op = operands.back().first;
+    operands.pop_back();
+
+    if (cff2 && !ValidCFF2DictOp(op, type)) {
+      return OTS_FAILURE();
+    }
+
+    switch (op) {
+      // SID
+      case 0:   // version
+      case 1:   // Notice
+      case 2:   // Copyright
+      case 3:   // FullName
+      case 4:   // FamilyName
+      case (12U << 8) + 0:   // Copyright
+      case (12U << 8) + 21:  // PostScript
+      case (12U << 8) + 22:  // BaseFontName
+      case (12U << 8) + 38:  // FontName
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (!CheckSid(operands.back(), sid_max)) {
+          return OTS_FAILURE();
+        }
+        break;
 
-        // array
-        case 5:   // FontBBox
-        case 14:  // XUID
-        case (12U << 8) + 7:   // FontMatrix
-        case (12U << 8) + 23:  // BaseFontBlend (delta)
-          if (operands.empty()) {
-            return OTS_FAILURE();
-          }
-          break;
+      // array
+      case 5:   // FontBBox
+      case 14:  // XUID
+      case (12U << 8) + 7:   // FontMatrix
+      case (12U << 8) + 23:  // BaseFontBlend (delta)
+        if (operands.empty()) {
+          return OTS_FAILURE();
+        }
+        break;
 
-        // number
-        case 13:  // UniqueID
-        case (12U << 8) + 2:   // ItalicAngle
-        case (12U << 8) + 3:   // UnderlinePosition
-        case (12U << 8) + 4:   // UnderlineThickness
-        case (12U << 8) + 5:   // PaintType
-        case (12U << 8) + 8:   // StrokeWidth
-        case (12U << 8) + 20:  // SyntheticBase
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
+      // number
+      case 13:  // UniqueID
+      case (12U << 8) + 2:   // ItalicAngle
+      case (12U << 8) + 3:   // UnderlinePosition
+      case (12U << 8) + 4:   // UnderlineThickness
+      case (12U << 8) + 5:   // PaintType
+      case (12U << 8) + 8:   // StrokeWidth
+      case (12U << 8) + 20:  // SyntheticBase
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        break;
+      case (12U << 8) + 31:  // CIDFontVersion
+      case (12U << 8) + 32:  // CIDFontRevision
+      case (12U << 8) + 33:  // CIDFontType
+      case (12U << 8) + 34:  // CIDCount
+      case (12U << 8) + 35:  // UIDBase
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (font_format != FORMAT_CID_KEYED) {
+          return OTS_FAILURE();
+        }
+        break;
+      case (12U << 8) + 6:   // CharstringType
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if(operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first != 2) {
+          // We only support the "Type 2 Charstring Format."
+          // TODO(yusukes): Support Type 1 format? Is that still in use?
+          return OTS_FAILURE();
+        }
+        break;
+
+      // boolean
+      case (12U << 8) + 1:   // isFixedPitch
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first >= 2) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      // offset(0)
+      case 15:  // charset
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first <= 2) {
+          // predefined charset, ISOAdobe, Expert or ExpertSubset, is used.
           break;
-        case (12U << 8) + 31:  // CIDFontVersion
-        case (12U << 8) + 32:  // CIDFontRevision
-        case (12U << 8) + 33:  // CIDFontType
-        case (12U << 8) + 34:  // CIDCount
-        case (12U << 8) + 35:  // UIDBase
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (font_format != FORMAT_CID_KEYED) {
-            return OTS_FAILURE();
-          }
-          break;
-        case (12U << 8) + 6:   // CharstringType
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if(operands.back().second != DICT_OPERAND_INTEGER) {
-            return OTS_FAILURE();
-          }
-          if (operands.back().first != 2) {
-            // We only support the "Type 2 Charstring Format."
-            // TODO(yusukes): Support Type 1 format? Is that still in use?
-            return OTS_FAILURE();
+        }
+        if (!CheckOffset(operands.back(), table.length())) {
+          return OTS_FAILURE();
+        }
+        if (charset_offset) {
+          return OTS_FAILURE();  // multiple charset tables?
+        }
+        charset_offset = operands.back().first;
+        break;
+
+      case 16: {  // Encoding
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first <= 1) {
+          break;  // predefined encoding, "Standard" or "Expert", is used.
+        }
+        if (!CheckOffset(operands.back(), table.length())) {
+          return OTS_FAILURE();
+        }
+
+        table.set_offset(operands.back().first);
+        uint8_t format = 0;
+        if (!table.ReadU8(&format)) {
+          return OTS_FAILURE();
+        }
+        if (format & 0x80) {
+          // supplemental encoding is not supported at the moment.
+          return OTS_FAILURE();
+        }
+        // TODO(yusukes): support & parse supplemental encoding tables.
+        break;
+      }
+
+      case 17: {  // CharStrings
+        if (type != DICT_DATA_TOPLEVEL) {
+          return OTS_FAILURE();
+        }
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (!CheckOffset(operands.back(), table.length())) {
+          return OTS_FAILURE();
+        }
+        // parse "14. CharStrings INDEX"
+        table.set_offset(operands.back().first);
+        ots::CFFIndex *charstring_index = out_cff->charstrings_index;
+        if (!ParseIndex(table, *charstring_index, cff2)) {
+          return OTS_FAILURE();
+        }
+        if (charstring_index->count < 2) {
+          return OTS_FAILURE();
+        }
+        if (have_charstrings) {
+          return OTS_FAILURE();  // multiple charstring tables?
+        }
+        have_charstrings = true;
+        if (charstring_index->count != glyphs) {
+          return OTS_FAILURE();  // CFF and maxp have different number of glyphs?
+        }
+        break;
+      }
+
+      case 24: {  // vstore
+        if (!cff2) {
+          return OTS_FAILURE();
+        }
+        if (have_vstore) {
+          return OTS_FAILURE();  // multiple vstore tables?
+        }
+        have_vstore = true;
+        // parsed above.
+        break;
+      }
+
+      case (12U << 8) + 36: {  // FDArray
+        if (type != DICT_DATA_TOPLEVEL) {
+          return OTS_FAILURE();
+        }
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (!CheckOffset(operands.back(), table.length())) {
+          return OTS_FAILURE();
+        }
+
+        // parse Font DICT INDEX.
+        table.set_offset(operands.back().first);
+        ots::CFFIndex sub_dict_index;
+        if (!ParseIndex(table, sub_dict_index, cff2)) {
+          return OTS_FAILURE();
+        }
+        if (!ParseDictData(table, sub_dict_index,
+                           glyphs, sid_max, DICT_DATA_FDARRAY,
+                           out_cff)) {
+          return OTS_FAILURE();
+        }
+        if (out_cff->font_dict_length != 0) {
+          return OTS_FAILURE();  // two or more FDArray found.
+        }
+        out_cff->font_dict_length = sub_dict_index.count;
+        break;
+      }
+
+      case (12U << 8) + 37: {  // FDSelect
+        if (type != DICT_DATA_TOPLEVEL) {
+          return OTS_FAILURE();
+        }
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (!CheckOffset(operands.back(), table.length())) {
+          return OTS_FAILURE();
+        }
+
+        // parse FDSelect data structure
+        table.set_offset(operands.back().first);
+        uint8_t format = 0;
+        if (!table.ReadU8(&format)) {
+          return OTS_FAILURE();
+        }
+        if (format == 0) {
+          for (uint16_t j = 0; j < glyphs; ++j) {
+            uint8_t fd_index = 0;
+            if (!table.ReadU8(&fd_index)) {
+              return OTS_FAILURE();
+            }
+            (out_cff->fd_select)[j] = fd_index;
           }
-          break;
-
-        // boolean
-        case (12U << 8) + 1:   // isFixedPitch
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (operands.back().second != DICT_OPERAND_INTEGER) {
-            return OTS_FAILURE();
-          }
-          if (operands.back().first >= 2) {
-            return OTS_FAILURE();
-          }
-          break;
-
-        // offset(0)
-        case 15:  // charset
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (operands.back().first <= 2) {
-            // predefined charset, ISOAdobe, Expert or ExpertSubset, is used.
-            break;
-          }
-          if (!CheckOffset(operands.back(), table_length)) {
+        } else if (format == 3) {
+          uint16_t n_ranges = 0;
+          if (!table.ReadU16(&n_ranges)) {
             return OTS_FAILURE();
           }
-          if (charset_offset) {
-            return OTS_FAILURE();  // multiple charset tables?
-          }
-          charset_offset = operands.back().first;
-          break;
-
-        case 16: {  // Encoding
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (operands.back().first <= 1) {
-            break;  // predefined encoding, "Standard" or "Expert", is used.
-          }
-          if (!CheckOffset(operands.back(), table_length)) {
-            return OTS_FAILURE();
-          }
-
-          // parse sub dictionary INDEX.
-          ots::Buffer cff_table(data, table_length);
-          cff_table.set_offset(operands.back().first);
-          uint8_t format = 0;
-          if (!cff_table.ReadU8(&format)) {
-            return OTS_FAILURE();
-          }
-          if (format & 0x80) {
-            // supplemental encoding is not supported at the moment.
-            return OTS_FAILURE();
-          }
-          // TODO(yusukes): support & parse supplemental encoding tables.
-          break;
-        }
-
-        case 17: {  // CharStrings
-          if (type != DICT_DATA_TOPLEVEL) {
-            return OTS_FAILURE();
-          }
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (!CheckOffset(operands.back(), table_length)) {
-            return OTS_FAILURE();
-          }
-          // parse "14. CharStrings INDEX"
-          ots::Buffer cff_table(data, table_length);
-          cff_table.set_offset(operands.back().first);
-          ots::CFFIndex *charstring_index = out_cff->char_strings_array.back();
-          if (!ParseIndex(&cff_table, charstring_index)) {
-            return OTS_FAILURE();
-          }
-          if (charstring_index->count < 2) {
-            return OTS_FAILURE();
-          }
-          if (charstring_glyphs) {
-            return OTS_FAILURE();  // multiple charstring tables?
-          }
-          charstring_glyphs = charstring_index->count;
-          if (charstring_glyphs != glyphs) {
-            return OTS_FAILURE();  // CFF and maxp have different number of glyphs?
-          }
-          break;
-        }
-
-        case (12U << 8) + 36: {  // FDArray
-          if (type != DICT_DATA_TOPLEVEL) {
-            return OTS_FAILURE();
-          }
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (!CheckOffset(operands.back(), table_length)) {
-            return OTS_FAILURE();
-          }
-
-          // parse sub dictionary INDEX.
-          ots::Buffer cff_table(data, table_length);
-          cff_table.set_offset(operands.back().first);
-          ots::CFFIndex sub_dict_index;
-          if (!ParseIndex(&cff_table, &sub_dict_index)) {
-            return OTS_FAILURE();
-          }
-          if (!ParseDictData(data, table_length,
-                             sub_dict_index,
-                             glyphs, sid_max, DICT_DATA_FDARRAY,
-                             out_cff)) {
-            return OTS_FAILURE();
-          }
-          if (out_cff->font_dict_length != 0) {
-            return OTS_FAILURE();  // two or more FDArray found.
-          }
-          out_cff->font_dict_length = sub_dict_index.count;
-          break;
-        }
-
-        case (12U << 8) + 37: {  // FDSelect
-          if (type != DICT_DATA_TOPLEVEL) {
-            return OTS_FAILURE();
-          }
-          if (operands.size() != 1) {
-            return OTS_FAILURE();
-          }
-          if (!CheckOffset(operands.back(), table_length)) {
+          if (n_ranges == 0) {
             return OTS_FAILURE();
           }
 
-          // parse FDSelect data structure
-          ots::Buffer cff_table(data, table_length);
-          cff_table.set_offset(operands.back().first);
-          uint8_t format = 0;
-          if (!cff_table.ReadU8(&format)) {
+          uint16_t last_gid = 0;
+          uint8_t fd_index = 0;
+          for (unsigned j = 0; j < n_ranges; ++j) {
+            uint16_t first = 0;  // GID
+            if (!table.ReadU16(&first)) {
+              return OTS_FAILURE();
+            }
+
+            // Sanity checks.
+            if ((j == 0) && (first != 0)) {
+              return OTS_FAILURE();
+            }
+            if ((j != 0) && (last_gid >= first)) {
+              return OTS_FAILURE();  // not increasing order.
+            }
+            if (first >= glyphs) {
+              return OTS_FAILURE();  // invalid gid.
+            }
+
+            // Copy the mapping to |out_cff->fd_select|.
+            if (j != 0) {
+              for (auto k = last_gid; k < first; ++k) {
+                if (!out_cff->fd_select.insert(
+                        std::make_pair(k, fd_index)).second) {
+                  return OTS_FAILURE();
+                }
+              }
+            }
+
+            if (!table.ReadU8(&fd_index)) {
+              return OTS_FAILURE();
+            }
+            last_gid = first;
+          }
+          uint16_t sentinel = 0;
+          if (!table.ReadU16(&sentinel)) {
             return OTS_FAILURE();
           }
-          if (format == 0) {
-            for (uint16_t j = 0; j < glyphs; ++j) {
-              uint8_t fd_index = 0;
-              if (!cff_table.ReadU8(&fd_index)) {
-                return OTS_FAILURE();
-              }
-              (out_cff->fd_select)[j] = fd_index;
-            }
-          } else if (format == 3) {
-            uint16_t n_ranges = 0;
-            if (!cff_table.ReadU16(&n_ranges)) {
+          if (last_gid >= sentinel) {
+            return OTS_FAILURE();
+          }
+          if (sentinel > glyphs) {
+            return OTS_FAILURE();  // invalid gid.
+          }
+          for (auto k = last_gid; k < sentinel; ++k) {
+            if (!out_cff->fd_select.insert(
+                    std::make_pair(k, fd_index)).second) {
               return OTS_FAILURE();
             }
-            if (n_ranges == 0) {
+          }
+        } else if (cff2 && format == 4) {
+          uint32_t n_ranges = 0;
+          if (!table.ReadU32(&n_ranges)) {
+            return OTS_FAILURE();
+          }
+          if (n_ranges == 0) {
+            return OTS_FAILURE();
+          }
+
+          uint32_t last_gid = 0;
+          uint16_t fd_index = 0;
+          for (unsigned j = 0; j < n_ranges; ++j) {
+            uint32_t first = 0;  // GID
+            if (!table.ReadU32(&first)) {
               return OTS_FAILURE();
             }
 
-            uint16_t last_gid = 0;
-            uint8_t fd_index = 0;
-            for (unsigned j = 0; j < n_ranges; ++j) {
-              uint16_t first = 0;  // GID
-              if (!cff_table.ReadU16(&first)) {
-                return OTS_FAILURE();
-              }
-
-              // Sanity checks.
-              if ((j == 0) && (first != 0)) {
-                return OTS_FAILURE();
-              }
-              if ((j != 0) && (last_gid >= first)) {
-                return OTS_FAILURE();  // not increasing order.
-              }
-
-              // Copy the mapping to |out_cff->fd_select|.
-              if (j != 0) {
-                for (uint16_t k = last_gid; k < first; ++k) {
-                  if (!out_cff->fd_select.insert(
-                          std::make_pair(k, fd_index)).second) {
-                    return OTS_FAILURE();
-                  }
-                }
-              }
-
-              if (!cff_table.ReadU8(&fd_index)) {
-                return OTS_FAILURE();
-              }
-              last_gid = first;
-              // TODO(yusukes): check GID?
-            }
-            uint16_t sentinel = 0;
-            if (!cff_table.ReadU16(&sentinel)) {
-              return OTS_FAILURE();
-            }
-            if (last_gid >= sentinel) {
+            // Sanity checks.
+            if ((j == 0) && (first != 0)) {
               return OTS_FAILURE();
             }
-            for (uint16_t k = last_gid; k < sentinel; ++k) {
-              if (!out_cff->fd_select.insert(
-                      std::make_pair(k, fd_index)).second) {
-                return OTS_FAILURE();
+            if ((j != 0) && (last_gid >= first)) {
+              return OTS_FAILURE();  // not increasing order.
+            }
+            if (first >= glyphs) {
+              return OTS_FAILURE();  // invalid gid.
+            }
+
+            // Copy the mapping to |out_cff->fd_select|.
+            if (j != 0) {
+              for (auto k = last_gid; k < first; ++k) {
+                if (!out_cff->fd_select.insert(
+                        std::make_pair(k, fd_index)).second) {
+                  return OTS_FAILURE();
+                }
               }
             }
-          } else {
-            // unknown format
-            return OTS_FAILURE();
+
+            if (!table.ReadU16(&fd_index)) {
+              return OTS_FAILURE();
+            }
+            last_gid = first;
           }
-          break;
-        }
-
-        // Private DICT (2 * number)
-        case 18: {
-          if (operands.size() != 2) {
+          uint32_t sentinel = 0;
+          if (!table.ReadU32(&sentinel)) {
             return OTS_FAILURE();
           }
-          if (operands.back().second != DICT_OPERAND_INTEGER) {
-            return OTS_FAILURE();
-          }
-          const uint32_t private_offset = operands.back().first;
-          operands.pop_back();
-          if (operands.back().second != DICT_OPERAND_INTEGER) {
-            return OTS_FAILURE();
-          }
-          const uint32_t private_length = operands.back().first;
-          if (private_offset > table_length) {
-            return OTS_FAILURE();
-          }
-          if (private_length >= table_length) {
-            return OTS_FAILURE();
-          }
-          if (private_length + private_offset > table_length) {
-            return OTS_FAILURE();
-          }
-          // parse "15. Private DICT Data"
-          if (!ParsePrivateDictData(data, table_length,
-                                    private_offset, private_length,
-                                    type, out_cff)) {
+          if (last_gid >= sentinel) {
             return OTS_FAILURE();
           }
-          break;
-        }
-
-        // ROS
-        case (12U << 8) + 30:
-          if (font_format != FORMAT_UNKNOWN) {
-            return OTS_FAILURE();
+          if (sentinel > glyphs) {
+            return OTS_FAILURE();  // invalid gid.
+          }
+          for (auto k = last_gid; k < sentinel; ++k) {
+            if (!out_cff->fd_select.insert(
+                    std::make_pair(k, fd_index)).second) {
+              return OTS_FAILURE();
+            }
           }
-          font_format = FORMAT_CID_KEYED;
-          if (operands.size() != 3) {
-            return OTS_FAILURE();
-          }
-          // check SIDs
-          operands.pop_back();  // ignore the first number.
-          if (!CheckSid(operands.back(), sid_max)) {
-            return OTS_FAILURE();
-          }
-          operands.pop_back();
-          if (!CheckSid(operands.back(), sid_max)) {
+        } else {
+          // unknown format
+          return OTS_FAILURE();
+        }
+        break;
+      }
+
+      // Private DICT (2 * number)
+      case 18: {
+        if (operands.size() != 2) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        const uint32_t private_offset = operands.back().first;
+        operands.pop_back();
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        const uint32_t private_length = operands.back().first;
+        if (private_offset > table.length()) {
+          return OTS_FAILURE();
+        }
+        if (private_length >= table.length()) {
+          return OTS_FAILURE();
+        }
+        if (private_length + private_offset > table.length()) {
+          return OTS_FAILURE();
+        }
+        // parse "15. Private DICT data"
+        if (!ParsePrivateDictData(table, private_offset, private_length,
+                                  type, out_cff)) {
+          return OTS_FAILURE();
+        }
+        break;
+      }
+
+      // ROS
+      case (12U << 8) + 30:
+        if (font_format != FORMAT_UNKNOWN) {
+          return OTS_FAILURE();
+        }
+        font_format = FORMAT_CID_KEYED;
+        if (operands.size() != 3) {
+          return OTS_FAILURE();
+        }
+        // check SIDs
+        operands.pop_back();  // ignore the first number.
+        if (!CheckSid(operands.back(), sid_max)) {
+          return OTS_FAILURE();
+        }
+        operands.pop_back();
+        if (!CheckSid(operands.back(), sid_max)) {
+          return OTS_FAILURE();
+        }
+        if (have_ros) {
+          return OTS_FAILURE();  // multiple ROS tables?
+        }
+        have_ros = true;
+        break;
+
+      default:
+        return OTS_FAILURE();
+    }
+    operands.clear();
+
+    if (font_format == FORMAT_UNKNOWN) {
+      font_format = FORMAT_OTHER;
+    }
+  }
+
+  // parse "13. Charsets"
+  if (charset_offset) {
+    table.set_offset(charset_offset);
+    uint8_t format = 0;
+    if (!table.ReadU8(&format)) {
+      return OTS_FAILURE();
+    }
+    switch (format) {
+      case 0:
+        for (uint16_t j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
+          uint16_t sid = 0;
+          if (!table.ReadU16(&sid)) {
             return OTS_FAILURE();
           }
-          if (have_ros) {
-            return OTS_FAILURE();  // multiple ROS tables?
+          if (!have_ros && (sid > sid_max)) {
+            return OTS_FAILURE();
           }
-          have_ros = true;
-          break;
-
-        default:
-          return OTS_FAILURE();
-      }
-      operands.clear();
-
-      if (font_format == FORMAT_UNKNOWN) {
-        font_format = FORMAT_OTHER;
-      }
-    }
+          // TODO(yusukes): check CIDs when have_ros is true.
+        }
+        break;
 
-    // parse "13. Charsets"
-    if (charset_offset) {
-      ots::Buffer cff_table(data, table_length);
-      cff_table.set_offset(charset_offset);
-      uint8_t format = 0;
-      if (!cff_table.ReadU8(&format)) {
-        return OTS_FAILURE();
-      }
-      switch (format) {
-        case 0:
-          for (uint16_t j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
-            uint16_t sid = 0;
-            if (!cff_table.ReadU16(&sid)) {
-              return OTS_FAILURE();
-            }
-            if (!have_ros && (sid > sid_max)) {
+      case 1:
+      case 2: {
+        uint32_t total = 1;  // .notdef is omitted.
+        while (total < glyphs) {
+          uint16_t sid = 0;
+          if (!table.ReadU16(&sid)) {
+            return OTS_FAILURE();
+          }
+          if (!have_ros && (sid > sid_max)) {
+            return OTS_FAILURE();
+          }
+          // TODO(yusukes): check CIDs when have_ros is true.
+
+          if (format == 1) {
+            uint8_t left = 0;
+            if (!table.ReadU8(&left)) {
               return OTS_FAILURE();
             }
-            // TODO(yusukes): check CIDs when have_ros is true.
-          }
-          break;
-
-        case 1:
-        case 2: {
-          uint32_t total = 1;  // .notdef is omitted.
-          while (total < glyphs) {
-            uint16_t sid = 0;
-            if (!cff_table.ReadU16(&sid)) {
-              return OTS_FAILURE();
-            }
-            if (!have_ros && (sid > sid_max)) {
+            total += (left + 1);
+          } else {
+            uint16_t left = 0;
+            if (!table.ReadU16(&left)) {
               return OTS_FAILURE();
             }
-            // TODO(yusukes): check CIDs when have_ros is true.
+            total += (left + 1);
+          }
+        }
+        break;
+      }
 
-            if (format == 1) {
-              uint8_t left = 0;
-              if (!cff_table.ReadU8(&left)) {
-                return OTS_FAILURE();
-              }
-              total += (left + 1);
-            } else {
-              uint16_t left = 0;
-              if (!cff_table.ReadU16(&left)) {
-                return OTS_FAILURE();
-              }
-              total += (left + 1);
-            }
-          }
-          break;
-        }
-
-        default:
-          return OTS_FAILURE();
-      }
+      default:
+        return OTS_FAILURE();
     }
   }
   return true;
 }
 
 }  // namespace
 
 namespace ots {
 
+bool OpenTypeCFF::ValidateFDSelect(uint16_t num_glyphs) {
+  for (const auto& fd_select : this->fd_select) {
+    if (fd_select.first >= num_glyphs) {
+      return Error("Invalid glyph index in FDSelect: %d >= %d\n",
+                   fd_select.first, num_glyphs);
+    }
+    if (fd_select.second >= this->font_dict_length) {
+      return Error("Invalid FD index: %d >= %d\n",
+                   fd_select.second, this->font_dict_length);
+    }
+  }
+  return true;
+}
+
 bool OpenTypeCFF::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
   Font *font = GetFont();
 
   this->m_data = data;
   this->m_length = length;
 
   // parse "6. Header" in the Adobe Compact Font Format Specification
   uint8_t major = 0;
   uint8_t minor = 0;
   uint8_t hdr_size = 0;
   uint8_t off_size = 0;
-  if (!table.ReadU8(&major)) {
-    return OTS_FAILURE();
-  }
-  if (!table.ReadU8(&minor)) {
-    return OTS_FAILURE();
-  }
-  if (!table.ReadU8(&hdr_size)) {
-    return OTS_FAILURE();
-  }
-  if (!table.ReadU8(&off_size)) {
-    return OTS_FAILURE();
-  }
-  if ((off_size == 0) || (off_size > 4)) {
-    return OTS_FAILURE();
+  if (!table.ReadU8(&major) ||
+      !table.ReadU8(&minor) ||
+      !table.ReadU8(&hdr_size) ||
+      !table.ReadU8(&off_size)) {
+    return Error("Failed to read table header");
   }
 
-  if ((major != 1) ||
-      (minor != 0) ||
-      (hdr_size != 4)) {
-    return OTS_FAILURE();
+  if (off_size < 1 || off_size > 4) {
+    return Error("Bad offSize: %d", off_size);
   }
-  if (hdr_size >= length) {
-    return OTS_FAILURE();
+
+  if (major != 1 || minor != 0) {
+    return Error("Unsupported table version: %d.%d", major, minor);
+  }
+
+  this->major = major;
+
+  if (hdr_size != 4 || hdr_size >= length) {
+    return Error("Bad hdrSize: %d", hdr_size);
   }
 
   // parse "7. Name INDEX"
   table.set_offset(hdr_size);
   CFFIndex name_index;
-  if (!ParseIndex(&table, &name_index)) {
-    return OTS_FAILURE();
+  if (!ParseIndex(table, name_index)) {
+    return Error("Failed to parse Name INDEX");
+  }
+  if (name_index.count != 1 || name_index.offsets.size() != 2) {
+    return Error("Name INDEX must contain only one entry, not %d",
+                 name_index.count);
   }
   if (!ParseNameData(&table, name_index, &(this->name))) {
-    return OTS_FAILURE();
+    return Error("Failed to parse Name INDEX data");
   }
 
   // parse "8. Top DICT INDEX"
   table.set_offset(name_index.offset_to_next);
   CFFIndex top_dict_index;
-  if (!ParseIndex(&table, &top_dict_index)) {
-    return OTS_FAILURE();
+  if (!ParseIndex(table, top_dict_index)) {
+    return Error("Failed to parse Top DICT INDEX");
   }
-  if (name_index.count != top_dict_index.count) {
-    return OTS_FAILURE();
+  if (top_dict_index.count != 1) {
+    return Error("Top DICT INDEX must contain only one entry, not %d",
+                 top_dict_index.count);
   }
 
   // parse "10. String INDEX"
   table.set_offset(top_dict_index.offset_to_next);
   CFFIndex string_index;
-  if (!ParseIndex(&table, &string_index)) {
-    return OTS_FAILURE();
+  if (!ParseIndex(table, string_index)) {
+    return Error("Failed to parse String INDEX");
   }
   if (string_index.count >= 65000 - kNStdString) {
-    return OTS_FAILURE();
+    return Error("Too many entries in String INDEX: %d", string_index.count);
   }
 
   OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
-    GetFont()->GetTypedTable(OTS_TAG_MAXP));
+    font->GetTypedTable(OTS_TAG_MAXP));
   if (!maxp) {
     return Error("Required maxp table missing");
   }
   const uint16_t num_glyphs = maxp->num_glyphs;
   const size_t sid_max = string_index.count + kNStdString;
   // string_index.count == 0 is allowed.
 
   // parse "9. Top DICT Data"
-  if (!ParseDictData(data, length, top_dict_index,
+  this->charstrings_index = new ots::CFFIndex;
+  if (!ParseDictData(table, top_dict_index,
                      num_glyphs, sid_max,
                      DICT_DATA_TOPLEVEL, this)) {
-    return OTS_FAILURE();
+    return Error("Failed to parse Top DICT Data");
   }
 
   // parse "16. Global Subrs INDEX"
   table.set_offset(string_index.offset_to_next);
   CFFIndex global_subrs_index;
-  if (!ParseIndex(&table, &global_subrs_index)) {
-    return OTS_FAILURE();
+  if (!ParseIndex(table, global_subrs_index)) {
+    return Error("Failed to parse Global Subrs INDEX");
   }
 
-  // Check if all fd_index in FDSelect are valid.
-  std::map<uint16_t, uint8_t>::const_iterator iter;
-  std::map<uint16_t, uint8_t>::const_iterator end = this->fd_select.end();
-  for (iter = this->fd_select.begin(); iter != end; ++iter) {
-    if (iter->second >= this->font_dict_length) {
-      return OTS_FAILURE();
-    }
+  // Check if all fd and glyph indices in FDSelect are valid.
+  if (!ValidateFDSelect(num_glyphs)) {
+    return Error("Failed to validate FDSelect");
   }
 
   // Check if all charstrings (font hinting code for each glyph) are valid.
-  for (size_t i = 0; i < this->char_strings_array.size(); ++i) {
-    if (!ValidateType2CharStringIndex(font,
-                                      *(this->char_strings_array.at(i)),
-                                      global_subrs_index,
-                                      this->fd_select,
-                                      this->local_subrs_per_font,
-                                      this->local_subrs,
-                                      &table)) {
-      return Error("Failed validating charstring set %d", (int) i);
-    }
+  if (!ValidateCFFCharStrings(*this, global_subrs_index, &table)) {
+    return Error("Failed validating CharStrings INDEX");
   }
 
   return true;
 }
 
 bool OpenTypeCFF::Serialize(OTSStream *out) {
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write table");
   }
   return true;
 }
 
 OpenTypeCFF::~OpenTypeCFF() {
-  for (size_t i = 0; i < this->char_strings_array.size(); ++i) {
-    delete (this->char_strings_array)[i];
-  }
   for (size_t i = 0; i < this->local_subrs_per_font.size(); ++i) {
     delete (this->local_subrs_per_font)[i];
   }
+  delete this->charstrings_index;
   delete this->local_subrs;
 }
 
+bool OpenTypeCFF2::Parse(const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  Font *font = GetFont();
+
+  this->m_data = data;
+  this->m_length = length;
+
+  // parse "6. Header"
+  uint8_t major = 0;
+  uint8_t minor = 0;
+  uint8_t hdr_size = 0;
+  uint16_t top_dict_size = 0;
+  if (!table.ReadU8(&major) ||
+      !table.ReadU8(&minor) ||
+      !table.ReadU8(&hdr_size) ||
+      !table.ReadU16(&top_dict_size)) {
+    return Error("Failed to read table header");
+  }
+
+  if (major != 2 || minor != 0) {
+    return Error("Unsupported table version: %d.%d", major, minor);
+  }
+
+  this->major = major;
+
+  if (hdr_size >= length) {
+    return Error("Bad hdrSize: %d", hdr_size);
+  }
+
+  if (top_dict_size == 0 || hdr_size + top_dict_size > length) {
+    return Error("Bad topDictLength: %d", top_dict_size);
+  }
+
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+    font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return Error("Required maxp table missing");
+  }
+  const uint16_t num_glyphs = maxp->num_glyphs;
+  const size_t sid_max = kNStdString;
+
+  // parse "7. Top DICT Data"
+  ots::Buffer top_dict(data + hdr_size, top_dict_size);
+  table.set_offset(hdr_size);
+  this->charstrings_index = new ots::CFFIndex;
+  if (!ParseDictData(table, top_dict,
+                     num_glyphs, sid_max,
+                     DICT_DATA_TOPLEVEL, this)) {
+    return Error("Failed to parse Top DICT Data");
+  }
+
+  // parse "9. Global Subrs INDEX"
+  table.set_offset(hdr_size + top_dict_size);
+  CFFIndex global_subrs_index;
+  if (!ParseIndex(table, global_subrs_index, true)) {
+    return Error("Failed to parse Global Subrs INDEX");
+  }
+
+  // Check if all fd and glyph indices in FDSelect are valid.
+  if (!ValidateFDSelect(num_glyphs)) {
+    return Error("Failed to validate FDSelect");
+  }
+
+  // Check if all charstrings (font hinting code for each glyph) are valid.
+  if (!ValidateCFFCharStrings(*this, global_subrs_index, &table)) {
+    return Error("Failed validating CharStrings INDEX");
+  }
+
+  return true;
+}
+
+bool OpenTypeCFF2::Serialize(OTSStream *out) {
+  if (!out->Write(this->m_data, this->m_length)) {
+    return Error("Failed to write table");
+  }
+  return true;
+}
+
 }  // namespace ots
 
 #undef TABLE_NAME
--- a/gfx/ots/src/cff.h
+++ b/gfx/ots/src/cff.h
@@ -6,57 +6,88 @@
 #define OTS_CFF_H_
 
 #include "ots.h"
 
 #include <map>
 #include <string>
 #include <vector>
 
+#undef major // glibc defines major!
+
 namespace ots {
 
 struct CFFIndex {
   CFFIndex()
       : count(0), off_size(0), offset_to_next(0) {}
-  uint16_t count;
+  uint32_t count;
   uint8_t off_size;
   std::vector<uint32_t> offsets;
   uint32_t offset_to_next;
 };
 
+typedef std::map<uint32_t, uint16_t> CFFFDSelect;
+
 class OpenTypeCFF : public Table {
  public:
   explicit OpenTypeCFF(Font *font, uint32_t tag)
       : Table(font, tag, tag),
+        major(0),
         font_dict_length(0),
+        charstrings_index(NULL),
         local_subrs(NULL),
         m_data(NULL),
         m_length(0) {
   }
 
   ~OpenTypeCFF();
 
   bool Parse(const uint8_t *data, size_t length);
   bool Serialize(OTSStream *out);
 
+  // Major version number.
+  uint8_t major;
+
   // Name INDEX. This name is used in name.cc as a postscript font name.
   std::string name;
 
   // The number of fonts the file has.
   size_t font_dict_length;
   // A map from glyph # to font #.
-  std::map<uint16_t, uint8_t> fd_select;
+  CFFFDSelect fd_select;
 
   // A list of char strings.
-  std::vector<CFFIndex *> char_strings_array;
+  CFFIndex* charstrings_index;
   // A list of Local Subrs associated with FDArrays. Can be empty.
   std::vector<CFFIndex *> local_subrs_per_font;
   // A Local Subrs associated with Top DICT. Can be NULL.
   CFFIndex *local_subrs;
 
+  // CFF2 VariationStore regionIndexCount.
+  std::vector<uint16_t> region_index_count;
+
+ protected:
+  bool ValidateFDSelect(uint16_t num_glyphs);
+
+ private:
+  const uint8_t *m_data;
+  size_t m_length;
+};
+
+class OpenTypeCFF2 : public OpenTypeCFF {
+ public:
+  explicit OpenTypeCFF2(Font *font, uint32_t tag)
+      : OpenTypeCFF(font, tag),
+        m_data(NULL),
+        m_length(0) {
+  }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
  private:
   const uint8_t *m_data;
   size_t m_length;
 };
 
 }  // namespace ots
 
 #endif  // OTS_CFF_H_
rename from gfx/ots/src/cff_type2_charstring.cc
rename to gfx/ots/src/cff_charstring.cc
--- a/gfx/ots/src/cff_type2_charstring.cc
+++ b/gfx/ots/src/cff_charstring.cc
@@ -1,163 +1,175 @@
 // Copyright (c) 2010-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // A parser for the Type 2 Charstring Format.
 // http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
 
-#include "cff_type2_charstring.h"
+#include "cff_charstring.h"
 
 #include <climits>
 #include <cstdio>
 #include <cstring>
 #include <stack>
 #include <string>
 #include <utility>
 
 #define TABLE_NAME "CFF"
 
 namespace {
 
 // Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
 // Note #5177.
 const int32_t kMaxSubrsCount = 65536;
 const size_t kMaxCharStringLength = 65535;
-const size_t kMaxArgumentStack = 48;
 const size_t kMaxNumberOfStemHints = 96;
 const size_t kMaxSubrNesting = 10;
 
 // |dummy_result| should be a huge positive integer so callsubr and callgsubr
 // will fail with the dummy value.
 const int32_t dummy_result = INT_MAX;
 
-bool ExecuteType2CharString(ots::Font *font,
-                            size_t call_depth,
-                            const ots::CFFIndex& global_subrs_index,
-                            const ots::CFFIndex& local_subrs_index,
-                            ots::Buffer *cff_table,
-                            ots::Buffer *char_string,
-                            std::stack<int32_t> *argument_stack,
-                            bool *out_found_endchar,
-                            bool *out_found_width,
-                            size_t *in_out_num_stems);
+bool ExecuteCharString(ots::OpenTypeCFF& cff,
+                       size_t call_depth,
+                       const ots::CFFIndex& global_subrs_index,
+                       const ots::CFFIndex& local_subrs_index,
+                       ots::Buffer *cff_table,
+                       ots::Buffer *char_string,
+                       std::stack<int32_t> *argument_stack,
+                       bool *out_found_endchar,
+                       bool *out_found_width,
+                       size_t *in_out_num_stems,
+                       bool cff2);
+
+bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) {
+  if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) ||
+      (!cff2 && argument_stack->size() > ots::kMaxCFF1ArgumentStack)) {
+    return true;
+  }
+  return false;
+}
 
 #ifdef DUMP_T2CHARSTRING
 // Converts |op| to a string and returns it.
-const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
+const char *CharStringOperatorToString(ots::CharStringOperator op) {
   switch (op) {
   case ots::kHStem:
-    return "HStem";
+    return "hstem";
   case ots::kVStem:
-    return "VStem";
+    return "vstem";
   case ots::kVMoveTo:
-    return "VMoveTo";
+    return "vmoveto";
   case ots::kRLineTo:
-    return "RLineTo";
+    return "rlineto";
   case ots::kHLineTo:
-    return "HLineTo";
+    return "hlineto";
   case ots::kVLineTo:
-    return "VLineTo";
+    return "vlineto";
   case ots::kRRCurveTo:
-    return "RRCurveTo";
+    return "rrcurveto";
   case ots::kCallSubr:
-    return "CallSubr";
+    return "callsubr";
   case ots::kReturn:
-    return "Return";
+    return "return";
   case ots::kEndChar:
-    return "EndChar";
+    return "endchar";
+  case ots::kVSIndex:
+    return "vsindex";
+  case ots::kBlend:
+    return "blend";
   case ots::kHStemHm:
-    return "HStemHm";
+    return "hstemhm";
   case ots::kHintMask:
-    return "HintMask";
+    return "hintmask";
   case ots::kCntrMask:
-    return "CntrMask";
+    return "cntrmask";
   case ots::kRMoveTo:
-    return "RMoveTo";
+    return "rmoveto";
   case ots::kHMoveTo:
-    return "HMoveTo";
+    return "hmoveto";
   case ots::kVStemHm:
-    return "VStemHm";
+    return "vstemhm";
   case ots::kRCurveLine:
-    return "RCurveLine";
+    return "rcurveline";
   case ots::kRLineCurve:
-    return "RLineCurve";
+    return "rlinecurve";
   case ots::kVVCurveTo:
     return "VVCurveTo";
   case ots::kHHCurveTo:
-    return "HHCurveTo";
+    return "hhcurveto";
   case ots::kCallGSubr:
-    return "CallGSubr";
+    return "callgsubr";
   case ots::kVHCurveTo:
-    return "VHCurveTo";
+    return "vhcurveto";
   case ots::kHVCurveTo:
     return "HVCurveTo";
   case ots::kDotSection:
-    return "DotSection";
+    return "dotsection";
   case ots::kAnd:
-    return "And";
+    return "and";
   case ots::kOr:
-    return "Or";
+    return "or";
   case ots::kNot:
-    return "Not";
+    return "not";
   case ots::kAbs:
-    return "Abs";
+    return "abs";
   case ots::kAdd:
-    return "Add";
+    return "add";
   case ots::kSub:
-    return "Sub";
+    return "sub";
   case ots::kDiv:
-    return "Div";
+    return "div";
   case ots::kNeg:
-    return "Neg";
+    return "neg";
   case ots::kEq:
-    return "Eq";
+    return "eq";
   case ots::kDrop:
-    return "Drop";
+    return "drop";
   case ots::kPut:
-    return "Put";
+    return "put";
   case ots::kGet:
-    return "Get";
+    return "get";
   case ots::kIfElse:
-    return "IfElse";
+    return "ifelse";
   case ots::kRandom:
-    return "Random";
+    return "random";
   case ots::kMul:
-    return "Mul";
+    return "mul";
   case ots::kSqrt:
-    return "Sqrt";
+    return "sqrt";
   case ots::kDup:
-    return "Dup";
+    return "dup";
   case ots::kExch:
-    return "Exch";
+    return "exch";
   case ots::kIndex:
-    return "Index";
+    return "index";
   case ots::kRoll:
-    return "Roll";
+    return "roll";
   case ots::kHFlex:
-    return "HFlex";
+    return "hflex";
   case ots::kFlex:
-    return "Flex";
+    return "flex";
   case ots::kHFlex1:
-    return "HFlex1";
+    return "hflex1";
   case ots::kFlex1:
-    return "Flex1";
+    return "flex1";
   }
 
   return "UNKNOWN";
 }
 #endif
 
 // Read one or more bytes from the |char_string| buffer and stores the number
 // read on |out_number|. If the number read is an operator (ex 'vstem'), sets
 // true on |out_is_operator|. Returns true if the function read a number.
-bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
-                                       int32_t *out_number,
-                                       bool *out_is_operator) {
+bool ReadNextNumberFromCharString(ots::Buffer *char_string,
+                                  int32_t *out_number,
+                                  bool *out_is_operator) {
   uint8_t v = 0;
   if (!char_string->ReadU8(&v)) {
     return OTS_FAILURE();
   }
   *out_is_operator = false;
 
   // The conversion algorithm is described in Adobe Technical Note #5177, page
   // 13, Table 1.
@@ -169,17 +181,17 @@ bool ReadNextNumberFromType2CharString(o
     if (!char_string->ReadU8(&v)) {
       return OTS_FAILURE();
     }
     result += v;
     *out_number = result;
     *out_is_operator = true;
   } else if (v <= 27) {
     // Special handling for v==19 and v==20 are implemented in
-    // ExecuteType2CharStringOperator().
+    // ExecuteCharStringOperator().
     *out_number = v;
     *out_is_operator = true;
   } else if (v == 28) {
     if (!char_string->ReadU8(&v)) {
       return OTS_FAILURE();
     }
     uint16_t result = (v << 8);
     if (!char_string->ReadU8(&v)) {
@@ -216,33 +228,73 @@ bool ReadNextNumberFromType2CharString(o
     *out_number = dummy_result;
   } else {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
+bool ValidCFF2Operator(int32_t op) {
+  switch (op) {
+  case ots::kReturn:
+  case ots::kEndChar:
+  case ots::kAbs:
+  case ots::kAdd:
+  case ots::kSub:
+  case ots::kDiv:
+  case ots::kNeg:
+  case ots::kRandom:
+  case ots::kMul:
+  case ots::kSqrt:
+  case ots::kDrop:
+  case ots::kExch:
+  case ots::kIndex:
+  case ots::kRoll:
+  case ots::kDup:
+  case ots::kPut:
+  case ots::kGet:
+  case ots::kDotSection:
+  case ots::kAnd:
+  case ots::kOr:
+  case ots::kNot:
+  case ots::kEq:
+  case ots::kIfElse:
+    return false;
+  }
+
+  return true;
+}
+
 // Executes |op| and updates |argument_stack|. Returns true if the execution
 // succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
-// calls ExecuteType2CharString() function. The arguments other than |op| and
+// calls ExecuteCharString() function. The arguments other than |op| and
 // |argument_stack| are passed for that reason.
-bool ExecuteType2CharStringOperator(ots::Font *font,
-                                    int32_t op,
-                                    size_t call_depth,
-                                    const ots::CFFIndex& global_subrs_index,
-                                    const ots::CFFIndex& local_subrs_index,
-                                    ots::Buffer *cff_table,
-                                    ots::Buffer *char_string,
-                                    std::stack<int32_t> *argument_stack,
-                                    bool *out_found_endchar,
-                                    bool *in_out_found_width,
-                                    size_t *in_out_num_stems) {
+bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff,
+                               int32_t op,
+                               size_t call_depth,
+                               const ots::CFFIndex& global_subrs_index,
+                               const ots::CFFIndex& local_subrs_index,
+                               ots::Buffer *cff_table,
+                               ots::Buffer *char_string,
+                               std::stack<int32_t> *argument_stack,
+                               bool *out_found_endchar,
+                               bool *in_out_found_width,
+                               size_t *in_out_num_stems,
+                               bool *in_out_have_blend,
+                               bool *in_out_have_visindex,
+                               int32_t *in_out_vsindex,
+                               bool cff2) {
+  ots::Font* font = cff.GetFont();
   const size_t stack_size = argument_stack->size();
 
+  if (cff2 && !ValidCFF2Operator(op)) {
+    return OTS_FAILURE();
+  }
+
   switch (op) {
   case ots::kCallSubr:
   case ots::kCallGSubr: {
     const ots::CFFIndex& subrs_index =
         (op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
 
     if (stack_size < 1) {
       return OTS_FAILURE();
@@ -285,36 +337,82 @@ bool ExecuteType2CharStringOperator(ots:
     }
     const size_t offset = subrs_index.offsets[subr_number];
     cff_table->set_offset(offset);
     if (!cff_table->Skip(length)) {
       return OTS_FAILURE();
     }
     ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
 
-    return ExecuteType2CharString(font,
-                                  call_depth + 1,
-                                  global_subrs_index,
-                                  local_subrs_index,
-                                  cff_table,
-                                  &char_string_to_jump,
-                                  argument_stack,
-                                  out_found_endchar,
-                                  in_out_found_width,
-                                  in_out_num_stems);
+    return ExecuteCharString(cff,
+                             call_depth + 1,
+                             global_subrs_index,
+                             local_subrs_index,
+                             cff_table,
+                             &char_string_to_jump,
+                             argument_stack,
+                             out_found_endchar,
+                             in_out_found_width,
+                             in_out_num_stems,
+                             cff2);
   }
 
   case ots::kReturn:
     return true;
 
   case ots::kEndChar:
     *out_found_endchar = true;
     *in_out_found_width = true;  // just in case.
     return true;
 
+  case ots::kVSIndex: {
+    if (!cff2) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 1) {
+      return OTS_FAILURE();
+    }
+    if (*in_out_have_blend || *in_out_have_visindex) {
+      return OTS_FAILURE();
+    }
+    if (argument_stack->top() >= cff.region_index_count.size()) {
+      return OTS_FAILURE();
+    }
+    *in_out_have_visindex = true;
+    *in_out_vsindex = argument_stack->top();
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+  }
+
+  case ots::kBlend: {
+    if (!cff2) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    if (*in_out_vsindex >= cff.region_index_count.size()) {
+      return OTS_FAILURE();
+    }
+    uint16_t k = cff.region_index_count.at(*in_out_vsindex);
+    uint16_t n = argument_stack->top();
+    if (stack_size < n * (k + 1) + 1) {
+      return OTS_FAILURE();
+    }
+
+    // Keep the 1st n operands on the stack for the next operator to use and
+    // pop the rest. There can be multiple consecutive blend operator, so this
+    // makes sure the operands of all of them are kept on the stack.
+    while (argument_stack->size() > stack_size - ((n * k) + 1))
+      argument_stack->pop();
+    *in_out_have_blend = true;
+    return true;
+  }
+
   case ots::kHStem:
   case ots::kVStem:
   case ots::kHStemHm:
   case ots::kVStemHm: {
     bool successful = false;
     if (stack_size < 2) {
       return OTS_FAILURE();
     }
@@ -644,17 +742,17 @@ bool ExecuteType2CharStringOperator(ots:
 
   case ots::kDup:
     if (stack_size < 1) {
       return OTS_FAILURE();
     }
     argument_stack->pop();
     argument_stack->push(dummy_result);
     argument_stack->push(dummy_result);
-    if (argument_stack->size() > kMaxArgumentStack) {
+    if (ArgumentStackOverflows(argument_stack, cff2)) {
       return OTS_FAILURE();
     }
     // TODO(yusukes): Implement this. We should push a real value for all
     // arithmetic and conditional operations.
     return true;
 
   case ots::kExch:
     if (stack_size < 2) {
@@ -724,143 +822,150 @@ bool ExecuteType2CharStringOperator(ots:
 // cff_table: A whole CFF table which contains all global and local subroutines.
 // char_string: A charstring we'll execute. |char_string| can be a main routine
 //              in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr.
 // argument_stack: The stack which an operator in |char_string| operates.
 // out_found_endchar: true is set if |char_string| contains 'endchar'.
 // in_out_found_width: true is set if |char_string| contains 'width' byte (which
 //                     is 0 or 1 byte.)
 // in_out_num_stems: total number of hstems and vstems processed so far.
-bool ExecuteType2CharString(ots::Font *font,
-                            size_t call_depth,
-                            const ots::CFFIndex& global_subrs_index,
-                            const ots::CFFIndex& local_subrs_index,
-                            ots::Buffer *cff_table,
-                            ots::Buffer *char_string,
-                            std::stack<int32_t> *argument_stack,
-                            bool *out_found_endchar,
-                            bool *in_out_found_width,
-                            size_t *in_out_num_stems) {
+bool ExecuteCharString(ots::OpenTypeCFF& cff,
+                       size_t call_depth,
+                       const ots::CFFIndex& global_subrs_index,
+                       const ots::CFFIndex& local_subrs_index,
+                       ots::Buffer *cff_table,
+                       ots::Buffer *char_string,
+                       std::stack<int32_t> *argument_stack,
+                       bool *out_found_endchar,
+                       bool *in_out_found_width,
+                       size_t *in_out_num_stems,
+                       bool cff2) {
   if (call_depth > kMaxSubrNesting) {
     return OTS_FAILURE();
   }
   *out_found_endchar = false;
 
+  bool in_out_have_blend = false, in_out_have_visindex = false;
+  int32_t in_out_vsindex = 0;
   const size_t length = char_string->length();
   while (char_string->offset() < length) {
     int32_t operator_or_operand = 0;
     bool is_operator = false;
-    if (!ReadNextNumberFromType2CharString(char_string,
+    if (!ReadNextNumberFromCharString(char_string,
                                            &operator_or_operand,
                                            &is_operator)) {
       return OTS_FAILURE();
     }
 
 #ifdef DUMP_T2CHARSTRING
     /*
       You can dump all operators and operands (except mask bytes for hintmask
       and cntrmask) by the following code:
     */
 
       if (!is_operator) {
-        std::fprintf(stderr, "#%d# ", operator_or_operand);
+        std::fprintf(stderr, "%d ", operator_or_operand);
       } else {
-        std::fprintf(stderr, "#%s#\n",
-           Type2CharStringOperatorToString(
-               ots::Type2CharStringOperator(operator_or_operand))
+        std::fprintf(stderr, "%s\n",
+           CharStringOperatorToString(
+               ots::CharStringOperator(operator_or_operand))
            );
       }
 #endif
 
     if (!is_operator) {
       argument_stack->push(operator_or_operand);
-      if (argument_stack->size() > kMaxArgumentStack) {
+      if (ArgumentStackOverflows(argument_stack, cff2)) {
         return OTS_FAILURE();
       }
       continue;
     }
 
     // An operator is found. Execute it.
-    if (!ExecuteType2CharStringOperator(font,
-                                        operator_or_operand,
-                                        call_depth,
-                                        global_subrs_index,
-                                        local_subrs_index,
-                                        cff_table,
-                                        char_string,
-                                        argument_stack,
-                                        out_found_endchar,
-                                        in_out_found_width,
-                                        in_out_num_stems)) {
+    if (!ExecuteCharStringOperator(cff,
+                                   operator_or_operand,
+                                   call_depth,
+                                   global_subrs_index,
+                                   local_subrs_index,
+                                   cff_table,
+                                   char_string,
+                                   argument_stack,
+                                   out_found_endchar,
+                                   in_out_found_width,
+                                   in_out_num_stems,
+                                   &in_out_have_blend,
+                                   &in_out_have_visindex,
+                                   &in_out_vsindex,
+                                   cff2)) {
       return OTS_FAILURE();
     }
     if (*out_found_endchar) {
       return true;
     }
     if (operator_or_operand == ots::kReturn) {
       return true;
     }
   }
 
   // No endchar operator is found.
+  if (cff2)
+    return true;
   return OTS_FAILURE();
 }
 
 // Selects a set of subroutings for |glyph_index| from |cff| and sets it on
 // |out_local_subrs_to_use|. Returns true on success.
-bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
-                     const std::vector<ots::CFFIndex *> &local_subrs_per_font,
-                     const ots::CFFIndex *local_subrs,
+bool SelectLocalSubr(const ots::OpenTypeCFF& cff,
                      uint16_t glyph_index,  // 0-origin
                      const ots::CFFIndex **out_local_subrs_to_use) {
+  bool cff2 = (cff.major == 2);
   *out_local_subrs_to_use = NULL;
 
   // First, find local subrs from |local_subrs_per_font|.
-  if ((fd_select.size() > 0) &&
-      (!local_subrs_per_font.empty())) {
+  if ((cff.fd_select.size() > 0) &&
+      (!cff.local_subrs_per_font.empty())) {
     // Look up FDArray index for the glyph.
-    std::map<uint16_t, uint8_t>::const_iterator iter =
-        fd_select.find(glyph_index);
-    if (iter == fd_select.end()) {
+    const auto& iter = cff.fd_select.find(glyph_index);
+    if (iter == cff.fd_select.end()) {
       return OTS_FAILURE();
     }
-    const uint8_t fd_index = iter->second;
-    if (fd_index >= local_subrs_per_font.size()) {
+    const auto fd_index = iter->second;
+    if (fd_index >= cff.local_subrs_per_font.size()) {
       return OTS_FAILURE();
     }
-    *out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
-  } else if (local_subrs) {
+    *out_local_subrs_to_use = cff.local_subrs_per_font.at(fd_index);
+  } else if (cff.local_subrs) {
     // Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
     // entries. If The font has a local subrs index associated with the Top
     // DICT (not FDArrays), use it.
-    *out_local_subrs_to_use = local_subrs;
+    *out_local_subrs_to_use = cff.local_subrs;
+  } else if (cff2 && cff.local_subrs_per_font.size() == 1) {
+    *out_local_subrs_to_use = cff.local_subrs_per_font.at(0);
   } else {
     // Just return NULL.
     *out_local_subrs_to_use = NULL;
   }
 
   return true;
 }
 
 }  // namespace
 
 namespace ots {
 
-bool ValidateType2CharStringIndex(
-    ots::Font *font,
-    const CFFIndex& char_strings_index,
+bool ValidateCFFCharStrings(
+    ots::OpenTypeCFF& cff,
     const CFFIndex& global_subrs_index,
-    const std::map<uint16_t, uint8_t> &fd_select,
-    const std::vector<CFFIndex *> &local_subrs_per_font,
-    const CFFIndex *local_subrs,
     Buffer* cff_table) {
+  const CFFIndex& char_strings_index = *(cff.charstrings_index);
   if (char_strings_index.offsets.size() == 0) {
     return OTS_FAILURE();  // no charstring.
   }
 
+  bool cff2 = (cff.major == 2);
   // For each glyph, validate the corresponding charstring.
   for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
     // Prepare a Buffer object, |char_string|, which contains the charstring
     // for the |i|-th glyph.
     const size_t length =
       char_strings_index.offsets[i] - char_strings_index.offsets[i - 1];
     if (length > kMaxCharStringLength) {
       return OTS_FAILURE();
@@ -870,42 +975,43 @@ bool ValidateType2CharStringIndex(
     if (!cff_table->Skip(length)) {
       return OTS_FAILURE();
     }
     Buffer char_string(cff_table->buffer() + offset, length);
 
     // Get a local subrs for the glyph.
     const unsigned glyph_index = i - 1;  // index in the map is 0-origin.
     const CFFIndex *local_subrs_to_use = NULL;
-    if (!SelectLocalSubr(fd_select,
-                         local_subrs_per_font,
-                         local_subrs,
+    if (!SelectLocalSubr(cff,
                          glyph_index,
                          &local_subrs_to_use)) {
       return OTS_FAILURE();
     }
     // If |local_subrs_to_use| is still NULL, use an empty one.
     CFFIndex default_empty_subrs;
     if (!local_subrs_to_use){
       local_subrs_to_use = &default_empty_subrs;
     }
 
     // Check a charstring for the |i|-th glyph.
     std::stack<int32_t> argument_stack;
     bool found_endchar = false;
-    bool found_width = false;
+    // CFF2 CharString has no value for width, so we start with true here to
+    // error out if width is found.
+    bool found_width = cff2;
     size_t num_stems = 0;
-    if (!ExecuteType2CharString(font,
-                                0 /* initial call_depth is zero */,
-                                global_subrs_index, *local_subrs_to_use,
-                                cff_table, &char_string, &argument_stack,
-                                &found_endchar, &found_width, &num_stems)) {
+    if (!ExecuteCharString(cff,
+                           0 /* initial call_depth is zero */,
+                           global_subrs_index, *local_subrs_to_use,
+                           cff_table, &char_string, &argument_stack,
+                           &found_endchar, &found_width, &num_stems,
+                           cff2)) {
       return OTS_FAILURE();
     }
-    if (!found_endchar) {
+    if (!cff2 && !found_endchar) {
       return OTS_FAILURE();
     }
   }
   return true;
 }
 
 }  // namespace ots
 
rename from gfx/ots/src/cff_type2_charstring.h
rename to gfx/ots/src/cff_charstring.h
--- a/gfx/ots/src/cff_type2_charstring.h
+++ b/gfx/ots/src/cff_charstring.h
@@ -8,16 +8,19 @@
 #include "cff.h"
 #include "ots.h"
 
 #include <map>
 #include <vector>
 
 namespace ots {
 
+const size_t kMaxCFF1ArgumentStack = 48;
+const size_t kMaxCFF2ArgumentStack = 513;
+
 // Validates all charstrings in |char_strings_index|. Charstring is a small
 // language for font hinting defined in Adobe Technical Note #5177.
 // http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
 //
 // The validation will fail if one of the following conditions is met:
 //  1. The code uses more than 48 values of argument stack.
 //  2. The code uses deeply nested subroutine calls (more than 10 levels.)
 //  3. The code passes invalid number of operands to an operator.
@@ -29,37 +32,36 @@ namespace ots {
 //  global_subrs_index: Global subroutines which could be called by a charstring
 //                      in |char_strings_index|.
 //  fd_select: A map from glyph # to font #.
 //  local_subrs_per_font: A list of Local Subrs associated with FDArrays. Can be
 //                        empty.
 //  local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
 //  cff_table: A buffer which contains actual byte code of charstring, global
 //             subroutines and local subroutines.
-bool ValidateType2CharStringIndex(
-    Font *font,
-    const CFFIndex &char_strings_index,
+bool ValidateCFFCharStrings(
+    OpenTypeCFF& cff,
     const CFFIndex &global_subrs_index,
-    const std::map<uint16_t, uint8_t> &fd_select,
-    const std::vector<CFFIndex *> &local_subrs_per_font,
-    const CFFIndex *local_subrs,
     Buffer *cff_table);
 
 // The list of Operators. See Appendix. A in Adobe Technical Note #5177.
-enum Type2CharStringOperator {
+// and https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr
+enum CharStringOperator {
   kHStem = 1,
   kVStem = 3,
   kVMoveTo = 4,
   kRLineTo = 5,
   kHLineTo = 6,
   kVLineTo = 7,
   kRRCurveTo = 8,
   kCallSubr = 10,
   kReturn = 11,
   kEndChar = 14,
+  kVSIndex = 15,
+  kBlend = 16,
   kHStemHm = 18,
   kHintMask = 19,
   kCntrMask = 20,
   kRMoveTo = 21,
   kHMoveTo = 22,
   kVStemHm = 23,
   kRCurveLine = 24,
   kRLineCurve = 25,
--- a/gfx/ots/src/gdef.cc
+++ b/gfx/ots/src/gdef.cc
@@ -6,20 +6,17 @@
 
 #include <limits>
 #include <vector>
 
 #include "gpos.h"
 #include "gsub.h"
 #include "layout.h"
 #include "maxp.h"
-
-#ifdef OTS_VARIATIONS
 #include "variations.h"
-#endif
 
 // GDEF - The Glyph Definition Table
 // http://www.microsoft.com/typography/otspec/gdef.htm
 
 namespace {
 
 // The maximum class value in the glyph class definision table.
 const uint16_t kMaxGlyphClassDefValue = 4;
@@ -340,22 +337,20 @@ bool OpenTypeGDEF::Parse(const uint8_t *
     }
   }
 
   if (item_var_store_offset) {
     if (item_var_store_offset >= length ||
         item_var_store_offset < gdef_header_end) {
       return Error("invalid offset to item variation store");
     }
-#ifdef OTS_VARIATIONS
     if (!ParseItemVariationStore(GetFont(), data + item_var_store_offset,
                                  length - item_var_store_offset)) {
       return Error("Invalid item variation store");
     }
-#endif
   }
 
   this->m_data = data;
   this->m_length = length;
   return true;
 }
 
 bool OpenTypeGDEF::Serialize(OTSStream *out) {
--- a/gfx/ots/src/glat.cc
+++ b/gfx/ots/src/glat.cc
@@ -195,17 +195,29 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
   }
   switch ((this->compHead & SCHEME) >> 27) {
     case 0:  // uncompressed
       break;
     case 1: {  // lz4
       if (prevent_decompression) {
         return DropGraphite("Illegal nested compression");
       }
-      std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
+      size_t decompressed_size = this->compHead & FULL_SIZE;
+      if (decompressed_size < length) {
+        return DropGraphite("Decompressed size is less than compressed size");
+      }
+      if (decompressed_size == 0) {
+        return DropGraphite("Decompressed size is set to 0");
+      }
+      // decompressed table must be <= 30MB
+      if (decompressed_size > 30 * 1024 * 1024) {
+        return DropGraphite("Decompressed size exceeds 30MB: %gMB",
+                            decompressed_size / (1024.0 * 1024.0));
+      }
+      std::vector<uint8_t> decompressed(decompressed_size);
       size_t outputSize = 0;
       bool ret = mozilla::Compression::LZ4::decompressPartial(
           reinterpret_cast<const char*>(data + table.offset()),
           table.remaining(),  // input buffer size (input size + padding)
           reinterpret_cast<char*>(decompressed.data()),
           decompressed.size(),  // target output size
           &outputSize);  // return output size
       if (!ret || outputSize != decompressed.size()) {
--- a/gfx/ots/src/hvar.cc
+++ b/gfx/ots/src/hvar.cc
@@ -1,18 +1,16 @@
 // Copyright (c) 2018 The OTS 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 "hvar.h"
 
 #include "variations.h"
 
-#define TABLE_NAME "HVAR"
-
 namespace ots {
 
 // -----------------------------------------------------------------------------
 // OpenTypeHVAR
 // -----------------------------------------------------------------------------
 
 bool OpenTypeHVAR::Parse(const uint8_t* data, size_t length) {
   Buffer table(data, length);
@@ -80,10 +78,8 @@ bool OpenTypeHVAR::Serialize(OTSStream* 
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write HVAR table");
   }
 
   return true;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/layout.cc
+++ b/gfx/ots/src/layout.cc
@@ -2,19 +2,17 @@
 // 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>
 
-#ifdef OTS_VARIATIONS
 #include "fvar.h"
-#endif
 #include "gdef.h"
 
 // OpenType Layout Common Table Formats
 // http://www.microsoft.com/typography/otspec/chapter2.htm
 
 #define TABLE_NAME "Layout" // XXX: use individual table names
 
 namespace {
@@ -1525,21 +1523,19 @@ bool ParseConditionTable(const Font *fon
   int16_t filter_range_min_value = 0;
   int16_t filter_range_max_value = 0;
   if (!subtable.ReadU16(&axis_index) ||
       !subtable.ReadS16(&filter_range_min_value) ||
       !subtable.ReadS16(&filter_range_max_value)) {
     return OTS_FAILURE_MSG("Failed to read condition table (format 1)");
   }
 
-#ifdef OTS_VARIATIONS // we can't check this if we're not parsing variation tables
   if (axis_index >= axis_count) {
     return OTS_FAILURE_MSG("Axis index out of range in condition");
   }
-#endif
 
   // Check min/max values are within range -1.0 .. 1.0 and properly ordered
   if (filter_range_min_value < -0x4000 || // -1.0 in F2DOT14 format
       filter_range_max_value > 0x4000 || // +1.0 in F2DOT14 format
       filter_range_min_value > filter_range_max_value) {
     return OTS_FAILURE_MSG("Invalid filter range in condition");
   }
 
@@ -1624,25 +1620,21 @@ bool ParseFeatureVariationsTable(const F
   uint32_t feature_variation_record_count = 0;
 
   if (!subtable.ReadU16(&version_major) ||
       !subtable.ReadU16(&version_minor) ||
       !subtable.ReadU32(&feature_variation_record_count)) {
     return OTS_FAILURE_MSG("Failed to read feature variations table header");
   }
 
-#ifdef OTS_VARIATIONS
   OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
   if (!fvar) {
     return OTS_FAILURE_MSG("Not a variation font");
   }
   const uint16_t axis_count = fvar->AxisCount();
-#else
-  const uint16_t axis_count = 0;
-#endif
 
   const size_t kEndOfFeatureVariationRecords =
     2 * sizeof(uint16_t) + sizeof(uint32_t) +
     feature_variation_record_count * 2 * sizeof(uint32_t);
 
   for (uint32_t i = 0; i < feature_variation_record_count; i++) {
     uint32_t condition_set_offset = 0;
     uint32_t feature_table_substitution_offset = 0;
--- a/gfx/ots/src/moz.build
+++ b/gfx/ots/src/moz.build
@@ -4,32 +4,28 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     '../include/opentype-sanitiser.h',
     '../include/ots-memory-stream.h',
 ]
 
-SOURCES += [
-    # needs to be separate because gpos.cc also defines kMaxClassDefValue
-    'gdef.cc',
-]
-
 UNIFIED_SOURCES += [
     'avar.cc',
     'cff.cc',
-    'cff_type2_charstring.cc',
+    'cff_charstring.cc',
     'cmap.cc',
     'cvar.cc',
     'cvt.cc',
     'feat.cc',
     'fpgm.cc',
     'fvar.cc',
     'gasp.cc',
+    'gdef.cc',
     'glat.cc',
     'gloc.cc',
     'glyf.cc',
     'gpos.cc',
     'gsub.cc',
     'gvar.cc',
     'hdmx.cc',
     'head.cc',
--- a/gfx/ots/src/mvar.cc
+++ b/gfx/ots/src/mvar.cc
@@ -1,18 +1,16 @@
 // Copyright (c) 2018 The OTS 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 "mvar.h"
 
 #include "variations.h"
 
-#define TABLE_NAME "MVAR"
-
 namespace ots {
 
 // -----------------------------------------------------------------------------
 // OpenTypeMVAR
 // -----------------------------------------------------------------------------
 
 bool OpenTypeMVAR::Parse(const uint8_t* data, size_t length) {
   Buffer table(data, length);
@@ -100,10 +98,8 @@ bool OpenTypeMVAR::Serialize(OTSStream* 
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write MVAR table");
   }
 
   return true;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/name.cc
+++ b/gfx/ots/src/name.cc
@@ -206,27 +206,26 @@ bool OpenTypeNAME::Parse(const uint8_t* 
     "1.000",
     "OTS-derived-font"
   };
 
   // scan the names to check whether the required "standard" ones are present;
   // if not, we'll add our fixed versions here
   bool mac_name[kStdNameCount] = { 0 };
   bool win_name[kStdNameCount] = { 0 };
-  for (std::vector<NameRecord>::iterator name_iter = this->names.begin();
-       name_iter != this->names.end(); ++name_iter) {
-    const uint16_t id = name_iter->name_id;
+  for (const auto& name : this->names) {
+    const uint16_t id = name.name_id;
     if (id >= kStdNameCount || kStdNames[id] == NULL) {
       continue;
     }
-    if (name_iter->platform_id == 1) {
+    if (name.platform_id == 1) {
       mac_name[id] = true;
       continue;
     }
-    if (name_iter->platform_id == 3) {
+    if (name.platform_id == 3) {
       win_name[id] = true;
       continue;
     }
   }
 
   for (uint16_t i = 0; i < kStdNameCount; ++i) {
     if (kStdNames[i] == NULL) {
       continue;
@@ -269,19 +268,17 @@ bool OpenTypeNAME::Serialize(OTSStream* 
   }
   if (!out->WriteU16(format) ||
       !out->WriteU16(name_count) ||
       !out->WriteU16(static_cast<uint16_t>(string_offset))) {
     return Error("Failed to write name header");
   }
 
   std::string string_data;
-  for (std::vector<NameRecord>::const_iterator name_iter = this->names.begin();
-       name_iter != this->names.end(); ++name_iter) {
-    const NameRecord& rec = *name_iter;
+  for (const auto& rec : this->names) {
     if (string_data.size() + rec.text.size() >
             std::numeric_limits<uint16_t>::max() ||
         !out->WriteU16(rec.platform_id) ||
         !out->WriteU16(rec.encoding_id) ||
         !out->WriteU16(rec.language_id) ||
         !out->WriteU16(rec.name_id) ||
         !out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
         !out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
@@ -289,26 +286,24 @@ bool OpenTypeNAME::Serialize(OTSStream* 
     }
     string_data.append(rec.text);
   }
 
   if (format == 1) {
     if (!out->WriteU16(lang_tag_count)) {
       return Error("Faile to write langTagCount");
     }
-    for (std::vector<std::string>::const_iterator tag_iter =
-             this->lang_tags.begin();
-         tag_iter != this->lang_tags.end(); ++tag_iter) {
-      if (string_data.size() + tag_iter->size() >
+    for (const auto& tag : this->lang_tags) {
+      if (string_data.size() + tag.size() >
               std::numeric_limits<uint16_t>::max() ||
-          !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
+          !out->WriteU16(static_cast<uint16_t>(tag.size())) ||
           !out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
         return Error("Failed to write langTagRecord");
       }
-      string_data.append(*tag_iter);
+      string_data.append(tag);
     }
   }
 
   if (!out->Write(string_data.data(), string_data.size())) {
     return Error("Faile to write string data");
   }
 
   return true;
--- a/gfx/ots/src/ots.cc
+++ b/gfx/ots/src/ots.cc
@@ -14,73 +14,69 @@
 #include <map>
 #include <vector>
 
 #include <woff2/decode.h>
 
 // The OpenType Font File
 // http://www.microsoft.com/typography/otspec/otff.htm
 
+#include "avar.h"
 #include "cff.h"
 #include "cmap.h"
+#include "cvar.h"
 #include "cvt.h"
 #include "fpgm.h"
+#include "fvar.h"
 #include "gasp.h"
 #include "gdef.h"
 #include "glyf.h"
 #include "gpos.h"
 #include "gsub.h"
+#include "gvar.h"
 #include "hdmx.h"
 #include "head.h"
 #include "hhea.h"
 #include "hmtx.h"
+#include "hvar.h"
 #include "kern.h"
 #include "loca.h"
 #include "ltsh.h"
 #include "math_.h"
 #include "maxp.h"
+#include "mvar.h"
 #include "name.h"
 #include "os2.h"
 #include "ots.h"
 #include "post.h"
 #include "prep.h"
+#include "stat.h"
 #include "vdmx.h"
 #include "vhea.h"
 #include "vmtx.h"
 #include "vorg.h"
+#include "vvar.h"
 
 // Graphite tables
 #ifdef OTS_GRAPHITE
 #include "feat.h"
 #include "glat.h"
 #include "gloc.h"
 #include "sile.h"
 #include "silf.h"
 #include "sill.h"
 #endif
 
-#ifdef OTS_VARIATIONS
-#include "avar.h"
-#include "cvar.h"
-#include "fvar.h"
-#include "gvar.h"
-#include "hvar.h"
-#include "mvar.h"
-#include "stat.h"
-#include "vvar.h"
-#endif
-
 namespace ots {
 
 struct Arena {
  public:
   ~Arena() {
-    for (std::vector<uint8_t*>::iterator
-         i = hunks_.begin(); i != hunks_.end(); ++i) {
-      delete[] *i;
+    for (auto& hunk : hunks_) {
+      delete[] hunk;
     }
   }
 
   uint8_t* Allocate(size_t length) {
     uint8_t* p = new uint8_t[length];
     hunks_.push_back(p);
     return p;
   }
@@ -134,26 +130,25 @@ const struct {
   { OTS_TAG_CVT,  false },
   { OTS_TAG_FPGM, false },
   { OTS_TAG_PREP, false },
   { OTS_TAG_LTSH, false },
   { OTS_TAG_VORG, false },
   { OTS_TAG_KERN, false },
   // We need to parse fvar table before other tables that may need to know
   // the number of variation axes (if any)
-#ifdef OTS_VARIATIONS
   { OTS_TAG_FVAR, false },
   { OTS_TAG_AVAR, false },
   { OTS_TAG_CVAR, false },
   { OTS_TAG_GVAR, false },
   { OTS_TAG_HVAR, false },
   { OTS_TAG_MVAR, false },
   { OTS_TAG_STAT, false },
   { OTS_TAG_VVAR, false },
-#endif
+  { OTS_TAG_CFF2, false },
   // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
   // because they could refer GDEF table.
   { OTS_TAG_GDEF, false },
   { OTS_TAG_GPOS, false },
   { OTS_TAG_GSUB, false },
   { OTS_TAG_VHEA, false },
   { OTS_TAG_VMTX, false },
   { OTS_TAG_MATH, false },
@@ -704,17 +699,17 @@ bool ProcessGeneric(ots::FontFile *heade
   for (const auto &table_entry : tables) {
     if (!font->GetTable(table_entry.tag)) {
       if (!font->ParseTable(table_entry, data, arena)) {
         return OTS_FAILURE_MSG_TAG("Failed to parse table", table_entry.tag);
       }
     }
   }
 
-  if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG('C', 'F', 'F', '2'))) {
+  if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG_CFF2)) {
     // font with PostScript glyph
     if (font->version != OTS_TAG('O','T','T','O')) {
       return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
     }
     if (font->GetTable(OTS_TAG_GLYF) || font->GetTable(OTS_TAG_LOCA)) {
       // mixing outline formats is not recommended
       return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
     }
@@ -880,61 +875,60 @@ bool Font::ParseTable(const TableEntry& 
 
   Table *table = NULL;
   bool ret = false;
 
   if (action == TABLE_ACTION_PASSTHRU) {
     table = new TablePassthru(this, tag);
   } else {
     switch (tag) {
+      case OTS_TAG_AVAR: table = new OpenTypeAVAR(this, tag); break;
       case OTS_TAG_CFF:  table = new OpenTypeCFF(this,  tag); break;
+      case OTS_TAG_CFF2: table = new OpenTypeCFF2(this, tag); break;
       case OTS_TAG_CMAP: table = new OpenTypeCMAP(this, tag); break;
+      case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
       case OTS_TAG_CVT:  table = new OpenTypeCVT(this,  tag); break;
       case OTS_TAG_FPGM: table = new OpenTypeFPGM(this, tag); break;
+      case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
       case OTS_TAG_GASP: table = new OpenTypeGASP(this, tag); break;
       case OTS_TAG_GDEF: table = new OpenTypeGDEF(this, tag); break;
       case OTS_TAG_GLYF: table = new OpenTypeGLYF(this, tag); break;
       case OTS_TAG_GPOS: table = new OpenTypeGPOS(this, tag); break;
       case OTS_TAG_GSUB: table = new OpenTypeGSUB(this, tag); break;
+      case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
       case OTS_TAG_HDMX: table = new OpenTypeHDMX(this, tag); break;
       case OTS_TAG_HEAD: table = new OpenTypeHEAD(this, tag); break;
       case OTS_TAG_HHEA: table = new OpenTypeHHEA(this, tag); break;
       case OTS_TAG_HMTX: table = new OpenTypeHMTX(this, tag); break;
+      case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
       case OTS_TAG_KERN: table = new OpenTypeKERN(this, tag); break;
       case OTS_TAG_LOCA: table = new OpenTypeLOCA(this, tag); break;
       case OTS_TAG_LTSH: table = new OpenTypeLTSH(this, tag); break;
       case OTS_TAG_MATH: table = new OpenTypeMATH(this, tag); break;
       case OTS_TAG_MAXP: table = new OpenTypeMAXP(this, tag); break;
+      case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
       case OTS_TAG_NAME: table = new OpenTypeNAME(this, tag); break;
       case OTS_TAG_OS2:  table = new OpenTypeOS2(this,  tag); break;
       case OTS_TAG_POST: table = new OpenTypePOST(this, tag); break;
       case OTS_TAG_PREP: table = new OpenTypePREP(this, tag); break;
+      case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
       case OTS_TAG_VDMX: table = new OpenTypeVDMX(this, tag); break;
-      case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
       case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
       case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
+      case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
+      case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
       // Graphite tables
 #ifdef OTS_GRAPHITE
       case OTS_TAG_FEAT: table = new OpenTypeFEAT(this, tag); break;
       case OTS_TAG_GLAT: table = new OpenTypeGLAT(this, tag); break;
       case OTS_TAG_GLOC: table = new OpenTypeGLOC(this, tag); break;
       case OTS_TAG_SILE: table = new OpenTypeSILE(this, tag); break;
       case OTS_TAG_SILF: table = new OpenTypeSILF(this, tag); break;
       case OTS_TAG_SILL: table = new OpenTypeSILL(this, tag); break;
 #endif
-#ifdef OTS_VARIATIONS
-      case OTS_TAG_AVAR: table = new OpenTypeAVAR(this, tag); break;
-      case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
-      case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
-      case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
-      case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
-      case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
-      case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
-      case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
-#endif
       default: break;
     }
   }
 
   if (table) {
     const uint8_t* table_data;
     size_t table_length;
 
--- a/gfx/ots/src/ots.h
+++ b/gfx/ots/src/ots.h
@@ -185,16 +185,17 @@ template<typename T> T Round2(T value) {
 }
 
 // Check that a tag consists entirely of printable ASCII characters
 bool CheckTag(uint32_t tag_value);
 
 bool IsValidVersionTag(uint32_t tag);
 
 #define OTS_TAG_CFF  OTS_TAG('C','F','F',' ')
+#define OTS_TAG_CFF2 OTS_TAG('C','F','F','2')
 #define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
 #define OTS_TAG_CVT  OTS_TAG('c','v','t',' ')
 #define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
 #define OTS_TAG_FPGM OTS_TAG('f','p','g','m')
 #define OTS_TAG_GASP OTS_TAG('g','a','s','p')
 #define OTS_TAG_GDEF OTS_TAG('G','D','E','F')
 #define OTS_TAG_GLAT OTS_TAG('G','l','a','t')
 #define OTS_TAG_GLOC OTS_TAG('G','l','o','c')
--- a/gfx/ots/src/silf.cc
+++ b/gfx/ots/src/silf.cc
@@ -33,17 +33,29 @@ bool OpenTypeSILF::Parse(const uint8_t* 
   if (this->version >> 16 >= 5) {
     switch ((this->compHead & SCHEME) >> 27) {
       case 0:  // uncompressed
         break;
       case 1: {  // lz4
         if (prevent_decompression) {
           return DropGraphite("Illegal nested compression");
         }
-        std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
+        size_t decompressed_size = this->compHead & FULL_SIZE;
+        if (decompressed_size < length) {
+          return DropGraphite("Decompressed size is less than compressed size");
+        }
+        if (decompressed_size == 0) {
+          return DropGraphite("Decompressed size is set to 0");
+        }
+        // decompressed table must be <= 30MB
+        if (decompressed_size > 30 * 1024 * 1024) {
+          return DropGraphite("Decompressed size exceeds 30MB: %gMB",
+                              decompressed_size / (1024.0 * 1024.0));
+        }
+        std::vector<uint8_t> decompressed(decompressed_size);
         size_t outputSize = 0;
         bool ret = mozilla::Compression::LZ4::decompressPartial(
             reinterpret_cast<const char*>(data + table.offset()),
             table.remaining(),  // input buffer size (input size + padding)
             reinterpret_cast<char*>(decompressed.data()),
             decompressed.size(),  // target output size
             &outputSize);   // return output size
         if (!ret || outputSize != decompressed.size()) {
@@ -160,25 +172,19 @@ bool OpenTypeSILF::SILSub::ParsePart(Buf
   }
   if (!table.ReadU8(&this->attrDirectionality)) {
     return parent->Error("SILSub: Failed to read attrDirectionality");
   }
   if (parent->version >> 16 >= 2) {
     if (!table.ReadU8(&this->attrMirroring)) {
       return parent->Error("SILSub: Failed to read attrMirroring");
     }
-    if (parent->version >> 16 < 4 && this->attrMirroring != 0) {
-      parent->Warning("SILSub: Nonzero attrMirroring (reserved before v4)");
-    }
     if (!table.ReadU8(&this->attrSkipPasses)) {
       return parent->Error("SILSub: Failed to read attrSkipPasses");
     }
-    if (parent->version >> 16 < 4 && this->attrSkipPasses != 0) {
-      parent->Warning("SILSub: Nonzero attrSkipPasses (reserved2 before v4)");
-    }
 
     if (!table.ReadU8(&this->numJLevels)) {
       return parent->Error("SILSub: Failed to read numJLevels");
     }
     //this->jLevels.resize(this->numJLevels, parent);
     for (unsigned i = 0; i < this->numJLevels; ++i) {
       this->jLevels.emplace_back(parent);
       if (!this->jLevels[i].ParsePart(table)) {
@@ -637,19 +643,16 @@ SILPass::ParsePart(Buffer& table, const 
   }
   if (!table.ReadU16(&this->numRules)) {
     return parent->Error("SILPass: Failed to read numRules");
   }
   if (parent->version >> 16 >= 2) {
     if (!table.ReadU16(&this->fsmOffset)) {
       return parent->Error("SILPass: Failed to read fsmOffset");
     }
-    if (parent->version >> 16 == 2 && this->fsmOffset != 0) {
-      parent->Warning("SILPass: Nonzero fsmOffset (reserved in SILSub v2)");
-    }
     if (!table.ReadU32(&this->pcCode) ||
         (parent->version >= 3 && this->pcCode < this->fsmOffset)) {
       return parent->Error("SILPass: Failed to read pcCode");
     }
   }
   if (!table.ReadU32(&this->rcCode) ||
       (parent->version >> 16 >= 2 && this->rcCode < this->pcCode)) {
     return parent->Error("SILPass: Failed to read valid rcCode");
@@ -765,20 +768,16 @@ SILPass::ParsePart(Buffer& table, const 
       return parent->Error("SILPass: Failed to read rulePreContext[%u]", i);
     }
   }
 
   if (parent->version >> 16 >= 2) {
     if (!table.ReadU8(&this->collisionThreshold)) {
       return parent->Error("SILPass: Failed to read collisionThreshold");
     }
-    if (parent->version >> 16 < 5 && this->collisionThreshold != 0) {
-      parent->Warning("SILPass: Nonzero collisionThreshold"
-                      " (reserved before v5)");
-    }
     if (!table.ReadU16(&this->pConstraint)) {
       return parent->Error("SILPass: Failed to read pConstraint");
     }
   }
 
   unsigned long ruleConstraints_len = this->aCode - this->rcCode;
     // this->rcCode <= this->aCode
   //this->oConstraints.resize(static_cast<unsigned long>(this->numRules) + 1);
--- a/gfx/ots/src/stat.cc
+++ b/gfx/ots/src/stat.cc
@@ -10,17 +10,17 @@ namespace ots {
 // -----------------------------------------------------------------------------
 // OpenTypeSTAT
 // -----------------------------------------------------------------------------
 
 bool OpenTypeSTAT::ValidateNameId(uint16_t nameid, bool allowPredefined) {
   OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
       GetFont()->GetTypedTable(OTS_TAG_NAME));
 
-  if (!name->IsValidNameId(nameid)) {
+  if (!name || !name->IsValidNameId(nameid)) {
     Drop("Invalid nameID: %d", nameid);
     return false;
   }
 
   if (!allowPredefined && nameid < 26) {
     Warning("nameID out of range: %d", nameid);
     return true;
   }
--- a/gfx/ots/src/variations.cc
+++ b/gfx/ots/src/variations.cc
@@ -18,16 +18,20 @@ bool ParseVariationRegionList(const ots:
 
   uint16_t axisCount;
 
   if (!subtable.ReadU16(&axisCount) ||
       !subtable.ReadU16(regionCount)) {
     return OTS_FAILURE_MSG("Failed to read variation region list header");
   }
 
+  if (*regionCount == 0) {
+    return true;
+  }
+
   const ots::OpenTypeFVAR* fvar =
     static_cast<ots::OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
   if (!fvar) {
     return OTS_FAILURE_MSG("Required fvar table is missing");
   }
   if (axisCount != fvar->AxisCount()) {
     return OTS_FAILURE_MSG("Axis count mismatch");
   }
@@ -53,49 +57,51 @@ bool ParseVariationRegionList(const ots:
     }
   }
 
   return true;
 }
 
 bool
 ParseVariationDataSubtable(const ots::Font* font, const uint8_t* data, const size_t length,
-                           const uint16_t regionCount) {
+                           const uint16_t regionCount,
+                           uint16_t* regionIndexCount) {
   ots::Buffer subtable(data, length);
 
   uint16_t itemCount;
   uint16_t shortDeltaCount;
-  uint16_t regionIndexCount;
 
   if (!subtable.ReadU16(&itemCount) ||
       !subtable.ReadU16(&shortDeltaCount) ||
-      !subtable.ReadU16(&regionIndexCount)) {
+      !subtable.ReadU16(regionIndexCount)) {
     return OTS_FAILURE_MSG("Failed to read variation data subtable header");
   }
 
-  for (unsigned i = 0; i < regionIndexCount; i++) {
+  for (unsigned i = 0; i < *regionIndexCount; i++) {
     uint16_t regionIndex;
     if (!subtable.ReadU16(&regionIndex) || regionIndex >= regionCount) {
       return OTS_FAILURE_MSG("Bad region index");
     }
   }
 
-  if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + size_t(regionIndexCount)))) {
+  if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + size_t(*regionIndexCount)))) {
     return OTS_FAILURE_MSG("Failed to read delta data");
   }
 
   return true;
 }
 
 } // namespace
 
 namespace ots {
 
 bool
-ParseItemVariationStore(const Font* font, const uint8_t* data, const size_t length) {
+ParseItemVariationStore(const Font* font,
+                        const uint8_t* data, const size_t length,
+                        std::vector<uint16_t>* regionIndexCounts) {
   Buffer subtable(data, length);
 
   uint16_t format;
   uint32_t variationRegionListOffset;
   uint16_t itemVariationDataCount;
 
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU32(&variationRegionListOffset) ||
@@ -123,36 +129,41 @@ ParseItemVariationStore(const Font* font
   for (unsigned i = 0; i < itemVariationDataCount; i++) {
     uint32_t offset;
     if (!subtable.ReadU32(&offset)) {
       return OTS_FAILURE_MSG("Failed to read variation data subtable offset");
     }
     if (offset >= length) {
       return OTS_FAILURE_MSG("Bad offset to variation data subtable");
     }
-    if (!ParseVariationDataSubtable(font, data + offset, length - offset, regionCount)) {
+    uint16_t regionIndexCount = 0;
+    if (!ParseVariationDataSubtable(font, data + offset, length - offset,
+                                    regionCount,
+                                    &regionIndexCount)) {
       return OTS_FAILURE_MSG("Failed to parse variation data subtable");
     }
+    if (regionIndexCounts) {
+      regionIndexCounts->push_back(regionIndexCount);
+    }
   }
 
   return true;
 }
 
 bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length) {
   Buffer subtable(data, length);
 
   uint16_t entryFormat;
   uint16_t mapCount;
 
   if (!subtable.ReadU16(&entryFormat) ||
       !subtable.ReadU16(&mapCount)) {
     return OTS_FAILURE_MSG("Failed to read delta set index map header");
   }
 
-  const uint16_t INNER_INDEX_BIT_COUNT_MASK = 0x000F;
   const uint16_t MAP_ENTRY_SIZE_MASK = 0x0030;
 
   const uint16_t entrySize = (((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1);
   if (!subtable.Skip(entrySize * mapCount)) {
     return OTS_FAILURE_MSG("Failed to read delta set index map data");
   }
 
   return true;
--- a/gfx/ots/src/variations.h
+++ b/gfx/ots/src/variations.h
@@ -1,22 +1,25 @@
 // Copyright (c) 2018 The OTS 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_VARIATIONS_H_
 #define OTS_VARIATIONS_H_
 
+#include <vector>
 #include "ots.h"
 
 // Utility functions for OpenType variations common table formats.
 
 namespace ots {
 
-bool ParseItemVariationStore(const Font* font, const uint8_t* data, const size_t length);
+bool ParseItemVariationStore(const Font* font,
+                             const uint8_t* data, const size_t length,
+                             std::vector<uint16_t>* out_region_index_count = NULL);
 
 bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length);
 
 bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
                         size_t axisCount, size_t sharedTupleCount);
 
 }  // namespace ots
 
--- a/gfx/ots/src/vvar.cc
+++ b/gfx/ots/src/vvar.cc
@@ -1,18 +1,16 @@
 // Copyright (c) 2018 The OTS 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 "vvar.h"
 
 #include "variations.h"
 
-#define TABLE_NAME "VVAR"
-
 namespace ots {
 
 // -----------------------------------------------------------------------------
 // OpenTypeVVAR
 // -----------------------------------------------------------------------------
 
 bool OpenTypeVVAR::Parse(const uint8_t* data, size_t length) {
   Buffer table(data, length);
@@ -90,10 +88,8 @@ bool OpenTypeVVAR::Serialize(OTSStream* 
   if (!out->Write(this->m_data, this->m_length)) {
     return Error("Failed to write VVAR table");
   }
 
   return true;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
rename from gfx/ots/tests/cff_type2_charstring_test.cc
rename to gfx/ots/tests/cff_charstring_test.cc
--- a/gfx/ots/tests/cff_type2_charstring_test.cc
+++ b/gfx/ots/tests/cff_charstring_test.cc
@@ -1,13 +1,13 @@
 // Copyright (c) 2010-2017 The OTS 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 "cff_type2_charstring.h"
+#include "cff_charstring.h"
 
 #include <gtest/gtest.h>
 
 #include <climits>
 #include <vector>
 
 #include "cff.h"
 
@@ -72,17 +72,17 @@ bool EncodeNumber(int num, std::vector<u
   }
   if (num <= -108 && num >= -1131) {
     const uint8_t v = (-(num + 108) / 256) + 251;
     const uint8_t w = -(num + 108) % 256;
     out_bytes->push_back(v);
     out_bytes->push_back(w);
     return true;
   }
-  if (num <= -32768 && num >= 32767) {
+  if (num <= 32768 && num >= -32767) {
     const uint8_t v = (num % 0xff00u) >> 8;
     const uint8_t w = num % 0xffu;
     out_bytes->push_back(28);
     out_bytes->push_back(v);
     out_bytes->push_back(w);
     return true;
   }
   return false;
@@ -119,56 +119,54 @@ bool AddSubr(const int *subr, size_t sub
   return true;
 }
 
 // Validates |char_string| and returns true if it's valid.
 bool Validate(const int *char_string, size_t char_string_len,
               const int *global_subrs, size_t global_subrs_len,
               const int *local_subrs, size_t local_subrs_len) {
   std::vector<uint8_t> buffer;
-  ots::CFFIndex char_strings_index;
+  ots::CFFIndex* char_strings_index = new ots::CFFIndex;
   ots::CFFIndex global_subrs_index;
-  ots::CFFIndex local_subrs_index;
+  ots::CFFIndex* local_subrs_index = new ots::CFFIndex;
 
   if (char_string) {
     if (!AddSubr(char_string, char_string_len,
-                 &buffer, &char_strings_index)) {
+                 &buffer, char_strings_index)) {
       return false;
     }
   }
   if (global_subrs) {
     if (!AddSubr(global_subrs, global_subrs_len,
                  &buffer, &global_subrs_index)) {
       return false;
     }
   }
   if (local_subrs) {
     if (!AddSubr(local_subrs, local_subrs_len,
-                 &buffer, &local_subrs_index)) {
+                 &buffer, local_subrs_index)) {
       return false;
     }
   }
 
-  const std::map<uint16_t, uint8_t> fd_select;  // empty
-  const std::vector<ots::CFFIndex *> local_subrs_per_font;  // empty
   ots::Buffer ots_buffer(&buffer[0], buffer.size());
 
   ots::FontFile* file = new ots::FontFile();
+  file->context = new ots::OTSContext();
   ots::Font* font = new ots::Font(file);
-  file->context = new ots::OTSContext();
-  bool ret = ots::ValidateType2CharStringIndex(font,
-                                           char_strings_index,
-                                           global_subrs_index,
-                                           fd_select,
-                                           local_subrs_per_font,
-                                           &local_subrs_index,
-                                           &ots_buffer);
+  ots::OpenTypeCFF* cff = new ots::OpenTypeCFF(font, OTS_TAG_CFF);
+  cff->charstrings_index = char_strings_index;
+  cff->local_subrs = local_subrs_index;
+  bool ret = ots::ValidateCFFCharStrings(*cff,
+                                         global_subrs_index,
+                                         &ots_buffer);
   delete file->context;
   delete file;
   delete font;
+  delete cff;
 
   return ret;
 }
 
 // Validates |char_string| and returns true if it's valid.
 bool ValidateCharStrings(const int *char_string, size_t char_string_len) {
   return Validate(char_string, char_string_len, NULL, 0, NULL, 0);
 }
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -49,17 +49,17 @@ UNIFIED_SOURCES += [ '/gfx/2d/unittest/%
 ]]
 
 # not UNIFIED_SOURCES because layout_common_table_test.cc has classes
 # in an anonymous namespace which result in a GCC error when used in
 # tests (e g. "error: 'ScriptListTableTest_TestSuccess_Test' has a field
 # 'ScriptListTableTest_TestSuccess_Test::<anonymous>' whose type uses
 # the anonymous namespace").
 SOURCES += [ '/gfx/ots/tests/%s' % p for p in [
-    'cff_type2_charstring_test.cc',
+    'cff_charstring_test.cc',
     'layout_common_table_test.cc',
 ]]
 
 # ICC profiles used for verifying QCMS transformations. The copyright
 # notice embedded in the profiles should be reviewed to ensure there are
 # no known restrictions on distribution.
 TEST_HARNESS_FILES.gtest += [
     'icc_profiles/lcms_samsung_syncmaster.icc',