Update from https://crrev.com/317530

TBR=qsr@chromium.org
BUG=461092

Review URL: https://codereview.chromium.org/952893003
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index d245868..e147754 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -28,9 +28,9 @@
 if (is_win) {
   import("//third_party/yasm/yasm_assemble.gni")
   yasm_assemble("boringssl_asm") {
-    if (cpu_arch == "x64") {
+    if (current_cpu == "x64") {
       sources = gypi_values.boringssl_win_x86_64_sources
-    } else if (cpu_arch == "x86") {
+    } else if (current_cpu == "x86") {
       sources = gypi_values.boringssl_win_x86_sources
     }
   }
@@ -52,7 +52,13 @@
   }
 
   configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [ "//build/config/compiler:no_chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+
+    # TODO(davidben): Fix size_t truncations in BoringSSL.
+    # https://crbug.com/429039
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   # Also gets the include dirs from :openssl_config
   include_dirs = [
@@ -64,13 +70,7 @@
     "src/crypto",
   ]
 
-  if (is_win) {
-    # TODO(davidben): Fix size_t truncations in BoringSSL.
-    # https://crbug.com/429039
-    cflags += [ "/wd4267" ]
-  }
-
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     if (is_mac) {
       sources += gypi_values.boringssl_mac_x86_64_sources
     } else if (is_linux || is_android) {
@@ -80,7 +80,7 @@
     } else {
       defines += [ "OPENSSL_NO_ASM" ]
     }
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     if (is_mac) {
       sources += gypi_values.boringssl_mac_x86_sources
     } else if (is_linux || is_android) {
@@ -90,9 +90,9 @@
     } else {
       defines += [ "OPENSSL_NO_ASM" ]
     }
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     sources += gypi_values.boringssl_linux_arm_sources
-  } else if (cpu_arch == "arm64") {
+  } else if (current_cpu == "arm64") {
     sources += gypi_values.boringssl_linux_aarch64_sources
   } else {
     defines += [ "OPENSSL_NO_ASM" ]
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index 000a9cc..71213fd 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -28,7 +28,7 @@
   } else {
     use_system_harfbuzz = false
   }
-  if (is_linux && cpu_arch == "arm" && !is_chromeos) {
+  if (is_linux && current_cpu == "arm" && !is_chromeos) {
     # Override use_system_harfbuzz for ARM cross compiling so system
     # harfbuzz is not used because the corresponding package is not
     # available.
@@ -161,10 +161,7 @@
       cflags += [ "-Wno-unused-value" ]
     }
     if (is_win) {
-      cflags += [
-        "/wd4267",  # size_t to 'type' converion.
-        "/wd4334",  # Result of 32-bit shift implicitly converted to 64 bits.
-      ]
+      cflags += [ "/wd4334" ]  # Result of 32-bit shift implicitly converted to 64 bits.
     }
     if (is_mac) {
       sources += [
diff --git a/third_party/jstemplate/README.chromium b/third_party/jstemplate/README.chromium
index 6fd6f26..14b92f3 100644
--- a/third_party/jstemplate/README.chromium
+++ b/third_party/jstemplate/README.chromium
@@ -1,6 +1,8 @@
 Name: google-jstemplate
 URL: http://code.google.com/p/google-jstemplate/
 License: Apache 2.0
+Security Critical: yes
+Version: unknown
 
 "Template processing that is more suitable for the specific development-time
 and runtime requirements of AJAX based web applications.
@@ -16,3 +18,7 @@
 
 jstemplate_compiled.js is the output after passing the code through
 compile.sh.
+
+Local modifications:
+Changed JSDoc annotations and subtle code changes to make it compile with modern
+versions of Closure Compiler. TODO(dbeam): upstream to google code project.
diff --git a/third_party/jstemplate/jsevalcontext.js b/third_party/jstemplate/jsevalcontext.js
index 52bbc62..f958a1e 100644
--- a/third_party/jstemplate/jsevalcontext.js
+++ b/third_party/jstemplate/jsevalcontext.js
@@ -243,7 +243,7 @@
  *
  * @param {Object} data The new context object.
  *
- * @param {number} index Position of the new context when multiply
+ * @param {number|string} index Position of the new context when multiply
  * instantiated. (See implementation of jstSelect().)
  * 
  * @param {number} count The total number of contexts that were multiply
diff --git a/third_party/jstemplate/jstemplate.js b/third_party/jstemplate/jstemplate.js
index b4c154f..449a31c 100644
--- a/third_party/jstemplate/jstemplate.js
+++ b/third_party/jstemplate/jstemplate.js
@@ -552,9 +552,6 @@
  * @param {Element} template The currently processed node of the template.
  *
  * @param {Function} select The javascript expression to evaluate.
- *
- * @notypecheck FIXME(hmitchell): See OCL6434950. instance and value need
- * type checks.
  */
 JstProcessor.prototype.jstSelect_ = function(context, template, select) {
   var me = this;
@@ -586,6 +583,7 @@
   var multipleEmpty = (multiple && count == 0);
 
   if (multiple) {
+    value = /** @type Array */(value);
     if (multipleEmpty) {
       // For an empty array, keep the first template instance and mark
       // it last. Remove all other template instances.
@@ -938,7 +936,7 @@
  * @param {Array} values The current input context, the array of
  * values of which the template node will render one instance.
  *
- * @param {number} index The index of this template node in values.
+ * @param {number|string} index The index of this template node in values.
  */
 function jstSetInstance(template, values, index) {
   if (index == jsLength(values) - 1) {
diff --git a/third_party/libpng/BUILD.gn b/third_party/libpng/BUILD.gn
index 488ef43..787d4fa 100644
--- a/third_party/libpng/BUILD.gn
+++ b/third_party/libpng/BUILD.gn
@@ -52,12 +52,8 @@
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): http://crbug.com/167187
-
-    if (component_mode == "shared_library") {
-      defines = [ "PNG_BUILD_DLL" ]
-    }
+  if (is_win && is_component_build) {
+    defines = [ "PNG_BUILD_DLL" ]
   }
 
   public_configs = [ ":libpng_config" ]
diff --git a/third_party/ots/BUILD.gn b/third_party/ots/BUILD.gn
new file mode 100644
index 0000000..513dce6
--- /dev/null
+++ b/third_party/ots/BUILD.gn
@@ -0,0 +1,90 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("ots_config") {
+  include_dirs = [ "include" ]
+}
+
+source_set("ots") {
+  sources = [
+    "include/ots-memory-stream.h",
+    "include/opentype-sanitiser.h",
+    "src/cff.cc",
+    "src/cff.h",
+    "src/cff_type2_charstring.cc",
+    "src/cff_type2_charstring.h",
+    "src/cmap.cc",
+    "src/cmap.h",
+    "src/cvt.cc",
+    "src/cvt.h",
+    "src/fpgm.cc",
+    "src/fpgm.h",
+    "src/gasp.cc",
+    "src/gasp.h",
+    "src/gdef.cc",
+    "src/gdef.h",
+    "src/glyf.cc",
+    "src/glyf.h",
+    "src/gpos.cc",
+    "src/gpos.h",
+    "src/gsub.cc",
+    "src/gsub.h",
+    "src/hdmx.cc",
+    "src/hdmx.h",
+    "src/head.cc",
+    "src/head.h",
+    "src/hhea.cc",
+    "src/hhea.h",
+    "src/hmtx.cc",
+    "src/hmtx.h",
+    "src/kern.cc",
+    "src/kern.h",
+    "src/layout.cc",
+    "src/layout.h",
+    "src/loca.cc",
+    "src/loca.h",
+    "src/ltsh.cc",
+    "src/ltsh.h",
+    "src/maxp.cc",
+    "src/maxp.h",
+    "src/math.cc",
+    "src/math_.h",
+    "src/metrics.cc",
+    "src/metrics.h",
+    "src/name.cc",
+    "src/name.h",
+    "src/os2.cc",
+    "src/os2.h",
+    "src/ots.cc",
+    "src/ots.h",
+    "src/post.cc",
+    "src/post.h",
+    "src/prep.cc",
+    "src/prep.h",
+    "src/vdmx.cc",
+    "src/vdmx.h",
+    "src/vhea.cc",
+    "src/vhea.h",
+    "src/vmtx.cc",
+    "src/vmtx.h",
+    "src/vorg.cc",
+    "src/vorg.h",
+    "src/woff2.cc",
+    "src/woff2.h",
+  ]
+
+  direct_dependent_configs = [ ":ots_config" ]
+
+  deps = [
+    "//third_party/brotli",
+    "//third_party/zlib",
+  ]
+
+  if (is_win) {
+    cflags = [
+      "/wd4267",  # Conversion from size_t to 'type'.
+      "/wd4334",  # 32-bit shift implicitly converted to 64-bits.
+    ]
+  }
+}
diff --git a/third_party/ots/INSTALL b/third_party/ots/INSTALL
new file mode 100644
index 0000000..dbb1d94
--- /dev/null
+++ b/third_party/ots/INSTALL
@@ -0,0 +1,38 @@
+How to build (using gyp):
+
+  (Note: test programs which require gtest can't build with gyp for now)
+
+  1. If you are building OTS on Windows, download both the source
+     code and compiled driver for zlib from http://www.zlib.net/
+     and put them in third_party/zlib.
+
+  2. If you are building from cloned Git repository, make sure to update the
+     submodules as well:
+
+     $ git submodule init
+     $ git submodule update
+
+  3. Run gyp_ots
+
+    $ python gyp_ots
+
+    This will fetch gyp and generate build files. By default, following
+    files will be generated:
+      - MSVS solution file on Windows
+      - Xcode project file on Mac
+      - Makefile on Linux
+
+    If you want to generate Makefile on Mac, you can use -f option:
+
+    $ python gyp_ots -f make
+
+  4. Build OTS
+
+    Using MSVS:
+      Open ots-standalone.sln and build the solution.
+
+    Using Xcode:
+      $ xcodebuild -target ots-standalone.xcodeproj -target all
+
+    Using Makefile:
+      $ make
diff --git a/third_party/ots/LICENSE b/third_party/ots/LICENSE
new file mode 100644
index 0000000..a7531cf
--- /dev/null
+++ b/third_party/ots/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/ots/OWNERS b/third_party/ots/OWNERS
new file mode 100644
index 0000000..96e5d87
--- /dev/null
+++ b/third_party/ots/OWNERS
@@ -0,0 +1,4 @@
+bashi@chromium.org
+behdad@chromium.org
+behdad@google.com
+jshin@chromium.org
diff --git a/third_party/ots/README b/third_party/ots/README
new file mode 100644
index 0000000..8dc1ebb
--- /dev/null
+++ b/third_party/ots/README
@@ -0,0 +1,21 @@
+Sanitiser for OpenType
+----------------------
+
+(Idea from Alex Russell)
+
+The CSS font-face property[1] is great for web typography. Having to use images
+in order to get the correct typeface is a great sadness; one should be able to
+use vectors.
+
+However, the TrueType renderers on many platforms have never been part of the
+attack surface before and putting them on the front line is a scary proposition.
+Esp on platforms like Windows where it's a closed-source blob running with high
+privilege.
+
+Thus, the OpenType Sanitiser (OTS) is designed to parse and serialise OpenType
+files, validating them and sanitising them as it goes.
+
+
+See INSTALL for build instructions.
+
+[1] http://www.w3.org/TR/CSS2/fonts.html#font-descriptions
diff --git a/third_party/ots/README.chromium b/third_party/ots/README.chromium
new file mode 100644
index 0000000..c828813
--- /dev/null
+++ b/third_party/ots/README.chromium
@@ -0,0 +1,5 @@
+Name: OTS
+URL: https://github.com/khaledhosny/ots.git
+Version: ea88f974e00e7fe0b4fbfe8d0adad8cfedf49c57
+Security Critical: yes
+License: BSD
diff --git a/third_party/ots/docs/DesignDoc.md b/third_party/ots/docs/DesignDoc.md
new file mode 100644
index 0000000..ffcc035
--- /dev/null
+++ b/third_party/ots/docs/DesignDoc.md
@@ -0,0 +1,136 @@
+What's OTS?
+===========
+
+Sanitiser for OpenType (OTS) is a small library which parses OpenType files
+(usually from `@font-face`) and attempts to validate and sanitise them. This
+library is primarily intended to be used with Chromium. We hope this reduces
+the attack surface of the system font libraries.
+
+What the sanitiser does is as follows:
+
+1. Parses an original font. If the parsing fails, OTS rejects the original
+   font.
+2. Validates the parsed data structure. If the validation fails, it rejects the
+   original font as well.
+3. Creates a new font on memory by serializing the data structure, and we call
+   this "transcoding".
+
+By transcoding fonts in this way, it is ensured that:
+
+1. All information in an original font that OTS doesn't know or can't parse is
+   dropped from the transcoded font.
+2. All information in the transcoded font is valid (standard compliant).
+   Particularly 'length' and 'offset' values, that are often used as attack
+   vectors, are ensured to be correct.
+
+Supported OpenType tables
+=========================
+
+| Name   | Mandatory table?            | Supported by OTS? | Note   |
+|--------|-----------------------------|-------------------|--------|
+| `sfnt` | Yes                         | Yes               | Overlapped tables are not allowed; it is treated as a fatal parser error.|
+| `maxp` | Yes                         | Yes               |        |
+| `head` | Yes                         | Yes               |        |
+| `hhea` | Yes                         | Yes               |        |
+| `hmtx` | Yes                         | Yes               |        |
+| `name` | Yes                         | Yes               |        |
+| `OS/2` | Yes                         | Yes               |        |
+| `post` | Yes                         | Yes               |        |
+| `cmap` | Yes                         | Partialy          | see below |
+| `glyf` | Yes, for TrueType fonts     | Yes               | TrueType bytecode is supported, but OTS does **not** validate it.|
+| `loca` | Yes, when glyf table exists | Yes               |        |
+| `CFF ` | Yes, for OpenType fonts     | Yes               | OpenType bytecode is also supported, and OTS **does** validate it.|
+| `cvt ` | No                          | Yes               | Though this table is not mandatory, OTS can't drop the table from a transcoded font since it might be referred from other hinting-related tables. Errors on this table should be treated as fatal.|
+| `fpgm` | No                          | Yes               | Ditto. |
+| `prep` | No                          | Yes               | Ditto. |
+| `VDMX` | No                          | Yes               | This table is important for calculating the correct line spacing, at least on Chromium Windows and Chromium Linux.|
+| `hdmx` | No                          | Yes               |        |
+| `gasp` | No                          | Yes               |        |
+| `VORG` | No                          | Yes               |        |
+| `LTSH` | No                          | Yes               |        |
+| `kern` | No                          | Yes               |        |
+| `GDEF` | No                          | Yes               |        |
+| `GSUB` | No                          | Yes               |        |
+| `GPOS` | No                          | Yes               |        |
+| `morx` | No                          | No                |        |
+| `jstf` | No                          | No                |        |
+| `vmtx` | No                          | Yes               |        |
+| `vhea` | No                          | Yes               |        |
+| `EBDT` | No                          | No                | We don't support embedded bitmap strikes.|
+| `EBLC` | No                          | No                | Ditto. |
+| `EBSC` | No                          | No                | Ditto. |
+| `bdat` | No                          | No                | Ditto. |
+| `bhed` | No                          | No                | Ditto. |
+| `bloc` | No                          | No                | Ditto. |
+| `DSIG` | No                          | No                |        |
+| All other tables | -                 | No                |        |
+
+Please note that OTS library does not parse "unsupported" tables. These
+unsupported tables never appear in a transcoded font.
+
+Supported cmap formats
+----------------------
+
+The following 9 formats are supported:
+
+* "MS Unicode" (platform 3 encoding 1 format 4)
+    * BMP
+* "MS UCS-4" (platform 3 encoding 10 format 12)
+* "MS UCS-4 fallback" (platform 3 encoding 10 format 13)
+* "MS Symbol" (platform 3 encoding 0 format 4)
+* "Mac Roman" (platform 1 encoding 0 format 0)
+    * 1-0-0 format is supported while 1-0-6 is not.
+* "Unicode default" format (platform 0 encoding 0 format 4)
+    * treated as 3-1-4 format
+* "Unicode 1.1" format (platform 0 encoding 1 format 4)
+    * ditto
+* "Unicode 2.0+" format (platform 0 encoding 3 format 4)
+* "Unicode UCS-4" format (platform 0 encoding 4 format 12)
+    * treated as 3-10-12 format
+* Unicode Variation Sequences (platform 0 encoding 5 format 14)
+
+All other types of subtables are not supported and do not appear in transcoded fonts.
+
+Validation strategies
+=====================
+
+With regards to 8 mandatory tables, glyph-related tables (`glyf`, `loca` and `CFF`),
+and hinting-related tables (`cvt`, `prep`, and `fpgm`):
+
+* If OTS finds table-length, table-offset, or table-alignment errors, in other
+  words it cannot continue parsing, OTS treats the error as fatal.
+* If OTS finds simple value error which could be automatically fixed (e.g.,
+  font weight is greater than 900 - that's undefined), and if the error is
+  considered common among non-malicious fonts, OTS rewrites the value and
+  continues transcoding.
+* If OTS finds a value error which is hard to fix (e.g., values which should be
+  sorted are left unsorted), OTS treats the error as fatal.
+
+With regards to optional tables (`VORG`, `gasp`, `hdmx`, `LTSH`, and `VDMX`):
+
+* If OTS finds table-length, table-offset, or table-alignment errors, OTS
+  treats the error as fatal.
+* If OTS finds other errors, it simply drops the table from a transcoded font.
+
+Files
+=====
+
+* include/opentype-sanitiser.h
+    * Declaration for the public API, `ots::Process()`.
+    * Definition of the `OTSStream` interface, a write-only memory stream.
+* include/ots-memory-stream.h
+    * Definition of the `MemoryStream` class which implements the `OTSStream`
+      interface above.
+* src/ots.h
+    * Debug macros.
+    * Definition of a `Buffer` class which is a read-only memory stream.
+* src/ots.cc
+    * Definition of the `ots::Process()` function.
+    * `sfnt` table parser.
+* test/\*.cc
+    * test tools. see test/README for details.
+
+Known issues
+============
+
+Please check the [issues](https://github.com/khaledhosny/ots/issues) page.
diff --git a/third_party/ots/docs/HowToTest.md b/third_party/ots/docs/HowToTest.md
new file mode 100644
index 0000000..a18ee34
--- /dev/null
+++ b/third_party/ots/docs/HowToTest.md
@@ -0,0 +1,68 @@
+Prerequisites
+=============
+
+You can use your Ubuntu box (>= 8.04. 9.10 is recommended) to test OTS library.
+
+First, install TrueType and OpenType fonts to the Ubuntu box as many as
+possible.
+
+    % sudo apt-get install ttf-.*[^0]$
+
+Then, put malicious TrueType fonts on `~/malicious/`. For details, please check
+http://code.google.com/p/chromium/issues/detail?id=27139#c2. Currently access
+to the issue is limited to chromium-security team members for security reasons.
+
+    % cd
+    % tar xjf ~/ttf-testsuite.tar.bz2
+
+Test
+====
+
+In order to verify that:
+
+1. OTS does not reject these unmalicious fonts.
+2. and transcoded fonts OTS generates can be loaded by a system font renderer (FreeType2).
+
+Run `test_unmalicious_fonts.sh` script:
+
+    % cd /path/to/ots/tests
+    % ./test_unmalicious_fonts.sh
+    ...............................................  (verify that no FAIL: is displayed)
+
+Then in order to verify that:
+
+1. OTS can reject malicious fonts
+2. or transcoded fonts generated by OTS do not crash a system font renderer (FreeType2).
+
+Run `test_malicious_fonts.sh` script:
+
+    % cd /path/to/ots/tests
+    % ./test_malicious_fonts.sh
+    ...............................................  (verify that no FAIL: is displayed)
+
+Command line tools
+==================
+
+We have some command line tools for tests. To build them:
+
+- On Linux:
+
+        % gyp --depth=. -f make ots-standalone.gyp
+        % make
+        (tool is located at build/Default directory)
+
+- On Windows (VC++ is needed):
+
+        % gyp --depth=. -f msvs ots-standalone.gyp
+        % devenv.exe /build Default ots-standalone.sln /project idempotent.vcproj
+        (tool is located at Default directory)
+
+- On Mac (XCode is needed):
+
+        % gyp --depth=. -f xcode ots-standalone.gyp
+        % xcodebuild -configuration Default -project ots-standalone.xcodeproj -target All
+        (tool is located at build/Default directory)
+
+You can use `idempotent` tool to check whether a font will be rejected or not.
+You can also use `ot-sanitise` tool to get sanitised font (it is available on
+Linux for now). See README file in the test directory for more details.
diff --git a/third_party/ots/gyp_ots b/third_party/ots/gyp_ots
new file mode 100755
index 0000000..9a4056e
--- /dev/null
+++ b/third_party/ots/gyp_ots
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+import sys
+
+_GYP_REVISION = '1344'
+_GYP_FETCH_URL = 'https://gyp.googlecode.com/svn/trunk@' + _GYP_REVISION
+
+def _fetch_gyp():
+  gyp_dir = os.path.join('third_party', 'gyp')
+  if not os.path.exists(gyp_dir):
+    retcode = subprocess.call(['svn', 'checkout', _GYP_FETCH_URL, gyp_dir])
+    if retcode < 0:
+      raise "Couldn't fetch gyp"
+  # TODO(bashi): Check revision, etc
+  sys.path.insert(0, os.path.abspath(os.path.join(gyp_dir, 'pylib')))
+
+def main():
+  script_dir = os.path.abspath(os.path.dirname(__file__))
+  os.chdir(script_dir)
+  _fetch_gyp()
+  import gyp
+
+  args = []
+  args.extend(['--depth', '.'])
+  args.extend(sys.argv[1:])
+  args.append(os.path.join(script_dir, 'ots-standalone.gyp'))
+  sys.exit(gyp.main(args))
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/ots/include/opentype-sanitiser.h b/third_party/ots/include/opentype-sanitiser.h
new file mode 100644
index 0000000..c454f1e
--- /dev/null
+++ b/third_party/ots/include/opentype-sanitiser.h
@@ -0,0 +1,231 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OPENTYPE_SANITISER_H_
+#define OPENTYPE_SANITISER_H_
+
+#if defined(_WIN32)
+#include <stdlib.h>
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#define ntohl(x) _byteswap_ulong (x)
+#define ntohs(x) _byteswap_ushort (x)
+#define htonl(x) _byteswap_ulong (x)
+#define htons(x) _byteswap_ushort (x)
+#else
+#include <arpa/inet.h>
+#include <stdint.h>
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+namespace ots {
+
+// -----------------------------------------------------------------------------
+// This is an interface for an abstract stream class which is used for writing
+// the serialised results out.
+// -----------------------------------------------------------------------------
+class OTSStream {
+ public:
+  OTSStream() {
+    ResetChecksum();
+  }
+
+  virtual ~OTSStream() {}
+
+  // This should be implemented to perform the actual write.
+  virtual bool WriteRaw(const void *data, size_t length) = 0;
+
+  bool Write(const void *data, size_t length) {
+    if (!length) return false;
+
+    const size_t orig_length = length;
+    size_t offset = 0;
+    if (chksum_buffer_offset_) {
+      const size_t l =
+        std::min(length, static_cast<size_t>(4) - chksum_buffer_offset_);
+      std::memcpy(chksum_buffer_ + chksum_buffer_offset_, data, l);
+      chksum_buffer_offset_ += l;
+      offset += l;
+      length -= l;
+    }
+
+    if (chksum_buffer_offset_ == 4) {
+      uint32_t tmp;
+      std::memcpy(&tmp, chksum_buffer_, 4);
+      chksum_ += ntohl(tmp);
+      chksum_buffer_offset_ = 0;
+    }
+
+    while (length >= 4) {
+      uint32_t tmp;
+      std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
+        sizeof(uint32_t));
+      chksum_ += ntohl(tmp);
+      length -= 4;
+      offset += 4;
+    }
+
+    if (length) {
+      if (chksum_buffer_offset_ != 0) return false;  // not reached
+      if (length > 4) return false;  // not reached
+      std::memcpy(chksum_buffer_,
+             reinterpret_cast<const uint8_t*>(data) + offset, length);
+      chksum_buffer_offset_ = length;
+    }
+
+    return WriteRaw(data, orig_length);
+  }
+
+  virtual bool Seek(off_t position) = 0;
+  virtual off_t Tell() const = 0;
+
+  virtual bool Pad(size_t bytes) {
+    static const uint32_t kZero = 0;
+    while (bytes >= 4) {
+      if (!WriteTag(kZero)) return false;
+      bytes -= 4;
+    }
+    while (bytes) {
+      static const uint8_t kZerob = 0;
+      if (!Write(&kZerob, 1)) return false;
+      bytes--;
+    }
+    return true;
+  }
+
+  bool WriteU8(uint8_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteU16(uint16_t v) {
+    v = htons(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteS16(int16_t v) {
+    v = htons(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteU24(uint32_t v) {
+    v = htonl(v);
+    return Write(reinterpret_cast<uint8_t*>(&v)+1, 3);
+  }
+
+  bool WriteU32(uint32_t v) {
+    v = htonl(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteS32(int32_t v) {
+    v = htonl(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteR64(uint64_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteTag(uint32_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  void ResetChecksum() {
+    chksum_ = 0;
+    chksum_buffer_offset_ = 0;
+  }
+
+  uint32_t chksum() const {
+    assert(chksum_buffer_offset_ == 0);
+    return chksum_;
+  }
+
+  struct ChecksumState {
+    uint32_t chksum;
+    uint8_t chksum_buffer[4];
+    unsigned chksum_buffer_offset;
+  };
+
+  ChecksumState SaveChecksumState() const {
+    ChecksumState s;
+    s.chksum = chksum_;
+    s.chksum_buffer_offset = chksum_buffer_offset_;
+    std::memcpy(s.chksum_buffer, chksum_buffer_, 4);
+
+    return s;
+  }
+
+  void RestoreChecksum(const ChecksumState &s) {
+    assert(chksum_buffer_offset_ == 0);
+    chksum_ += s.chksum;
+    chksum_buffer_offset_ = s.chksum_buffer_offset;
+    std::memcpy(chksum_buffer_, s.chksum_buffer, 4);
+  }
+
+ protected:
+  uint32_t chksum_;
+  uint8_t chksum_buffer_[4];
+  unsigned chksum_buffer_offset_;
+};
+
+#ifdef __GCC__
+#define MSGFUNC_FMT_ATTR __attribute__((format(printf, 2, 3)))
+#else
+#define MSGFUNC_FMT_ATTR
+#endif
+
+enum TableAction {
+  TABLE_ACTION_DEFAULT,  // Use OTS's default action for that table
+  TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
+  TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
+  TABLE_ACTION_DROP      // Drop the table
+};
+
+class OTSContext {
+  public:
+    OTSContext() {}
+    virtual ~OTSContext() {}
+
+    // Process a given OpenType file and write out a sanitised version
+    //   output: a pointer to an object implementing the OTSStream interface. The
+    //     sanitisied output will be written to this. In the even of a failure,
+    //     partial output may have been written.
+    //   input: the OpenType file
+    //   length: the size, in bytes, of |input|
+    //   context: optional context that holds various OTS settings like user callbacks
+    bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+    // This function will be called when OTS is reporting an error.
+    //   level: the severity of the generated message:
+    //     0: error messages in case OTS fails to sanitize the font.
+    //     1: warning messages about issue OTS fixed in the sanitized font.
+    virtual void Message(int level, const char *format, ...) MSGFUNC_FMT_ATTR {}
+
+    // This function will be called when OTS needs to decide what to do for a
+    // font table.
+    //   tag: table tag as an integer in big-endian byte order, independent of
+    //   platform endianness
+    virtual TableAction GetTableAction(uint32_t tag) { return ots::TABLE_ACTION_DEFAULT; }
+};
+
+// For backward compatibility - remove once Chrome switches over to the new API.
+bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+// For backward compatibility - remove once https://codereview.chromium.org/774253008/
+// is submitted.
+void EnableWOFF2();
+
+}  // namespace ots
+
+#endif  // OPENTYPE_SANITISER_H_
diff --git a/third_party/ots/include/ots-memory-stream.h b/third_party/ots/include/ots-memory-stream.h
new file mode 100644
index 0000000..579da61
--- /dev/null
+++ b/third_party/ots/include/ots-memory-stream.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MEMORY_STREAM_H_
+#define OTS_MEMORY_STREAM_H_
+
+#include <cstring>
+#include <limits>
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+class MemoryStream : public OTSStream {
+ public:
+  MemoryStream(void *ptr, size_t length)
+      : ptr_(ptr), length_(length), off_(0) {
+  }
+
+  virtual bool WriteRaw(const void *data, size_t length) {
+    if ((off_ + length > length_) ||
+        (length > std::numeric_limits<size_t>::max() - off_)) {
+      return false;
+    }
+    std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
+    off_ += length;
+    return true;
+  }
+
+  virtual bool Seek(off_t position) {
+    if (position < 0) return false;
+    if (static_cast<size_t>(position) > length_) return false;
+    off_ = position;
+    return true;
+  }
+
+  virtual off_t Tell() const {
+    return off_;
+  }
+
+ private:
+  void* const ptr_;
+  size_t length_;
+  off_t off_;
+};
+
+class ExpandingMemoryStream : public OTSStream {
+ public:
+  ExpandingMemoryStream(size_t initial, size_t limit)
+      : length_(initial), limit_(limit), off_(0) {
+    ptr_ = new uint8_t[length_];
+  }
+
+  ~ExpandingMemoryStream() {
+    delete[] static_cast<uint8_t*>(ptr_);
+  }
+
+  void* get() const {
+    return ptr_;
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    if ((off_ + length > length_) ||
+        (length > std::numeric_limits<size_t>::max() - off_)) {
+      if (length_ == limit_)
+        return false;
+      size_t new_length = (length_ + 1) * 2;
+      if (new_length < length_)
+        return false;
+      if (new_length > limit_)
+        new_length = limit_;
+      uint8_t* new_buf = new uint8_t[new_length];
+      std::memcpy(new_buf, ptr_, length_);
+      length_ = new_length;
+      delete[] static_cast<uint8_t*>(ptr_);
+      ptr_ = new_buf;
+      return WriteRaw(data, length);
+    }
+    std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
+    off_ += length;
+    return true;
+  }
+
+  bool Seek(off_t position) {
+    if (position < 0) return false;
+    if (static_cast<size_t>(position) > length_) return false;
+    off_ = position;
+    return true;
+  }
+
+  off_t Tell() const {
+    return off_;
+  }
+
+ private:
+  void* ptr_;
+  size_t length_;
+  const size_t limit_;
+  off_t off_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MEMORY_STREAM_H_
diff --git a/third_party/ots/ots-common.gypi b/third_party/ots/ots-common.gypi
new file mode 100644
index 0000000..9cb539c
--- /dev/null
+++ b/third_party/ots/ots-common.gypi
@@ -0,0 +1,77 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'ots_sources': [
+      'include/ots-memory-stream.h',
+      'include/opentype-sanitiser.h',
+      'src/cff.cc',
+      'src/cff.h',
+      'src/cff_type2_charstring.cc',
+      'src/cff_type2_charstring.h',
+      'src/cmap.cc',
+      'src/cmap.h',
+      'src/cvt.cc',
+      'src/cvt.h',
+      'src/fpgm.cc',
+      'src/fpgm.h',
+      'src/gasp.cc',
+      'src/gasp.h',
+      'src/gdef.cc',
+      'src/gdef.h',
+      'src/glyf.cc',
+      'src/glyf.h',
+      'src/gpos.cc',
+      'src/gpos.h',
+      'src/gsub.cc',
+      'src/gsub.h',
+      'src/hdmx.cc',
+      'src/hdmx.h',
+      'src/head.cc',
+      'src/head.h',
+      'src/hhea.cc',
+      'src/hhea.h',
+      'src/hmtx.cc',
+      'src/hmtx.h',
+      'src/kern.cc',
+      'src/kern.h',
+      'src/layout.cc',
+      'src/layout.h',
+      'src/loca.cc',
+      'src/loca.h',
+      'src/ltsh.cc',
+      'src/ltsh.h',
+      'src/maxp.cc',
+      'src/maxp.h',
+      'src/math.cc',
+      'src/math_.h',
+      'src/metrics.cc',
+      'src/metrics.h',
+      'src/name.cc',
+      'src/name.h',
+      'src/os2.cc',
+      'src/os2.h',
+      'src/ots.cc',
+      'src/ots.h',
+      'src/post.cc',
+      'src/post.h',
+      'src/prep.cc',
+      'src/prep.h',
+      'src/vdmx.cc',
+      'src/vdmx.h',
+      'src/vhea.cc',
+      'src/vhea.h',
+      'src/vmtx.cc',
+      'src/vmtx.h',
+      'src/vorg.cc',
+      'src/vorg.h',
+      'src/woff2.cc',
+      'src/woff2.h',
+    ],
+    'ots_include_dirs': [
+      'include',
+    ],
+  },
+}
diff --git a/third_party/ots/ots-standalone.gyp b/third_party/ots/ots-standalone.gyp
new file mode 100644
index 0000000..a45ec4a
--- /dev/null
+++ b/third_party/ots/ots-standalone.gyp
@@ -0,0 +1,256 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'gcc_cflags': [
+      '-ggdb',
+      '-W',
+      '-Wall',
+      '-Wshadow',
+      '-Wno-unused-parameter',
+      '-fPIE',
+      '-fstack-protector',
+    ],
+    'gcc_ldflags': [
+      '-ggdb',
+      '-fpie',
+      '-Wl,-z,relro',
+      '-Wl,-z,now',
+    ],
+  },
+  'includes': [
+    'ots-common.gypi',
+  ],
+  'target_defaults': {
+    'include_dirs': [
+      '.',
+      'third_party/brotli/dec',
+    ],
+    'conditions': [
+      ['OS=="linux"', {
+        'cflags': [
+          '<@(gcc_cflags)',
+          '-O',
+        ],
+        'ldflags': [
+          '<@(gcc_ldflags)',
+        ],
+        'defines': [
+          '_FORTIFY_SOURCE=2',
+        ],
+        'link_settings': {
+          'libraries': ['-lz'],
+        },
+      }],
+      ['OS=="mac"', {
+        'xcode_settings': {
+          'GCC_DYNAMIC_NO_PIC': 'NO',            # No -mdynamic-no-pic
+          'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES',   # -fvisibility=hidden
+          'OTHER_CFLAGS': [
+            '<@(gcc_cflags)',
+          ],
+        },
+        'link_settings': {
+          'libraries': [
+            '/System/Library/Frameworks/ApplicationServices.framework',
+            '/usr/lib/libz.dylib'
+          ],
+        },
+      }],
+      ['OS=="win"', {
+        'link_settings': {
+          'libraries': [
+            '-lzdll.lib',
+          ],
+        },
+        'msvs_settings': {
+          'VCLinkerTool': {
+            'AdditionalLibraryDirectories': ['third_party/zlib'],
+            'DelayLoadDLLs': ['zlib1.dll'],
+          },
+        },
+        'include_dirs': [
+          'third_party/zlib',
+        ],
+        'defines': [
+          'NOMINMAX', # To suppress max/min macro definition.
+          'WIN32',
+        ],
+      }],
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'ots',
+      'type': 'static_library',
+      'sources': [
+        '<@(ots_sources)',
+      ],
+      'dependencies': [
+        'third_party/brotli.gyp:brotli',
+      ],
+      'include_dirs': [
+        '<@(ots_include_dirs)',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<@(ots_include_dirs)',
+        ],
+      },
+    },
+    {
+      'target_name': 'freetype2',
+      'type': 'none',
+      'conditions': [
+        ['OS=="linux"', {
+          'direct_dependent_settings': {
+            'cflags': [
+              '<!(pkg-config freetype2 --cflags)',
+            ],
+            'link_settings': {
+              'libraries': [
+                '<!(pkg-config freetype2 --libs)',
+              ],
+            },
+          },
+        }],
+      ],
+    },
+    {
+      'target_name': 'idempotent',
+      'type': 'executable',
+      'sources': [
+        'test/idempotent.cc',
+      ],
+      'dependencies': [
+        'ots',
+      ],
+      'conditions': [
+        ['OS=="linux"', {
+          'dependencies': [
+            'freetype2',
+          ]
+        }],
+        ['OS=="win"', {
+          'link_settings': {
+            'libraries': [
+              '-lgdi32.lib',
+            ],
+          },
+        }],
+      ],
+    },
+    {
+      'target_name': 'ot-sanitise',
+      'type': 'executable',
+      'sources': [
+        'test/ot-sanitise.cc',
+        'test/file-stream.h',
+      ],
+      'dependencies': [
+        'ots',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS=="linux" or OS=="mac"', {
+      'targets': [
+        {
+          'target_name': 'validator_checker',
+          'type': 'executable',
+          'sources': [
+            'test/validator-checker.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'conditions': [
+            ['OS=="linux"', {
+              'dependencies': [
+                'freetype2',
+              ]
+            }],
+          ],
+        },
+        {
+          'target_name': 'perf',
+          'type': 'executable',
+          'sources': [
+            'test/perf.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+        },
+        {
+          'target_name': 'cff_type2_charstring_test',
+          'type': 'executable',
+          'sources': [
+            'test/cff_type2_charstring_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+        {
+          'target_name': 'layout_common_table_test',
+          'type': 'executable',
+          'sources': [
+            'test/layout_common_table_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+        {
+          'target_name': 'table_dependencies_test',
+          'type': 'executable',
+          'sources': [
+            'test/table_dependencies_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+      ],
+    }],
+    ['OS=="linux"', {
+      'targets': [
+        {
+          'target_name': 'side_by_side',
+          'type': 'executable',
+          'sources': [
+            'test/side-by-side.cc',
+          ],
+          'dependencies': [
+            'freetype2',
+            'ots',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/third_party/ots/ots.gyp b/third_party/ots/ots.gyp
new file mode 100644
index 0000000..288e41c
--- /dev/null
+++ b/third_party/ots/ots.gyp
@@ -0,0 +1,39 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  'includes': [
+    'ots-common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'ots',
+      'type': 'static_library',
+      'sources': [
+        '<@(ots_sources)',
+      ],
+      'include_dirs': [
+        '../..',
+        '<@(ots_include_dirs)',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<@(ots_include_dirs)',
+        ],
+      },
+      'dependencies': [
+        '../brotli/brotli.gyp:brotli',
+        '../zlib/zlib.gyp:zlib',
+      ],
+      # TODO(jschuh): http://crbug.com/167187
+      'msvs_disabled_warnings': [
+        4267,
+        4334,
+      ],      
+    },
+  ],
+}
diff --git a/third_party/ots/src/cff.cc b/third_party/ots/src/cff.cc
new file mode 100644
index 0000000..9c7204d
--- /dev/null
+++ b/third_party/ots/src/cff.cc
@@ -0,0 +1,1041 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cff.h"
+
+#include <cstring>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "cff_type2_charstring.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 {
+
+enum DICT_OPERAND_TYPE {
+  DICT_OPERAND_INTEGER,
+  DICT_OPERAND_REAL,
+  DICT_OPERATOR,
+};
+
+enum DICT_DATA_TYPE {
+  DICT_DATA_TOPLEVEL,
+  DICT_DATA_FDARRAY,
+};
+
+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) {
+  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)) {
+      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();
+
+  if (!table->ReadU16(&(index->count))) {
+    return OTS_FAILURE();
+  }
+  if (index->count == 0) {
+    // An empty INDEX.
+    index->offset_to_next = table->offset();
+    return true;
+  }
+
+  if (!table->ReadU8(&(index->off_size))) {
+    return OTS_FAILURE();
+  }
+  if ((index->off_size == 0) ||
+      (index->off_size > 4)) {
+    return OTS_FAILURE();
+  }
+
+  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;
+  // does not overflow too, since offset() <= 1GB.
+
+  if (object_data_offset >= table->length()) {
+    return OTS_FAILURE();
+  }
+
+  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)) {
+      return OTS_FAILURE();
+    }
+    if (rel_offset < 1) {
+      return OTS_FAILURE();
+    }
+    if (i == 0 && rel_offset != 1) {
+      return OTS_FAILURE();
+    }
+
+    if (rel_offset > table->length()) {
+      return OTS_FAILURE();
+    }
+
+    // does not underflow.
+    if (object_data_offset > table->length() - (rel_offset - 1)) {
+      return OTS_FAILURE();
+    }
+
+    index->offsets.push_back(
+        object_data_offset + (rel_offset - 1));  // less than length(), 1GB.
+  }
+
+  for (unsigned i = 1; i < index->offsets.size(); ++i) {
+    // We allow consecutive identical offsets here for zero-length strings.
+    // See http://crbug.com/69341 for more details.
+    if (index->offsets[i] < index->offsets[i - 1]) {
+      return OTS_FAILURE();
+    }
+  }
+
+  index->offset_to_next = index->offsets.back();
+  return true;
+}
+
+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.
+    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) {
+      return OTS_FAILURE();
+    }
+
+    table->set_offset(index.offsets[i - 1]);
+    if (!table->Read(name, length)) {
+      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) {
+  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) {
+  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 read_decimal_point = false;
+  bool read_e = false;
+
+  uint8_t nibble = 0;
+  size_t count = 0;
+  while (true) {
+    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),
+                                           DICT_OPERAND_REAL));
+        return true;
+      }
+      return OTS_FAILURE();
+    }
+    if ((nibble & 0x0f) == 0x0f) {
+      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);
+    for (unsigned i = 0; i < 2; ++i) {
+      if (nibbles[i] == 0xd) {  // reserved number
+        return OTS_FAILURE();
+      }
+      if ((nibbles[i] == 0xe) &&  // minus
+          ((count > 0) || (i > 0))) {
+        return OTS_FAILURE();  // minus sign should be the first character.
+      }
+      if (nibbles[i] == 0xa) {  // decimal point
+        if (!read_decimal_point) {
+          read_decimal_point = true;
+        } else {
+          return OTS_FAILURE();  // two or more points.
+        }
+      }
+      if ((nibbles[i] == 0xb) ||  // E+
+          (nibbles[i] == 0xc)) {  // E-
+        if (!read_e) {
+          read_e = true;
+        } else {
+          return OTS_FAILURE();  // two or more E's.
+        }
+      }
+    }
+    ++count;
+  }
+}
+
+bool ParseDictDataEscapedOperator(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t op = 0;
+  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));
+    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) {
+  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)) {
+        return OTS_FAILURE();
+      }
+      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)) {
+        return OTS_FAILURE();
+      }
+      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)) {
+      return OTS_FAILURE();
+    }
+    result = (b0 - 247) * 256 + b1 + 108;
+  } else if (b0 >= 251 && b0 <= 254) {
+    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));
+  return true;
+}
+
+bool ParseDictDataReadNext(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t op = 0;
+  if (!table->ReadU8(&op)) {
+    return OTS_FAILURE();
+  }
+  if (op <= 21) {
+    if (op == 12) {
+      return ParseDictDataEscapedOperator(table, operands);
+    }
+    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 ParsePrivateDictData(
+    const uint8_t *data,
+    size_t table_length, 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;
+
+  // 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.
+      return OTS_FAILURE();
+    }
+    if (operands.back().second != DICT_OPERATOR) {
+      continue;
+    }
+
+    // got operator
+    const uint32_t op = operands.back().first;
+    operands.pop_back();
+
+    switch (op) {
+      // array
+      case 6:  // BlueValues
+      case 7:  // OtherBlues
+      case 8:  // FamilyBlues
+      case 9:  // FamilyOtherBlues
+      case (12U << 8) + 12:  // StemSnapH (delta)
+      case (12U << 8) + 13:  // StemSnapV (delta)
+        if (operands.empty()) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      // number
+      case 10:  // StdHW
+      case 11:  // StdVW
+      case 20:  // defaultWidthX
+      case 21:  // nominalWidthX
+      case (12U << 8) + 9:   // BlueScale
+      case (12U << 8) + 10:  // BlueShift
+      case (12U << 8) + 11:  // BlueFuzz
+      case (12U << 8) + 17:  // LanguageGroup
+      case (12U << 8) + 18:  // ExpansionFactor
+      case (12U << 8) + 19:  // initialRandomSeed
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      // Local Subrs INDEX, offset(self)
+      case 19: {
+        if (operands.size() != 1) {
+          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) {
+          return OTS_FAILURE();
+        }
+        // parse "16. Local Subrs INDEX"
+        ots::Buffer cff_table(data, table_length);
+        cff_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)) {
+          return OTS_FAILURE();
+        }
+        break;
+      }
+
+      // boolean
+      case (12U << 8) + 14:  // ForceBold
+        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;
+
+      default:
+        return OTS_FAILURE();
+    }
+    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,
+                   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 table(data + index.offsets[i - 1], dict_length);
+
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+
+    FONT_FORMAT font_format = FORMAT_UNKNOWN;
+    bool have_ros = false;
+    uint16_t charstring_glyphs = 0;
+    size_t charset_offset = 0;
+
+    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.
+        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;
+
+        // 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();
+          }
+          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;
+          }
+          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();
+          }
+
+          // 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)) {
+            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)) {
+            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)) {
+              return OTS_FAILURE();
+            }
+            if (n_ranges == 0) {
+              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) {
+              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();
+              }
+            }
+          } 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(data, table_length,
+                                    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) {
+      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)) {
+              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)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check CIDs when have_ros is true.
+
+            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();
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_cff_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  file->cff = new OpenTypeCFF;
+  file->cff->data = data;
+  file->cff->length = length;
+  file->cff->font_dict_length = 0;
+  file->cff->local_subrs = NULL;
+
+  // 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 ((major != 1) ||
+      (minor != 0) ||
+      (hdr_size != 4)) {
+    return OTS_FAILURE();
+  }
+  if (hdr_size >= length) {
+    return OTS_FAILURE();
+  }
+
+  // parse "7. Name INDEX"
+  table.set_offset(hdr_size);
+  CFFIndex name_index;
+  if (!ParseIndex(&table, &name_index)) {
+    return OTS_FAILURE();
+  }
+  if (!ParseNameData(&table, name_index, &(file->cff->name))) {
+    return OTS_FAILURE();
+  }
+
+  // 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 (name_index.count != top_dict_index.count) {
+    return OTS_FAILURE();
+  }
+
+  // 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 (string_index.count >= 65000 - kNStdString) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->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,
+                     num_glyphs, sid_max,
+                     DICT_DATA_TOPLEVEL, file->cff)) {
+    return OTS_FAILURE();
+  }
+
+  // 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();
+  }
+
+  // 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 = file->cff->fd_select.end();
+  for (iter = file->cff->fd_select.begin(); iter != end; ++iter) {
+    if (iter->second >= file->cff->font_dict_length) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check if all charstrings (font hinting code for each glyph) are valid.
+  for (size_t i = 0; i < file->cff->char_strings_array.size(); ++i) {
+    if (!ValidateType2CharStringIndex(file,
+                                      *(file->cff->char_strings_array.at(i)),
+                                      global_subrs_index,
+                                      file->cff->fd_select,
+                                      file->cff->local_subrs_per_font,
+                                      file->cff->local_subrs,
+                                      &table)) {
+      return OTS_FAILURE_MSG("Failed validating charstring set %d", (int) i);
+    }
+  }
+
+  return true;
+}
+
+bool ots_cff_should_serialise(OpenTypeFile *file) {
+  return file->cff != NULL;
+}
+
+bool ots_cff_serialise(OTSStream *out, OpenTypeFile *file) {
+  // TODO(yusukes): would be better to transcode the data,
+  //                rather than simple memcpy.
+  if (!out->Write(file->cff->data, file->cff->length)) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+void ots_cff_free(OpenTypeFile *file) {
+  if (file->cff) {
+    for (size_t i = 0; i < file->cff->char_strings_array.size(); ++i) {
+      delete (file->cff->char_strings_array)[i];
+    }
+    for (size_t i = 0; i < file->cff->local_subrs_per_font.size(); ++i) {
+      delete (file->cff->local_subrs_per_font)[i];
+    }
+    delete file->cff->local_subrs;
+    delete file->cff;
+  }
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cff.h b/third_party/ots/src/cff.h
new file mode 100644
index 0000000..5584acc
--- /dev/null
+++ b/third_party/ots/src/cff.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CFF_H_
+#define OTS_CFF_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct CFFIndex {
+  CFFIndex()
+      : count(0), off_size(0), offset_to_next(0) {}
+  uint16_t count;
+  uint8_t off_size;
+  std::vector<uint32_t> offsets;
+  uint32_t offset_to_next;
+};
+
+struct OpenTypeCFF {
+  const uint8_t *data;
+  size_t length;
+  // 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;
+
+  // A list of char strings.
+  std::vector<CFFIndex *> char_strings_array;
+  // 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;
+};
+
+}  // namespace ots
+
+#endif  // OTS_CFF_H_
diff --git a/third_party/ots/src/cff_type2_charstring.cc b/third_party/ots/src/cff_type2_charstring.cc
new file mode 100644
index 0000000..6dd4766
--- /dev/null
+++ b/third_party/ots/src/cff_type2_charstring.cc
@@ -0,0 +1,914 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A parser for the Type 2 Charstring Format.
+// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
+
+#include "cff_type2_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::OpenTypeFile *file,
+                            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);
+
+#ifdef DUMP_T2CHARSTRING
+// Converts |op| to a string and returns it.
+const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
+  switch (op) {
+  case ots::kHStem:
+    return "HStem";
+  case ots::kVStem:
+    return "VStem";
+  case ots::kVMoveTo:
+    return "VMoveTo";
+  case ots::kRLineTo:
+    return "RLineTo";
+  case ots::kHLineTo:
+    return "HLineTo";
+  case ots::kVLineTo:
+    return "VLineTo";
+  case ots::kRRCurveTo:
+    return "RRCurveTo";
+  case ots::kCallSubr:
+    return "CallSubr";
+  case ots::kReturn:
+    return "Return";
+  case ots::kEndChar:
+    return "EndChar";
+  case ots::kHStemHm:
+    return "HStemHm";
+  case ots::kHintMask:
+    return "HintMask";
+  case ots::kCntrMask:
+    return "CntrMask";
+  case ots::kRMoveTo:
+    return "RMoveTo";
+  case ots::kHMoveTo:
+    return "HMoveTo";
+  case ots::kVStemHm:
+    return "VStemHm";
+  case ots::kRCurveLine:
+    return "RCurveLine";
+  case ots::kRLineCurve:
+    return "RLineCurve";
+  case ots::kVVCurveTo:
+    return "VVCurveTo";
+  case ots::kHHCurveTo:
+    return "HHCurveTo";
+  case ots::kCallGSubr:
+    return "CallGSubr";
+  case ots::kVHCurveTo:
+    return "VHCurveTo";
+  case ots::kHVCurveTo:
+    return "HVCurveTo";
+  case ots::kDotSection:
+    return "DotSection";
+  case ots::kAnd:
+    return "And";
+  case ots::kOr:
+    return "Or";
+  case ots::kNot:
+    return "Not";
+  case ots::kAbs:
+    return "Abs";
+  case ots::kAdd:
+    return "Add";
+  case ots::kSub:
+    return "Sub";
+  case ots::kDiv:
+    return "Div";
+  case ots::kNeg:
+    return "Neg";
+  case ots::kEq:
+    return "Eq";
+  case ots::kDrop:
+    return "Drop";
+  case ots::kPut:
+    return "Put";
+  case ots::kGet:
+    return "Get";
+  case ots::kIfElse:
+    return "IfElse";
+  case ots::kRandom:
+    return "Random";
+  case ots::kMul:
+    return "Mul";
+  case ots::kSqrt:
+    return "Sqrt";
+  case ots::kDup:
+    return "Dup";
+  case ots::kExch:
+    return "Exch";
+  case ots::kIndex:
+    return "Index";
+  case ots::kRoll:
+    return "Roll";
+  case ots::kHFlex:
+    return "HFlex";
+  case ots::kFlex:
+    return "Flex";
+  case ots::kHFlex1:
+    return "HFlex1";
+  case ots::kFlex1:
+    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) {
+  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.
+  if (v <= 11) {
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v == 12) {
+    uint16_t result = (v << 8);
+    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().
+    *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)) {
+      return OTS_FAILURE();
+    }
+    result += v;
+    *out_number = result;
+  } else if (v <= 31) {
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v <= 246) {
+    *out_number = static_cast<int32_t>(v) - 139;
+  } else if (v <= 250) {
+    uint8_t w = 0;
+    if (!char_string->ReadU8(&w)) {
+      return OTS_FAILURE();
+    }
+    *out_number = ((static_cast<int32_t>(v) - 247) * 256) +
+        static_cast<int32_t>(w) + 108;
+  } else if (v <= 254) {
+    uint8_t w = 0;
+    if (!char_string->ReadU8(&w)) {
+      return OTS_FAILURE();
+    }
+    *out_number = -((static_cast<int32_t>(v) - 251) * 256) -
+        static_cast<int32_t>(w) - 108;
+  } else if (v == 255) {
+    // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
+    // we should treat the following 4-bytes as a 16.16 fixed-point number
+    // rather than 32bit signed int.
+    if (!char_string->Skip(4)) {
+      return OTS_FAILURE();
+    }
+    *out_number = dummy_result;
+  } else {
+    return OTS_FAILURE();
+  }
+
+  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
+// |argument_stack| are passed for that reason.
+bool ExecuteType2CharStringOperator(ots::OpenTypeFile *file,
+                                    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) {
+  const size_t stack_size = argument_stack->size();
+
+  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();
+    }
+    int32_t subr_number = argument_stack->top();
+    argument_stack->pop();
+    if (subr_number == dummy_result) {
+      // For safety, we allow subr calls only with immediate subr numbers for
+      // now. For example, we allow "123 callgsubr", but does not allow "100 12
+      // add callgsubr". Please note that arithmetic and conditional operators
+      // always push the |dummy_result| in this implementation.
+      return OTS_FAILURE();
+    }
+
+    // See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes."
+    int32_t bias = 32768;
+    if (subrs_index.count < 1240) {
+      bias = 107;
+    } else if (subrs_index.count < 33900) {
+      bias = 1131;
+    }
+    subr_number += bias;
+
+    // Sanity checks of |subr_number|.
+    if (subr_number < 0) {
+      return OTS_FAILURE();
+    }
+    if (subr_number >= kMaxSubrsCount) {
+      return OTS_FAILURE();
+    }
+    if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
+      return OTS_FAILURE();  // The number is out-of-bounds.
+    }
+
+    // Prepare ots::Buffer where we're going to jump.
+    const size_t length =
+      subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number];
+    if (length > kMaxCharStringLength) {
+      return OTS_FAILURE();
+    }
+    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(file,
+                                  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);
+  }
+
+  case ots::kReturn:
+    return true;
+
+  case ots::kEndChar:
+    *out_found_endchar = true;
+    *in_out_found_width = true;  // just in case.
+    return true;
+
+  case ots::kHStem:
+  case ots::kVStem:
+  case ots::kHStemHm:
+  case ots::kVStemHm: {
+    bool successful = false;
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 2) == 0) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) {
+      // The -1 is for "width" argument. For details, see Adobe Technical Note
+      // #5177, page 16, note 4.
+      successful = true;
+    }
+    (*in_out_num_stems) += (stack_size / 2);
+    if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;  // always set true since "w" might be 0 byte.
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kRMoveTo: {
+    bool successful = false;
+    if (stack_size == 2) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) {
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kVMoveTo:
+  case ots::kHMoveTo: {
+    bool successful = false;
+    if (stack_size == 1) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) {
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kHintMask:
+  case ots::kCntrMask: {
+    bool successful = false;
+    if (stack_size == 0) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size == 1)) {
+      // A number for "width" is found.
+      successful = true;
+    } else if ((!(*in_out_found_width)) ||  // in this case, any sizes are ok.
+               ((stack_size % 2) == 0)) {
+      // The numbers are vstem definition.
+      // See Adobe Technical Note #5177, page 24, hintmask.
+      (*in_out_num_stems) += (stack_size / 2);
+      if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+        return OTS_FAILURE();
+      }
+      successful = true;
+    }
+    if (!successful) {
+       return OTS_FAILURE();
+    }
+
+    if ((*in_out_num_stems) == 0) {
+      return OTS_FAILURE();
+    }
+    const size_t mask_bytes = (*in_out_num_stems + 7) / 8;
+    if (!char_string->Skip(mask_bytes)) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return true;
+  }
+
+  case ots::kRLineTo:
+    if (!(*in_out_found_width)) {
+      // The first stack-clearing operator should be one of hstem, hstemhm,
+      // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
+      // endchar. For details, see Adobe Technical Note #5177, page 16, note 4.
+      return OTS_FAILURE();
+    }
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 2) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHLineTo:
+  case ots::kVLineTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRRCurveTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 6) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 6) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRCurveLine:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 8) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 2) % 6) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRLineCurve:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 8) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 6) % 2) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kVVCurveTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size % 4) != 0) &&
+        (((stack_size - 1) % 4) != 0)) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHHCurveTo: {
+    bool successful = false;
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 4) == 0) {
+      // {dxa dxb dyb dxc}+
+      successful = true;
+    } else if (((stack_size - 1) % 4) == 0) {
+      // dy1? {dxa dxb dyb dxc}+
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kVHCurveTo:
+  case ots::kHVCurveTo: {
+    bool successful = false;
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 4) % 8) == 0) {
+      // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}*
+      successful = true;
+    } else if ((stack_size >= 5) &&
+               ((stack_size - 5) % 8) == 0) {
+      // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf
+      successful = true;
+    } else if ((stack_size >= 8) &&
+               ((stack_size - 8) % 8) == 0) {
+      // {dxa dxb dyb dyc dyd dxe dye dxf}+
+      successful = true;
+    } else if ((stack_size >= 9) &&
+               ((stack_size - 9) % 8) == 0) {
+      // {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kDotSection:
+    // Deprecated operator but harmless, we probably should drop it some how.
+    if (stack_size != 0) {
+      return OTS_FAILURE();
+    }
+    return true;
+
+  case ots::kAnd:
+  case ots::kOr:
+  case ots::kEq:
+  case ots::kAdd:
+  case ots::kSub:
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kNot:
+  case ots::kAbs:
+  case ots::kNeg:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDiv:
+    // TODO(yusukes): Should detect div-by-zero errors.
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDrop:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    return true;
+
+  case ots::kPut:
+  case ots::kGet:
+  case ots::kIndex:
+    // For now, just call OTS_FAILURE since there is no way to check whether the
+    // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
+    // fonts I have (except malicious ones!) use the operators.
+    // TODO(yusukes): Implement them in a secure way.
+    return OTS_FAILURE();
+
+  case ots::kRoll:
+    // Likewise, just call OTS_FAILURE for kRoll since there is no way to check
+    // whether |N| is smaller than the current stack depth or not.
+    // TODO(yusukes): Implement them in a secure way.
+    return OTS_FAILURE();
+
+  case ots::kRandom:
+    // For now, we don't handle the 'random' operator since the operator makes
+    // it hard to analyze hinting code statically.
+    return OTS_FAILURE();
+
+  case ots::kIfElse:
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kMul:
+    // TODO(yusukes): Should detect overflows.
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kSqrt:
+    // TODO(yusukes): Should check if the argument is negative.
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  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) {
+      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) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kHFlex:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 7) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kFlex:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 13) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHFlex1:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 9) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kFlex1:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 11) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+  }
+
+  return OTS_FAILURE_MSG("Undefined operator: %d (0x%x)", op, op);
+}
+
+// Executes |char_string| and updates |argument_stack|.
+//
+// call_depth: The current call depth. Initial value is zero.
+// global_subrs_index: Global subroutines.
+// local_subrs_index: Local subroutines for the current glyph.
+// 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::OpenTypeFile *file,
+                            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) {
+  if (call_depth > kMaxSubrNesting) {
+    return OTS_FAILURE();
+  }
+  *out_found_endchar = false;
+
+  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,
+                                           &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);
+      } else {
+        std::fprintf(stderr, "#%s#\n",
+           Type2CharStringOperatorToString(
+               ots::Type2CharStringOperator(operator_or_operand))
+           );
+      }
+#endif
+
+    if (!is_operator) {
+      argument_stack->push(operator_or_operand);
+      if (argument_stack->size() > kMaxArgumentStack) {
+        return OTS_FAILURE();
+      }
+      continue;
+    }
+
+    // An operator is found. Execute it.
+    if (!ExecuteType2CharStringOperator(file,
+                                        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)) {
+      return OTS_FAILURE();
+    }
+    if (*out_found_endchar) {
+      return true;
+    }
+    if (operator_or_operand == ots::kReturn) {
+      return true;
+    }
+  }
+
+  // No endchar operator is found.
+  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,
+                     uint16_t glyph_index,  // 0-origin
+                     const ots::CFFIndex **out_local_subrs_to_use) {
+  *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())) {
+    // 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()) {
+      return OTS_FAILURE();
+    }
+    const uint8_t fd_index = iter->second;
+    if (fd_index >= local_subrs_per_font.size()) {
+      return OTS_FAILURE();
+    }
+    *out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
+  } else if (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;
+  } else {
+    // Just return NULL.
+    *out_local_subrs_to_use = NULL;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ValidateType2CharStringIndex(
+    ots::OpenTypeFile *file,
+    const CFFIndex& char_strings_index,
+    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 uint16_t num_offsets =
+      static_cast<uint16_t>(char_strings_index.offsets.size());
+  if (num_offsets != char_strings_index.offsets.size() || num_offsets == 0) {
+    return OTS_FAILURE();  // no charstring.
+  }
+
+  // For each glyph, validate the corresponding charstring.
+  for (uint16_t i = 1; i < num_offsets; ++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();
+    }
+    const size_t offset = char_strings_index.offsets[i - 1];
+    cff_table->set_offset(offset);
+    if (!cff_table->Skip(length)) {
+      return OTS_FAILURE();
+    }
+    Buffer char_string(cff_table->buffer() + offset, length);
+
+    // Get a local subrs for the glyph.
+    const uint16_t 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,
+                         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;
+    size_t num_stems = 0;
+    if (!ExecuteType2CharString(file,
+                                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)) {
+      return OTS_FAILURE();
+    }
+    if (!found_endchar) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cff_type2_charstring.h b/third_party/ots/src/cff_type2_charstring.h
new file mode 100644
index 0000000..6732342
--- /dev/null
+++ b/third_party/ots/src/cff_type2_charstring.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CFF_TYPE2_CHARSTRING_H_
+#define OTS_CFF_TYPE2_CHARSTRING_H_
+
+#include "cff.h"
+#include "ots.h"
+
+#include <map>
+#include <vector>
+
+namespace ots {
+
+// 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.
+//  4. The code calls an undefined global or local subroutine.
+//  5. The code uses one of the following operators that are unlikely used in
+//     an ordinary fonts, and could be dangerous: random, put, get, index, roll.
+//
+// Arguments:
+//  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(
+    OpenTypeFile *file,
+    const CFFIndex &char_strings_index,
+    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 {
+  kHStem = 1,
+  kVStem = 3,
+  kVMoveTo = 4,
+  kRLineTo = 5,
+  kHLineTo = 6,
+  kVLineTo = 7,
+  kRRCurveTo = 8,
+  kCallSubr = 10,
+  kReturn = 11,
+  kEndChar = 14,
+  kHStemHm = 18,
+  kHintMask = 19,
+  kCntrMask = 20,
+  kRMoveTo = 21,
+  kHMoveTo = 22,
+  kVStemHm = 23,
+  kRCurveLine = 24,
+  kRLineCurve = 25,
+  kVVCurveTo = 26,
+  kHHCurveTo = 27,
+  kCallGSubr = 29,
+  kVHCurveTo = 30,
+  kHVCurveTo = 31,
+  kDotSection = 12 << 8,
+  kAnd = (12 << 8) + 3,
+  kOr = (12 << 8) + 4,
+  kNot = (12 << 8) + 5,
+  kAbs = (12 << 8) + 9,
+  kAdd = (12 << 8) + 10,
+  kSub = (12 << 8) + 11,
+  kDiv = (12 << 8) + 12,
+  kNeg = (12 << 8) + 14,
+  kEq = (12 << 8) + 15,
+  kDrop = (12 << 8) + 18,
+  kPut = (12 << 8) + 20,
+  kGet = (12 << 8) + 21,
+  kIfElse = (12 << 8) + 22,
+  kRandom = (12 << 8) + 23,
+  kMul = (12 << 8) + 24,
+  kSqrt = (12 << 8) + 26,
+  kDup = (12 << 8) + 27,
+  kExch = (12 << 8) + 28,
+  kIndex = (12 << 8) + 29,
+  kRoll = (12 << 8) + 30,
+  kHFlex = (12 << 8) + 34,
+  kFlex = (12 << 8) + 35,
+  kHFlex1 = (12 << 8) + 36,
+  kFlex1 = (12 << 8) + 37,
+  // Operators that are undocumented, such as 'blend', will be rejected.
+};
+
+}  // namespace ots
+
+#endif  // OTS_CFF_TYPE2_CHARSTRING_H_
diff --git a/third_party/ots/src/cmap.cc b/third_party/ots/src/cmap.cc
new file mode 100644
index 0000000..d9ca9fa
--- /dev/null
+++ b/third_party/ots/src/cmap.cc
@@ -0,0 +1,1106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cmap.h"
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "os2.h"
+
+// cmap - Character To Glyph Index Mapping Table
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+#define TABLE_NAME "cmap"
+
+namespace {
+
+struct CMAPSubtableHeader {
+  uint16_t platform;
+  uint16_t encoding;
+  uint32_t offset;
+  uint16_t format;
+  uint32_t length;
+  uint32_t language;
+};
+
+struct Subtable314Range {
+  uint16_t start_range;
+  uint16_t end_range;
+  int16_t id_delta;
+  uint16_t id_range_offset;
+  uint32_t id_range_offset_offset;
+};
+
+// The maximum number of groups in format 12, 13 or 14 subtables.
+// Note: 0xFFFF is the maximum number of glyphs in a single font file.
+const unsigned kMaxCMAPGroups = 0xFFFF;
+
+// Glyph array size for the Mac Roman (format 0) table.
+const size_t kFormat0ArraySize = 256;
+
+// The upper limit of the Unicode code point.
+const uint32_t kUnicodeUpperLimit = 0x10FFFF;
+
+// The maximum number of UVS records (See below).
+const uint32_t kMaxCMAPSelectorRecords = 259;
+// The range of UVSes are:
+//   0x180B-0x180D (3 code points)
+//   0xFE00-0xFE0F (16 code points)
+//   0xE0100-0xE01EF (240 code points)
+const uint32_t kMongolianVSStart = 0x180B;
+const uint32_t kMongolianVSEnd = 0x180D;
+const uint32_t kVSStart = 0xFE00;
+const uint32_t kVSEnd = 0xFE0F;
+const uint32_t kIVSStart = 0xE0100;
+const uint32_t kIVSEnd = 0xE01EF;
+const uint32_t kUVSUpperLimit = 0xFFFFFF;
+
+// Parses Format 4 tables
+bool ParseFormat4(ots::OpenTypeFile *file, int platform, int encoding,
+              const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
+  // whole thing and recompacting it, we validate it and include it verbatim
+  // in the output.
+
+  if (!file->os2) {
+    return OTS_FAILURE_MSG("Required OS/2 table missing");
+  }
+
+  if (!subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
+  }
+  uint16_t language = 0;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE_MSG("Can't read language");
+  }
+  if (language) {
+    // Platform ID 3 (windows) subtables should have language '0'.
+    return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
+  }
+
+  uint16_t segcountx2, search_range, entry_selector, range_shift;
+  segcountx2 = search_range = entry_selector = range_shift = 0;
+  if (!subtable.ReadU16(&segcountx2) ||
+      !subtable.ReadU16(&search_range) ||
+      !subtable.ReadU16(&entry_selector) ||
+      !subtable.ReadU16(&range_shift)) {
+    return OTS_FAILURE_MSG("Failed to read subcmap structure");
+  }
+
+  if (segcountx2 & 1 || search_range & 1) {
+    return OTS_FAILURE_MSG("Bad subcmap structure");
+  }
+  const uint16_t segcount = segcountx2 >> 1;
+  // There must be at least one segment according the spec.
+  if (segcount < 1) {
+    return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
+  }
+
+  // log2segcount is the maximal x s.t. 2^x < segcount
+  unsigned log2segcount = 0;
+  while (1u << (log2segcount + 1) <= segcount) {
+    log2segcount++;
+  }
+
+  const uint16_t expected_search_range = 2 * 1u << log2segcount;
+  if (expected_search_range != search_range) {
+    return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
+  }
+
+  if (entry_selector != log2segcount) {
+    return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
+  }
+
+  const uint16_t expected_range_shift = segcountx2 - search_range;
+  if (range_shift != expected_range_shift) {
+    return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
+  }
+
+  std::vector<Subtable314Range> ranges(segcount);
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].end_range)) {
+      return OTS_FAILURE_MSG("Failed to read segment %d", i);
+    }
+  }
+
+  uint16_t padding;
+  if (!subtable.ReadU16(&padding)) {
+    return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
+  }
+  if (padding) {
+    return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
+  }
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].start_range)) {
+      return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
+    }
+  }
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadS16(&ranges[i].id_delta)) {
+      return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
+    }
+  }
+  for (unsigned i = 0; i < segcount; ++i) {
+    ranges[i].id_range_offset_offset = subtable.offset();
+    if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
+      return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
+    }
+
+    if (ranges[i].id_range_offset & 1) {
+      // Some font generators seem to put 65535 on id_range_offset
+      // for 0xFFFF-0xFFFF range.
+      // (e.g., many fonts in http://www.princexml.com/fonts/)
+      if (i == segcount - 1u) {
+        OTS_WARNING("bad id_range_offset");
+        ranges[i].id_range_offset = 0;
+        // The id_range_offset value in the transcoded font will not change
+        // since this table is not actually "transcoded" yet.
+      } else {
+        return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
+      }
+    }
+  }
+
+  // ranges must be ascending order, based on the end_code. Ranges may not
+  // overlap.
+  for (unsigned i = 1; i < segcount; ++i) {
+    if ((i == segcount - 1u) &&
+        (ranges[i - 1].start_range == 0xffff) &&
+        (ranges[i - 1].end_range == 0xffff) &&
+        (ranges[i].start_range == 0xffff) &&
+        (ranges[i].end_range == 0xffff)) {
+      // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
+      // We'll accept them as an exception.
+      OTS_WARNING("multiple 0xffff terminators found");
+      continue;
+    }
+
+    // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
+    // unsorted table...
+    if (ranges[i].end_range <= ranges[i - 1].end_range) {
+      return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
+    }
+    if (ranges[i].start_range <= ranges[i - 1].end_range) {
+      return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
+    }
+
+    // On many fonts, the value of {first, last}_char_index are incorrect.
+    // Fix them.
+    if (file->os2->first_char_index != 0xFFFF &&
+        ranges[i].start_range != 0xFFFF &&
+        file->os2->first_char_index > ranges[i].start_range) {
+      file->os2->first_char_index = ranges[i].start_range;
+    }
+    if (file->os2->last_char_index != 0xFFFF &&
+        ranges[i].end_range != 0xFFFF &&
+        file->os2->last_char_index < ranges[i].end_range) {
+      file->os2->last_char_index = ranges[i].end_range;
+    }
+  }
+
+  // The last range must end at 0xffff
+  if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
+    return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
+                           ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
+  }
+
+  // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
+  // each code-point defined in the table and make sure that they are all valid
+  // glyphs and that we don't access anything out-of-bounds.
+  for (unsigned i = 0; i < segcount; ++i) {
+    for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
+      const uint16_t code_point = static_cast<uint16_t>(cp);
+      if (ranges[i].id_range_offset == 0) {
+        // this is explictly allowed to overflow in the spec
+        const uint16_t glyph = code_point + ranges[i].id_delta;
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+        }
+      } else {
+        const uint16_t range_delta = code_point - ranges[i].start_range;
+        // this might seem odd, but it's true. The offset is relative to the
+        // location of the offset value itself.
+        const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
+                                         ranges[i].id_range_offset +
+                                         range_delta * 2;
+        // We need to be able to access a 16-bit value from this offset
+        if (glyph_id_offset + 1 >= length) {
+          return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
+        }
+        uint16_t glyph;
+        std::memcpy(&glyph, data + glyph_id_offset, 2);
+        glyph = ntohs(glyph);
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+        }
+      }
+    }
+  }
+
+  // We accept the table.
+  // TODO(yusukes): transcode the subtable.
+  if (platform == 3 && encoding == 0) {
+    file->cmap->subtable_3_0_4_data = data;
+    file->cmap->subtable_3_0_4_length = length;
+  } else if (platform == 3 && encoding == 1) {
+    file->cmap->subtable_3_1_4_data = data;
+    file->cmap->subtable_3_1_4_length = length;
+  } else if (platform == 0 && encoding == 3) {
+    file->cmap->subtable_0_3_4_data = data;
+    file->cmap->subtable_0_3_4_length = length;
+  } else {
+    return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
+  }
+
+  return true;
+}
+
+bool Parse31012(ots::OpenTypeFile *file,
+                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Format 12 tables are simple. We parse these and fully serialise them
+  // later.
+
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
+  }
+  uint32_t language = 0;
+  if (!subtable.ReadU32(&language)) {
+    return OTS_FAILURE_MSG("can't read format 12 subtable language");
+  }
+  if (language) {
+    return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
+  }
+
+  uint32_t num_groups = 0;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
+  }
+  if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
+    return OTS_FAILURE_MSG("bad format 12 subtable group count %d", num_groups);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_3_10_12;
+  groups.resize(num_groups);
+
+  for (unsigned i = 0; i < num_groups; ++i) {
+    if (!subtable.ReadU32(&groups[i].start_range) ||
+        !subtable.ReadU32(&groups[i].end_range) ||
+        !subtable.ReadU32(&groups[i].start_glyph_id)) {
+      return OTS_FAILURE_MSG("can't read format 12 subtable group");
+    }
+
+    if (groups[i].start_range > kUnicodeUpperLimit ||
+        groups[i].end_range > kUnicodeUpperLimit ||
+        groups[i].start_glyph_id > 0xFFFF) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
+                             groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+    }
+
+    // [0xD800, 0xDFFF] are surrogate code points.
+    if (groups[i].start_range >= 0xD800 &&
+        groups[i].start_range <= 0xDFFF) {
+      return OTS_FAILURE_MSG("format 12 subtable out of range group startCharCode (0x%4X)", groups[i].start_range);
+    }
+    if (groups[i].end_range >= 0xD800 &&
+        groups[i].end_range <= 0xDFFF) {
+      return OTS_FAILURE_MSG("format 12 subtable out of range group endCharCode (0x%4X)", groups[i].end_range);
+    }
+    if (groups[i].start_range < 0xD800 &&
+        groups[i].end_range > 0xDFFF) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group startCharCode (0x%4X) or endCharCode (0x%4X)",
+                             groups[i].start_range, groups[i].end_range);
+    }
+
+    // We assert that the glyph value is within range. Because of the range
+    // limits, above, we don't need to worry about overflow.
+    if (groups[i].end_range < groups[i].start_range) {
+      return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
+                             groups[i].end_range, groups[i].start_range);
+    }
+    if ((groups[i].end_range - groups[i].start_range) +
+        groups[i].start_glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
+    }
+  }
+
+  // the groups must be sorted by start code and may not overlap
+  for (unsigned i = 1; i < num_groups; ++i) {
+    if (groups[i].start_range <= groups[i - 1].start_range) {
+      return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
+                             groups[i].start_range, groups[i-1].start_range);
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
+                             groups[i].start_range, groups[i-1].end_range);
+    }
+  }
+
+  return true;
+}
+
+bool Parse31013(ots::OpenTypeFile *file,
+                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Format 13 tables are simple. We parse these and fully serialise them
+  // later.
+
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Bad cmap subtable length");
+  }
+  uint32_t language = 0;
+  if (!subtable.ReadU32(&language)) {
+    return OTS_FAILURE_MSG("Can't read cmap subtable language");
+  }
+  if (language) {
+    return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
+  }
+
+  uint32_t num_groups = 0;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
+  }
+
+  // We limit the number of groups in the same way as in 3.10.12 tables. See
+  // the comment there in
+  if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
+    return OTS_FAILURE_MSG("Bad number of groups (%d) in a cmap subtable", num_groups);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_3_10_13;
+  groups.resize(num_groups);
+
+  for (unsigned i = 0; i < num_groups; ++i) {
+    if (!subtable.ReadU32(&groups[i].start_range) ||
+        !subtable.ReadU32(&groups[i].end_range) ||
+        !subtable.ReadU32(&groups[i].start_glyph_id)) {
+      return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
+    }
+
+    // We conservatively limit all of the values to protect some parsers from
+    // overflows
+    if (groups[i].start_range > kUnicodeUpperLimit ||
+        groups[i].end_range > kUnicodeUpperLimit ||
+        groups[i].start_glyph_id > 0xFFFF) {
+      return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+    }
+
+    if (groups[i].start_glyph_id >= num_glyphs) {
+      return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
+    }
+  }
+
+  // the groups must be sorted by start code and may not overlap
+  for (unsigned i = 1; i < num_groups; ++i) {
+    if (groups[i].start_range <= groups[i - 1].start_range) {
+      return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
+    }
+  }
+
+  return true;
+}
+
+bool Parse0514(ots::OpenTypeFile *file,
+               const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  // Unicode Variation Selector table
+  ots::Buffer subtable(data, length);
+
+  // Format 14 tables are simple. We parse these and fully serialise them
+  // later.
+
+  // Skip format (USHORT) and length (ULONG)
+  if (!subtable.Skip(6)) {
+    return OTS_FAILURE_MSG("Can't read start of cmap subtable");
+  }
+
+  uint32_t num_records = 0;
+  if (!subtable.ReadU32(&num_records)) {
+    return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
+  }
+  if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
+    return OTS_FAILURE_MSG("Bad number of records (%d) in cmap subtable", num_records);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
+      = file->cmap->subtable_0_5_14;
+  records.resize(num_records);
+
+  for (unsigned i = 0; i < num_records; ++i) {
+    if (!subtable.ReadU24(&records[i].var_selector) ||
+        !subtable.ReadU32(&records[i].default_offset) ||
+        !subtable.ReadU32(&records[i].non_default_offset)) {
+      return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
+    }
+    // Checks the value of variation selector
+    if (!((records[i].var_selector >= kMongolianVSStart &&
+           records[i].var_selector <= kMongolianVSEnd) ||
+          (records[i].var_selector >= kVSStart &&
+           records[i].var_selector <= kVSEnd) ||
+          (records[i].var_selector >= kIVSStart &&
+           records[i].var_selector <= kIVSEnd))) {
+      return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
+    }
+    if (i > 0 &&
+        records[i-1].var_selector >= records[i].var_selector) {
+      return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
+    }
+
+    // Checks offsets
+    if (!records[i].default_offset && !records[i].non_default_offset) {
+      return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
+    }
+    if (records[i].default_offset &&
+        records[i].default_offset >= length) {
+      return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
+    }
+    if (records[i].non_default_offset &&
+        records[i].non_default_offset >= length) {
+      return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
+    }
+  }
+
+  for (unsigned i = 0; i < num_records; ++i) {
+    // Checks default UVS table
+    if (records[i].default_offset) {
+      subtable.set_offset(records[i].default_offset);
+      uint32_t num_ranges = 0;
+      if (!subtable.ReadU32(&num_ranges)) {
+        return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
+      }
+      if (!num_ranges || num_ranges > kMaxCMAPGroups) {
+        return OTS_FAILURE_MSG("number of ranges too high (%d > %d) in record %d", num_ranges, kMaxCMAPGroups, i);
+      }
+
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
+          = records[i].ranges;
+      ranges.resize(num_ranges);
+
+      for (unsigned j = 0; j < num_ranges; ++j) {
+        if (!subtable.ReadU24(&ranges[j].unicode_value) ||
+            !subtable.ReadU8(&ranges[j].additional_count)) {
+          return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
+        }
+        const uint32_t check_value =
+            ranges[j].unicode_value + ranges[j].additional_count;
+        if (ranges[j].unicode_value == 0 ||
+            ranges[j].unicode_value > kUnicodeUpperLimit ||
+            check_value > kUVSUpperLimit ||
+            (last_unicode_value &&
+             ranges[j].unicode_value <= last_unicode_value)) {
+          return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
+        }
+        last_unicode_value = check_value;
+      }
+    }
+
+    // Checks non default UVS table
+    if (records[i].non_default_offset) {
+      subtable.set_offset(records[i].non_default_offset);
+      uint32_t num_mappings = 0;
+      if (!subtable.ReadU32(&num_mappings)) {
+        return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
+      }
+      if (!num_mappings || num_mappings > kMaxCMAPGroups) {
+        return OTS_FAILURE_MSG("Number of mappings too high (%d) in variation selector record %d", num_mappings, i);
+      }
+
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
+          = records[i].mappings;
+      mappings.resize(num_mappings);
+
+      for (unsigned j = 0; j < num_mappings; ++j) {
+        if (!subtable.ReadU24(&mappings[j].unicode_value) ||
+            !subtable.ReadU16(&mappings[j].glyph_id)) {
+          return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
+        }
+        if (mappings[j].glyph_id == 0 ||
+            mappings[j].unicode_value == 0 ||
+            mappings[j].unicode_value > kUnicodeUpperLimit ||
+            (last_unicode_value &&
+             mappings[j].unicode_value <= last_unicode_value)) {
+          return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
+        }
+        last_unicode_value = mappings[j].unicode_value;
+      }
+    }
+  }
+
+  if (subtable.offset() != length) {
+    return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
+  }
+  file->cmap->subtable_0_5_14_length = subtable.offset();
+  return true;
+}
+
+bool Parse100(ots::OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Mac Roman table
+  ots::Buffer subtable(data, length);
+
+  if (!subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Bad cmap subtable");
+  }
+  uint16_t language = 0;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE_MSG("Can't read language in cmap subtable");
+  }
+  if (language) {
+    // simsun.ttf has non-zero language id.
+    OTS_WARNING("language id should be zero: %u", language);
+  }
+
+  file->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
+  for (size_t i = 0; i < kFormat0ArraySize; ++i) {
+    uint8_t glyph_id = 0;
+    if (!subtable.ReadU8(&glyph_id)) {
+      return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
+    }
+    file->cmap->subtable_1_0_0.push_back(glyph_id);
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_cmap_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->cmap = new OpenTypeCMAP;
+
+  uint16_t version = 0;
+  uint16_t num_tables = 0;
+  if (!table.ReadU16(&version) ||
+      !table.ReadU16(&num_tables)) {
+    return OTS_FAILURE_MSG("Can't read structure of cmap");
+  }
+
+  if (version != 0) {
+    return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
+  }
+  if (!num_tables) {
+    return OTS_FAILURE_MSG("No subtables in cmap!");
+  }
+
+  std::vector<CMAPSubtableHeader> subtable_headers;
+
+  // read the subtable headers
+  subtable_headers.reserve(num_tables);
+  for (unsigned i = 0; i < num_tables; ++i) {
+    CMAPSubtableHeader subt;
+
+    if (!table.ReadU16(&subt.platform) ||
+        !table.ReadU16(&subt.encoding) ||
+        !table.ReadU32(&subt.offset)) {
+      return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
+    }
+
+    subtable_headers.push_back(subt);
+  }
+
+  const size_t data_offset = table.offset();
+
+  // make sure that all the offsets are valid.
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
+    }
+    if (subtable_headers[i].offset < data_offset ||
+        subtable_headers[i].offset >= length) {
+      return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
+    }
+  }
+
+  // the format of the table is the first couple of bytes in the table. The
+  // length of the table is stored in a format-specific way.
+  for (unsigned i = 0; i < num_tables; ++i) {
+    table.set_offset(subtable_headers[i].offset);
+    if (!table.ReadU16(&subtable_headers[i].format)) {
+      return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
+    }
+
+    uint16_t len = 0;
+    uint16_t lang = 0;
+    switch (subtable_headers[i].format) {
+      case 0:
+      case 4:
+        if (!table.ReadU16(&len)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+        }
+        if (!table.ReadU16(&lang)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+        }
+        subtable_headers[i].length = len;
+        subtable_headers[i].language = lang;
+        break;
+      case 12:
+      case 13:
+        if (!table.Skip(2)) {
+          return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
+        }
+        if (!table.ReadU32(&subtable_headers[i].length)) {
+          return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
+        }
+        if (!table.ReadU32(&subtable_headers[i].language)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+        }
+        break;
+      case 14:
+        if (!table.ReadU32(&subtable_headers[i].length)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+        }
+        subtable_headers[i].language = 0;
+        break;
+      default:
+        subtable_headers[i].length = 0;
+        subtable_headers[i].language = 0;
+        break;
+    }
+  }
+
+  // check if the table is sorted first by platform ID, then by encoding ID.
+  uint32_t last_id = 0;
+  for (unsigned i = 0; i < num_tables; ++i) {
+    uint32_t current_id
+        = (subtable_headers[i].platform << 24)
+        + (subtable_headers[i].encoding << 16)
+        + subtable_headers[i].language;
+    if ((i != 0) && (last_id >= current_id)) {
+      return OTS_FAILURE_MSG("subtable %d with platform ID %d, encoding ID %d, language ID %d "
+                             "following subtable with platform ID %d, encoding ID %d, language ID %d",
+                             i,
+                             (uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id),
+                             (uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id));
+    }
+    last_id = current_id;
+  }
+
+  // Now, verify that all the lengths are sane
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (!subtable_headers[i].length) continue;
+    if (subtable_headers[i].length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
+    }
+    // We know that both the offset and length are < 1GB, so the following
+    // addition doesn't overflow
+    const uint32_t end_byte
+        = subtable_headers[i].offset + subtable_headers[i].length;
+    if (end_byte > length) {
+      return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
+    }
+  }
+
+  // check that the cmap subtables are not overlapping.
+  std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
+  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+  for (unsigned i = 0; i < num_tables; ++i) {
+    const uint32_t end_byte
+        = subtable_headers[i].offset + subtable_headers[i].length;
+
+    if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset,
+                                            end_byte)).second) {
+      // Sometimes Unicode table and MS table share exactly the same data.
+      // We'll allow this.
+      continue;
+    }
+    overlap_checker.push_back(
+        std::make_pair(subtable_headers[i].offset,
+                       static_cast<uint8_t>(1) /* start */));
+    overlap_checker.push_back(
+        std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */));
+  }
+  std::sort(overlap_checker.begin(), overlap_checker.end());
+  int overlap_count = 0;
+  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+    overlap_count += (overlap_checker[i].second ? 1 : -1);
+    if (overlap_count > 1) {
+      return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
+    }
+  }
+
+  // we grab the number of glyphs in the file from the maxp table to make sure
+  // that the character map isn't referencing anything beyound this range.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  // We only support a subset of the possible character map tables. Microsoft
+  // 'strongly recommends' that everyone supports the Unicode BMP table with
+  // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
+  //   Platform ID   Encoding ID  Format
+  //   0             0            4       (Unicode Default)
+  //   0             1            4       (Unicode 1.1)
+  //   0             3            4       (Unicode BMP)
+  //   0             3            12      (Unicode UCS-4)
+  //   0             5            14      (Unicode Variation Sequences)
+  //   1             0            0       (Mac Roman)
+  //   3             0            4       (MS Symbol)
+  //   3             1            4       (MS Unicode BMP)
+  //   3             10           12      (MS Unicode UCS-4)
+  //   3             10           13      (MS UCS-4 Fallback mapping)
+  //
+  // Note:
+  //  * 0-0-4 and 0-1-4 tables are (usually) written as a 3-1-4 table. If 3-1-4 table
+  //    also exists, the 0-0-4 or 0-1-4 tables are ignored.
+  //  * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table.
+  //    Some fonts which include 0-5-14 table seems to be required 0-3-4
+  //    table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists.
+  //  * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
+  //    exists, the 0-3-12 table is ignored.
+  //
+
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].platform == 0) {
+      // Unicode platform
+
+      if ((subtable_headers[i].encoding == 0 || subtable_headers[i].encoding == 1) &&
+          (subtable_headers[i].format == 4)) {
+        // parse and output the 0-0-4 and 0-1-4 tables as 3-1-4 table. Sometimes the 0-0-4
+        // table actually points to MS symbol data and thus should be parsed as
+        // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
+        // recovered in ots_cmap_serialise().
+        if (!ParseFormat4(file, 3, 1, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 4)) {
+        // parse and output the 0-3-4 table as 0-3-4 table.
+        if (!ParseFormat4(file, 0, 3, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 12)) {
+        // parse and output the 0-3-12 table as 3-10-12 table.
+        if (!Parse31012(file, data + subtable_headers[i].offset,
+                        subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 5) &&
+                 (subtable_headers[i].format == 14)) {
+        if (!Parse0514(file, data + subtable_headers[i].offset,
+                       subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
+        }
+      }
+    } else if (subtable_headers[i].platform == 1) {
+      // Mac platform
+
+      if ((subtable_headers[i].encoding == 0) &&
+          (subtable_headers[i].format == 0)) {
+        // parse and output the 1-0-0 table.
+        if (!Parse100(file, data + subtable_headers[i].offset,
+                      subtable_headers[i].length)) {
+          return OTS_FAILURE();
+        }
+      }
+    } else if (subtable_headers[i].platform == 3) {
+      // MS platform
+
+      switch (subtable_headers[i].encoding) {
+        case 0:
+        case 1:
+          if (subtable_headers[i].format == 4) {
+            // parse 3-0-4 or 3-1-4 table.
+            if (!ParseFormat4(file, subtable_headers[i].platform,
+                          subtable_headers[i].encoding,
+                          data + subtable_headers[i].offset,
+                          subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          }
+          break;
+        case 10:
+          if (subtable_headers[i].format == 12) {
+            file->cmap->subtable_3_10_12.clear();
+            if (!Parse31012(file, data + subtable_headers[i].offset,
+                            subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          } else if (subtable_headers[i].format == 13) {
+            file->cmap->subtable_3_10_13.clear();
+            if (!Parse31013(file, data + subtable_headers[i].offset,
+                            subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          }
+          break;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ots_cmap_should_serialise(OpenTypeFile *file) {
+  return file->cmap != NULL;
+}
+
+bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
+  const bool have_034 = file->cmap->subtable_0_3_4_data != NULL;
+  const bool have_0514 = file->cmap->subtable_0_5_14.size() != 0;
+  const bool have_100 = file->cmap->subtable_1_0_0.size() != 0;
+  const bool have_304 = file->cmap->subtable_3_0_4_data != NULL;
+  // MS Symbol and MS Unicode tables should not co-exist.
+  // See the comment above in 0-0-4 parser.
+  const bool have_314 = (!have_304) && file->cmap->subtable_3_1_4_data;
+  const bool have_31012 = file->cmap->subtable_3_10_12.size() != 0;
+  const bool have_31013 = file->cmap->subtable_3_10_13.size() != 0;
+  const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
+                                 static_cast<uint16_t>(have_0514) +
+                                 static_cast<uint16_t>(have_100) +
+                                 static_cast<uint16_t>(have_304) +
+                                 static_cast<uint16_t>(have_314) +
+                                 static_cast<uint16_t>(have_31012) +
+                                 static_cast<uint16_t>(have_31013);
+  const off_t table_start = out->Tell();
+
+  // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
+  // (e.g., old fonts for Mac). We don't support them.
+  if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
+    return OTS_FAILURE_MSG("no supported subtables were found");
+  }
+
+  if (!out->WriteU16(0) ||
+      !out->WriteU16(num_subtables)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t record_offset = out->Tell();
+  if (!out->Pad(num_subtables * 8)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t offset_034 = out->Tell();
+  if (have_034) {
+    if (!out->Write(file->cmap->subtable_0_3_4_data,
+                    file->cmap->subtable_0_3_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_0514 = out->Tell();
+  if (have_0514) {
+    const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
+        = file->cmap->subtable_0_5_14;
+    const unsigned num_records = records.size();
+    if (!out->WriteU16(14) ||
+        !out->WriteU32(file->cmap->subtable_0_5_14_length) ||
+        !out->WriteU32(num_records)) {
+      return OTS_FAILURE();
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (!out->WriteU24(records[i].var_selector) ||
+          !out->WriteU32(records[i].default_offset) ||
+          !out->WriteU32(records[i].non_default_offset)) {
+        return OTS_FAILURE();
+      }
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (records[i].default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges
+            = records[i].ranges;
+        const unsigned num_ranges = ranges.size();
+        if (!out->Seek(records[i].default_offset + offset_0514) ||
+            !out->WriteU32(num_ranges)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_ranges; ++j) {
+          if (!out->WriteU24(ranges[j].unicode_value) ||
+              !out->WriteU8(ranges[j].additional_count)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+      if (records[i].non_default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings
+            = records[i].mappings;
+        const unsigned num_mappings = mappings.size();
+        if (!out->Seek(records[i].non_default_offset + offset_0514) ||
+            !out->WriteU32(num_mappings)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_mappings; ++j) {
+          if (!out->WriteU24(mappings[j].unicode_value) ||
+              !out->WriteU16(mappings[j].glyph_id)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+    }
+  }
+
+  const off_t offset_100 = out->Tell();
+  if (have_100) {
+    if (!out->WriteU16(0) ||  // format
+        !out->WriteU16(6 + kFormat0ArraySize) ||  // length
+        !out->WriteU16(0)) {  // language
+      return OTS_FAILURE();
+    }
+    if (!out->Write(&(file->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_304 = out->Tell();
+  if (have_304) {
+    if (!out->Write(file->cmap->subtable_3_0_4_data,
+                    file->cmap->subtable_3_0_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_314 = out->Tell();
+  if (have_314) {
+    if (!out->Write(file->cmap->subtable_3_1_4_data,
+                    file->cmap->subtable_3_1_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_31012 = out->Tell();
+  if (have_31012) {
+    std::vector<OpenTypeCMAPSubtableRange> &groups
+        = file->cmap->subtable_3_10_12;
+    const unsigned num_groups = groups.size();
+    if (!out->WriteU16(12) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(num_groups * 12 + 16) ||
+        !out->WriteU32(0) ||
+        !out->WriteU32(num_groups)) {
+      return OTS_FAILURE();
+    }
+
+    for (unsigned i = 0; i < num_groups; ++i) {
+      if (!out->WriteU32(groups[i].start_range) ||
+          !out->WriteU32(groups[i].end_range) ||
+          !out->WriteU32(groups[i].start_glyph_id)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  const off_t offset_31013 = out->Tell();
+  if (have_31013) {
+    std::vector<OpenTypeCMAPSubtableRange> &groups
+        = file->cmap->subtable_3_10_13;
+    const unsigned num_groups = groups.size();
+    if (!out->WriteU16(13) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(num_groups * 12 + 16) ||
+        !out->WriteU32(0) ||
+        !out->WriteU32(num_groups)) {
+      return OTS_FAILURE();
+    }
+
+    for (unsigned i = 0; i < num_groups; ++i) {
+      if (!out->WriteU32(groups[i].start_range) ||
+          !out->WriteU32(groups[i].end_range) ||
+          !out->WriteU32(groups[i].start_glyph_id)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  const off_t table_end = out->Tell();
+  // We might have hanging bytes from the above's checksum which the OTSStream
+  // then merges into the table of offsets.
+  OTSStream::ChecksumState saved_checksum = out->SaveChecksumState();
+  out->ResetChecksum();
+
+  // Now seek back and write the table of offsets
+  if (!out->Seek(record_offset)) {
+    return OTS_FAILURE();
+  }
+
+  if (have_034) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(3) ||
+        !out->WriteU32(offset_034 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_0514) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(5) ||
+        !out->WriteU32(offset_0514 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_100) {
+    if (!out->WriteU16(1) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(offset_100 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_304) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(offset_304 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_314) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(1) ||
+        !out->WriteU32(offset_314 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_31012) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(10) ||
+        !out->WriteU32(offset_31012 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_31013) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(10) ||
+        !out->WriteU32(offset_31013 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (!out->Seek(table_end)) {
+    return OTS_FAILURE();
+  }
+  out->RestoreChecksum(saved_checksum);
+
+  return true;
+}
+
+void ots_cmap_free(OpenTypeFile *file) {
+  delete file->cmap;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cmap.h b/third_party/ots/src/cmap.h
new file mode 100644
index 0000000..5b09556
--- /dev/null
+++ b/third_party/ots/src/cmap.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CMAP_H_
+#define OTS_CMAP_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCMAPSubtableRange {
+  uint32_t start_range;
+  uint32_t end_range;
+  uint32_t start_glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRange {
+  uint32_t unicode_value;
+  uint8_t additional_count;
+};
+
+struct OpenTypeCMAPSubtableVSMapping {
+  uint32_t unicode_value;
+  uint16_t glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRecord {
+  uint32_t var_selector;
+  uint32_t default_offset;
+  uint32_t non_default_offset;
+  std::vector<OpenTypeCMAPSubtableVSRange> ranges;
+  std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
+};
+
+struct OpenTypeCMAP {
+  OpenTypeCMAP()
+      : subtable_0_3_4_data(NULL),
+        subtable_0_3_4_length(0),
+        subtable_0_5_14_length(0),
+        subtable_3_0_4_data(NULL),
+        subtable_3_0_4_length(0),
+        subtable_3_1_4_data(NULL),
+        subtable_3_1_4_length(0) {
+  }
+
+  // Platform 0, Encoding 3, Format 4, Unicode BMP table.
+  const uint8_t *subtable_0_3_4_data;
+  size_t subtable_0_3_4_length;
+
+  // Platform 0, Encoding 5, Format 14, Unicode Variation Sequence table.
+  size_t subtable_0_5_14_length;
+  std::vector<OpenTypeCMAPSubtableVSRecord> subtable_0_5_14;
+
+  // Platform 3, Encoding 0, Format 4, MS Symbol table.
+  const uint8_t *subtable_3_0_4_data;
+  size_t subtable_3_0_4_length;
+  // Platform 3, Encoding 1, Format 4, MS Unicode BMP table.
+  const uint8_t *subtable_3_1_4_data;
+  size_t subtable_3_1_4_length;
+
+  // Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
+  std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
+  // Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
+  std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
+  // Platform 1, Encoding 0, Format 0, Mac Roman table.
+  std::vector<uint8_t> subtable_1_0_0;
+};
+
+}  // namespace ots
+
+#endif
diff --git a/third_party/ots/src/cvt.cc b/third_party/ots/src/cvt.cc
new file mode 100644
index 0000000..f83ad0e
--- /dev/null
+++ b/third_party/ots/src/cvt.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cvt.h"
+
+// cvt - Control Value Table
+// http://www.microsoft.com/typography/otspec/cvt.htm
+
+#define TABLE_NAME "cvt"
+
+namespace ots {
+
+bool ots_cvt_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeCVT *cvt = new OpenTypeCVT;
+  file->cvt = cvt;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("Length (%d) > 120K");  // almost all cvt tables are less than 4k bytes.
+  }
+
+  if (length % 2 != 0) {
+    return OTS_FAILURE_MSG("Uneven cvt length (%d)", length);
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Length too high");
+  }
+
+  cvt->data = data;
+  cvt->length = length;
+  return true;
+}
+
+bool ots_cvt_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) {
+    return false;  // this table is not for CFF fonts.
+  }
+  return file->cvt != NULL;
+}
+
+bool ots_cvt_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeCVT *cvt = file->cvt;
+
+  if (!out->Write(cvt->data, cvt->length)) {
+    return OTS_FAILURE_MSG("Failed to write CVT table");
+  }
+
+  return true;
+}
+
+void ots_cvt_free(OpenTypeFile *file) {
+  delete file->cvt;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cvt.h b/third_party/ots/src/cvt.h
new file mode 100644
index 0000000..3c25f06
--- /dev/null
+++ b/third_party/ots/src/cvt.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CVT_H_
+#define OTS_CVT_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCVT {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_CVT_H_
diff --git a/third_party/ots/src/fpgm.cc b/third_party/ots/src/fpgm.cc
new file mode 100644
index 0000000..eba03e7
--- /dev/null
+++ b/third_party/ots/src/fpgm.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpgm.h"
+
+// fpgm - Font Program
+// http://www.microsoft.com/typography/otspec/fpgm.htm
+
+#define TABLE_NAME "fpgm"
+
+namespace ots {
+
+bool ots_fpgm_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeFPGM *fpgm = new OpenTypeFPGM;
+  file->fpgm = fpgm;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("length (%ld) > 120", length);  // almost all fpgm tables are less than 5k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Bad fpgm length");
+  }
+
+  fpgm->data = data;
+  fpgm->length = length;
+  return true;
+}
+
+bool ots_fpgm_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->fpgm != NULL;
+}
+
+bool ots_fpgm_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeFPGM *fpgm = file->fpgm;
+
+  if (!out->Write(fpgm->data, fpgm->length)) {
+    return OTS_FAILURE_MSG("Failed to write fpgm");
+  }
+
+  return true;
+}
+
+void ots_fpgm_free(OpenTypeFile *file) {
+  delete file->fpgm;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/fpgm.h b/third_party/ots/src/fpgm.h
new file mode 100644
index 0000000..8fabac3
--- /dev/null
+++ b/third_party/ots/src/fpgm.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_FPGM_H_
+#define OTS_FPGM_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeFPGM {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_FPGM_H_
diff --git a/third_party/ots/src/gasp.cc b/third_party/ots/src/gasp.cc
new file mode 100644
index 0000000..1e2327f
--- /dev/null
+++ b/third_party/ots/src/gasp.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gasp.h"
+
+// gasp - Grid-fitting And Scan-conversion Procedure
+// http://www.microsoft.com/typography/otspec/gasp.htm
+
+#define TABLE_NAME "gasp"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->gasp; \
+    file->gasp = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_gasp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeGASP *gasp = new OpenTypeGASP;
+  file->gasp = gasp;
+
+  uint16_t num_ranges = 0;
+  if (!table.ReadU16(&gasp->version) ||
+      !table.ReadU16(&num_ranges)) {
+    return OTS_FAILURE_MSG("Failed to read table header");
+  }
+
+  if (gasp->version > 1) {
+    // Lots of Linux fonts have bad version numbers...
+    DROP_THIS_TABLE("bad version: %u", gasp->version);
+    return true;
+  }
+
+  if (num_ranges == 0) {
+    DROP_THIS_TABLE("num_ranges is zero");
+    return true;
+  }
+
+  gasp->gasp_ranges.reserve(num_ranges);
+  for (unsigned i = 0; i < num_ranges; ++i) {
+    uint16_t max_ppem = 0;
+    uint16_t behavior = 0;
+    if (!table.ReadU16(&max_ppem) ||
+        !table.ReadU16(&behavior)) {
+      return OTS_FAILURE_MSG("Failed to read subrange %d", i);
+    }
+    if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
+      // The records in the gaspRange[] array must be sorted in order of
+      // increasing rangeMaxPPEM value.
+      DROP_THIS_TABLE("ranges are not sorted");
+      return true;
+    }
+    if ((i == num_ranges - 1u) &&  // never underflow.
+        (max_ppem != 0xffffu)) {
+      DROP_THIS_TABLE("The last record should be 0xFFFF as a sentinel value "
+                  "for rangeMaxPPEM");
+      return true;
+    }
+
+    if (behavior >> 8) {
+      OTS_WARNING("undefined bits are used: %x", behavior);
+      // mask undefined bits.
+      behavior &= 0x000fu;
+    }
+
+    if (gasp->version == 0 && (behavior >> 2) != 0) {
+      OTS_WARNING("changed the version number to 1");
+      gasp->version = 1;
+    }
+
+    gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
+  }
+
+  return true;
+}
+
+bool ots_gasp_should_serialise(OpenTypeFile *file) {
+  return file->gasp != NULL;
+}
+
+bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeGASP *gasp = file->gasp;
+
+  const uint16_t num_ranges = static_cast<uint16_t>(gasp->gasp_ranges.size());
+  if (num_ranges != gasp->gasp_ranges.size() ||
+      !out->WriteU16(gasp->version) ||
+      !out->WriteU16(num_ranges)) {
+    return OTS_FAILURE_MSG("failed to write gasp header");
+  }
+
+  for (uint16_t i = 0; i < num_ranges; ++i) {
+    if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
+        !out->WriteU16(gasp->gasp_ranges[i].second)) {
+      return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_gasp_free(OpenTypeFile *file) {
+  delete file->gasp;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gasp.h b/third_party/ots/src/gasp.h
new file mode 100644
index 0000000..48d7e7c
--- /dev/null
+++ b/third_party/ots/src/gasp.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GASP_H_
+#define OTS_GASP_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGASP {
+  uint16_t version;
+  // A array of (max PPEM, GASP behavior) pairs.
+  std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GASP_H_
diff --git a/third_party/ots/src/gdef.cc b/third_party/ots/src/gdef.cc
new file mode 100644
index 0000000..4553d58
--- /dev/null
+++ b/third_party/ots/src/gdef.cc
@@ -0,0 +1,388 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gdef.h"
+
+#include <limits>
+#include <vector>
+
+#include "gpos.h"
+#include "gsub.h"
+#include "layout.h"
+#include "maxp.h"
+
+// GDEF - The Glyph Definition Table
+// http://www.microsoft.com/typography/otspec/gdef.htm
+
+#define TABLE_NAME "GDEF"
+
+namespace {
+
+// The maximum class value in class definition tables.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+// The maximum class value in the glyph class definision table.
+const uint16_t kMaxGlyphClassDefValue = 4;
+// The maximum format number of caret value tables.
+// We don't support format 3 for now. See the comment in
+// ParseLigCaretListTable() for the reason.
+const uint16_t kMaxCaretValueFormat = 2;
+
+bool ParseGlyphClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                             size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(file, data, length, num_glyphs,
+                                 kMaxGlyphClassDefValue);
+}
+
+bool ParseAttachListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                          size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read gdef header");
+  }
+  const unsigned attach_points_end =
+      2 * static_cast<unsigned>(glyph_count) + 4;
+  if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad glyph count in gdef");
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < attach_points_end) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %u", glyph_count);
+  }
+
+  std::vector<uint16_t> attach_points;
+  attach_points.resize(glyph_count);
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    if (!subtable.ReadU16(&attach_points[i])) {
+      return OTS_FAILURE_MSG("Can't read attachment point %d", i);
+    }
+    if (attach_points[i] >= length ||
+        attach_points[i] < attach_points_end) {
+      return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]);
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Bad coverage table");
+  }
+
+  // Parse attach point table
+  for (unsigned i = 0; i < attach_points.size(); ++i) {
+    subtable.set_offset(attach_points[i]);
+    uint16_t point_count = 0;
+    if (!subtable.ReadU16(&point_count)) {
+      return OTS_FAILURE_MSG("Can't read point count %d", i);
+    }
+    if (point_count == 0) {
+      return OTS_FAILURE_MSG("zero point count %d", i);
+    }
+    uint16_t last_point_index = 0;
+    uint16_t point_index = 0;
+    for (unsigned j = 0; j < point_count; ++j) {
+      if (!subtable.ReadU16(&point_index)) {
+        return OTS_FAILURE_MSG("Can't read point index %d in point %d", j, i);
+      }
+      // Contour point indeces are in increasing numerical order
+      if (last_point_index != 0 && last_point_index >= point_index) {
+        return OTS_FAILURE_MSG("bad contour indeces: %u >= %u",
+                    last_point_index, point_index);
+      }
+      last_point_index = point_index;
+    }
+  }
+  return true;
+}
+
+bool ParseLigCaretListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                            size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t offset_coverage = 0;
+  uint16_t lig_glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_glyph_count)) {
+    return OTS_FAILURE_MSG("Can't read caret structure");
+  }
+  const unsigned lig_glyphs_end =
+      2 * static_cast<unsigned>(lig_glyph_count) + 4;
+  if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad caret structure");
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < lig_glyphs_end) {
+    return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage);
+  }
+  if (lig_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad ligature glyph count: %u", lig_glyph_count);
+  }
+
+  std::vector<uint16_t> lig_glyphs;
+  lig_glyphs.resize(lig_glyph_count);
+  for (unsigned i = 0; i < lig_glyph_count; ++i) {
+    if (!subtable.ReadU16(&lig_glyphs[i])) {
+      return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i);
+    }
+    if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
+      return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Can't parse caret coverage table");
+  }
+
+  // Parse ligature glyph table
+  for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
+    subtable.set_offset(lig_glyphs[i]);
+    uint16_t caret_count = 0;
+    if (!subtable.ReadU16(&caret_count)) {
+      return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i);
+    }
+    if (caret_count == 0) {
+      return OTS_FAILURE_MSG("bad caret value count: %u", caret_count);
+    }
+
+    std::vector<uint16_t> caret_value_offsets;
+    caret_value_offsets.resize(caret_count);
+    unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2;
+    for (unsigned j = 0; j < caret_count; ++j) {
+      if (!subtable.ReadU16(&caret_value_offsets[j])) {
+        return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i);
+      }
+      if (caret_value_offsets[j] >= length || caret_value_offsets[j] < caret_value_offsets_end) {
+        return OTS_FAILURE_MSG("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
+      }
+    }
+
+    // Parse caret values table
+    for (unsigned j = 0; j < caret_count; ++j) {
+      subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]);
+      uint16_t caret_format = 0;
+      if (!subtable.ReadU16(&caret_format)) {
+        return OTS_FAILURE_MSG("Can't read caret values table %d in glyph %d", j, i);
+      }
+      // TODO(bashi): We only support caret value format 1 and 2 for now
+      // because there are no fonts which contain caret value format 3
+      // as far as we investigated.
+      if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
+        return OTS_FAILURE_MSG("bad caret value format: %u", caret_format);
+      }
+      // CaretValueFormats contain a 2-byte field which could be
+      // arbitrary value.
+      if (!subtable.Skip(2)) {
+        return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i);
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkAttachClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                  size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(file, data, length, num_glyphs, kMaxClassDefValue);
+}
+
+bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t format = 0;
+  uint16_t mark_set_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&mark_set_count)) {
+    return OTS_FAILURE_MSG("Can' read mark glyph table structure");
+  }
+  if (format != 1) {
+    return OTS_FAILURE_MSG("bad mark glyph set table format: %u", format);
+  }
+
+  const unsigned mark_sets_end = 2 * static_cast<unsigned>(mark_set_count) + 4;
+  if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark_set %d", mark_sets_end);
+  }
+  for (unsigned i = 0; i < mark_set_count; ++i) {
+    uint32_t offset_coverage = 0;
+    if (!subtable.ReadU32(&offset_coverage)) {
+      return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i);
+    }
+    if (offset_coverage >= length ||
+        offset_coverage < mark_sets_end) {
+      return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i);
+    }
+  }
+  file->gdef->num_mark_glyph_sets = mark_set_count;
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gdef->data = 0; \
+    file->gdef->length = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_gdef_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Grab the number of glyphs in the file from the maxp table to check
+  // GlyphIDs in GDEF table.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF");
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  Buffer table(data, length);
+
+  OpenTypeGDEF *gdef = new OpenTypeGDEF;
+  file->gdef = gdef;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+  if (version < 0x00010000 || version == 0x00010001) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (version >= 0x00010002) {
+    gdef->version_2 = true;
+  }
+
+  uint16_t offset_glyph_class_def = 0;
+  uint16_t offset_attach_list = 0;
+  uint16_t offset_lig_caret_list = 0;
+  uint16_t offset_mark_attach_class_def = 0;
+  if (!table.ReadU16(&offset_glyph_class_def) ||
+      !table.ReadU16(&offset_attach_list) ||
+      !table.ReadU16(&offset_lig_caret_list) ||
+      !table.ReadU16(&offset_mark_attach_class_def)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+  uint16_t offset_mark_glyph_sets_def = 0;
+  if (gdef->version_2) {
+    if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
+      DROP_THIS_TABLE("Incomplete table");
+      return true;
+    }
+  }
+
+  unsigned gdef_header_end = 4 + 4 * 2;
+  if (gdef->version_2)
+    gdef_header_end += 2;
+
+  // Parse subtables
+  if (offset_glyph_class_def) {
+    if (offset_glyph_class_def >= length ||
+        offset_glyph_class_def < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to glyph classes");
+      return true;
+    }
+    if (!ParseGlyphClassDefTable(file, data + offset_glyph_class_def,
+                                 length - offset_glyph_class_def,
+                                 num_glyphs)) {
+      DROP_THIS_TABLE("Invalid glyph classes");
+      return true;
+    }
+    gdef->has_glyph_class_def = true;
+  }
+
+  if (offset_attach_list) {
+    if (offset_attach_list >= length ||
+        offset_attach_list < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to attachment list");
+      return true;
+    }
+    if (!ParseAttachListTable(file, data + offset_attach_list,
+                              length - offset_attach_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE("Invalid attachment list");
+      return true;
+    }
+  }
+
+  if (offset_lig_caret_list) {
+    if (offset_lig_caret_list >= length ||
+        offset_lig_caret_list < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to ligature caret list");
+      return true;
+    }
+    if (!ParseLigCaretListTable(file, data + offset_lig_caret_list,
+                              length - offset_lig_caret_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE("Invalid ligature caret list");
+      return true;
+    }
+  }
+
+  if (offset_mark_attach_class_def) {
+    if (offset_mark_attach_class_def >= length ||
+        offset_mark_attach_class_def < gdef_header_end) {
+      return OTS_FAILURE_MSG("Invalid offset to mark attachment list");
+    }
+    if (!ParseMarkAttachClassDefTable(file,
+                                      data + offset_mark_attach_class_def,
+                                      length - offset_mark_attach_class_def,
+                                      num_glyphs)) {
+      DROP_THIS_TABLE("Invalid mark attachment list");
+      return true;
+    }
+    gdef->has_mark_attachment_class_def = true;
+  }
+
+  if (offset_mark_glyph_sets_def) {
+    if (offset_mark_glyph_sets_def >= length ||
+        offset_mark_glyph_sets_def < gdef_header_end) {
+      return OTS_FAILURE_MSG("invalid offset to mark glyph sets");
+    }
+    if (!ParseMarkGlyphSetsDefTable(file,
+                                    data + offset_mark_glyph_sets_def,
+                                    length - offset_mark_glyph_sets_def,
+                                    num_glyphs)) {
+      DROP_THIS_TABLE("Invalid mark glyph sets");
+      return true;
+    }
+    gdef->has_mark_glyph_sets_def = true;
+  }
+  gdef->data = data;
+  gdef->length = length;
+  return true;
+}
+
+bool ots_gdef_should_serialise(OpenTypeFile *file) {
+  return file->gdef != NULL && file->gdef->data != NULL;
+}
+
+bool ots_gdef_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gdef->data, file->gdef->length)) {
+    return OTS_FAILURE_MSG("Failed to write GDEF table");
+  }
+
+  return true;
+}
+
+void ots_gdef_free(OpenTypeFile *file) {
+  delete file->gdef;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gdef.h b/third_party/ots/src/gdef.h
new file mode 100644
index 0000000..f46f419
--- /dev/null
+++ b/third_party/ots/src/gdef.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GDEF_H_
+#define OTS_GDEF_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGDEF {
+  OpenTypeGDEF()
+      : version_2(false),
+        has_glyph_class_def(false),
+        has_mark_attachment_class_def(false),
+        has_mark_glyph_sets_def(false),
+        num_mark_glyph_sets(0),
+        data(NULL),
+        length(0) {
+  }
+
+  bool version_2;
+  bool has_glyph_class_def;
+  bool has_mark_attachment_class_def;
+  bool has_mark_glyph_sets_def;
+  uint16_t num_mark_glyph_sets;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/glyf.cc b/third_party/ots/src/glyf.cc
new file mode 100644
index 0000000..3579397
--- /dev/null
+++ b/third_party/ots/src/glyf.cc
@@ -0,0 +1,298 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "glyf.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "head.h"
+#include "loca.h"
+#include "maxp.h"
+
+// glyf - Glyph Data
+// http://www.microsoft.com/typography/otspec/glyf.htm
+
+#define TABLE_NAME "glyf"
+
+namespace {
+
+bool ParseFlagsForSimpleGlyph(ots::OpenTypeFile *file,
+                              ots::Buffer *table,
+                              uint32_t gly_length,
+                              uint32_t num_flags,
+                              uint32_t *flags_count_logical,
+                              uint32_t *flags_count_physical,
+                              uint32_t *xy_coordinates_length) {
+  uint8_t flag = 0;
+  if (!table->ReadU8(&flag)) {
+    return OTS_FAILURE_MSG("Can't read flag");
+  }
+
+  uint32_t delta = 0;
+  if (flag & (1u << 1)) {  // x-Short
+    ++delta;
+  } else if (!(flag & (1u << 4))) {
+    delta += 2;
+  }
+
+  if (flag & (1u << 2)) {  // y-Short
+    ++delta;
+  } else if (!(flag & (1u << 5))) {
+    delta += 2;
+  }
+
+  if (flag & (1u << 3)) {  // repeat
+    if (*flags_count_logical + 1 >= num_flags) {
+      return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
+    }
+    uint8_t repeat = 0;
+    if (!table->ReadU8(&repeat)) {
+      return OTS_FAILURE_MSG("Can't read repeat value");
+    }
+    if (repeat == 0) {
+      return OTS_FAILURE_MSG("Zero repeat");
+    }
+    delta += (delta * repeat);
+
+    *flags_count_logical += repeat;
+    if (*flags_count_logical >= num_flags) {
+      return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
+    }
+    ++(*flags_count_physical);
+  }
+
+  if ((flag & (1u << 6)) || (flag & (1u << 7))) {  // reserved flags
+    return OTS_FAILURE_MSG("Bad flag value (%d)", flag);
+  }
+
+  *xy_coordinates_length += delta;
+  if (gly_length < *xy_coordinates_length) {
+    return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
+  }
+
+  return true;
+}
+
+bool ParseSimpleGlyph(ots::OpenTypeFile *file, const uint8_t *data,
+                      ots::Buffer *table, int16_t num_contours,
+                      uint32_t gly_offset, uint32_t gly_length,
+                      uint32_t *new_size) {
+  ots::OpenTypeGLYF *glyf = file->glyf;
+
+  // read the end-points array
+  uint16_t num_flags = 0;
+  for (int i = 0; i < num_contours; ++i) {
+    uint16_t tmp_index = 0;
+    if (!table->ReadU16(&tmp_index)) {
+      return OTS_FAILURE_MSG("Can't read contour index %d", i);
+    }
+    if (tmp_index == 0xffffu) {
+      return OTS_FAILURE_MSG("Bad contour index %d", i);
+    }
+    // check if the indices are monotonically increasing
+    if (i && (tmp_index + 1 <= num_flags)) {
+      return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
+    }
+    num_flags = tmp_index + 1;
+  }
+
+  uint16_t bytecode_length = 0;
+  if (!table->ReadU16(&bytecode_length)) {
+    return OTS_FAILURE_MSG("Can't read bytecode length");
+  }
+  if ((file->maxp->version_1) &&
+      (file->maxp->max_size_glyf_instructions < bytecode_length)) {
+    return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
+  }
+
+  const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
+  if (gly_length < (gly_header_length + bytecode_length)) {
+    return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
+  }
+
+  glyf->iov.push_back(std::make_pair(
+      data + gly_offset,
+      static_cast<size_t>(gly_header_length + bytecode_length)));
+
+  if (!table->Skip(bytecode_length)) {
+    return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
+  }
+
+  uint32_t flags_count_physical = 0;  // on memory
+  uint32_t xy_coordinates_length = 0;
+  for (uint32_t flags_count_logical = 0;
+       flags_count_logical < num_flags;
+       ++flags_count_logical, ++flags_count_physical) {
+    if (!ParseFlagsForSimpleGlyph(file,
+                                  table,
+                                  gly_length,
+                                  num_flags,
+                                  &flags_count_logical,
+                                  &flags_count_physical,
+                                  &xy_coordinates_length)) {
+      return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
+    }
+  }
+
+  if (gly_length < (gly_header_length + bytecode_length +
+                    flags_count_physical + xy_coordinates_length)) {
+    return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
+  }
+
+  if (gly_length - (gly_header_length + bytecode_length +
+                    flags_count_physical + xy_coordinates_length) > 3) {
+    // We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
+    // zero-padded length.
+    return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
+  }
+
+  glyf->iov.push_back(std::make_pair(
+      data + gly_offset + gly_header_length + bytecode_length,
+      static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
+
+  *new_size
+      = gly_header_length + flags_count_physical + xy_coordinates_length + bytecode_length;
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_glyf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  if (!file->maxp || !file->loca || !file->head) {
+    return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
+  }
+
+  OpenTypeGLYF *glyf = new OpenTypeGLYF;
+  file->glyf = glyf;
+
+  const unsigned num_glyphs = file->maxp->num_glyphs;
+  std::vector<uint32_t> &offsets = file->loca->offsets;
+
+  if (offsets.size() != num_glyphs + 1) {
+    return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
+  }
+
+  std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
+  uint32_t current_offset = 0;
+
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    const unsigned gly_offset = offsets[i];
+    // The LOCA parser checks that these values are monotonic
+    const unsigned gly_length = offsets[i + 1] - offsets[i];
+    if (!gly_length) {
+      // this glyph has no outline (e.g. the space charactor)
+      resulting_offsets[i] = current_offset;
+      continue;
+    }
+
+    if (gly_offset >= length) {
+      return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
+    }
+    // Since these are unsigned types, the compiler is not allowed to assume
+    // that they never overflow.
+    if (gly_offset + gly_length < gly_offset) {
+      return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
+    }
+    if (gly_offset + gly_length > length) {
+      return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
+    }
+
+    table.set_offset(gly_offset);
+    int16_t num_contours, xmin, ymin, xmax, ymax;
+    if (!table.ReadS16(&num_contours) ||
+        !table.ReadS16(&xmin) ||
+        !table.ReadS16(&ymin) ||
+        !table.ReadS16(&xmax) ||
+        !table.ReadS16(&ymax)) {
+      return OTS_FAILURE_MSG("Can't read glyph %d header", i);
+    }
+
+    if (num_contours <= -2) {
+      // -2, -3, -4, ... are reserved for future use.
+      return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
+    }
+
+    // workaround for fonts in http://www.princexml.com/fonts/
+    if ((xmin == 32767) &&
+        (xmax == -32767) &&
+        (ymin == 32767) &&
+        (ymax == -32767)) {
+      OTS_WARNING("bad xmin/xmax/ymin/ymax values");
+      xmin = xmax = ymin = ymax = 0;
+    }
+
+    if (xmin > xmax || ymin > ymax) {
+      return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
+    }
+
+    unsigned new_size = 0;
+    if (num_contours >= 0) {
+      // this is a simple glyph and might contain bytecode
+      if (!ParseSimpleGlyph(file, data, &table,
+                            num_contours, gly_offset, gly_length, &new_size)) {
+        return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
+      }
+    } else {
+      // it's a composite glyph without any bytecode. Enqueue the whole thing
+      glyf->iov.push_back(std::make_pair(data + gly_offset,
+                                         static_cast<size_t>(gly_length)));
+      new_size = gly_length;
+    }
+
+    resulting_offsets[i] = current_offset;
+    // glyphs must be four byte aligned
+    // TODO(yusukes): investigate whether this padding is really necessary.
+    //                Which part of the spec requires this?
+    const unsigned padding = (4 - (new_size & 3)) % 4;
+    if (padding) {
+      glyf->iov.push_back(std::make_pair(
+          reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
+          static_cast<size_t>(padding)));
+      new_size += padding;
+    }
+    current_offset += new_size;
+  }
+  resulting_offsets[num_glyphs] = current_offset;
+
+  const uint16_t max16 = std::numeric_limits<uint16_t>::max();
+  if ((*std::max_element(resulting_offsets.begin(),
+                         resulting_offsets.end()) >= (max16 * 2u)) &&
+      (file->head->index_to_loc_format != 1)) {
+    OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
+    file->head->index_to_loc_format = 1;
+  }
+
+  file->loca->offsets = resulting_offsets;
+  return true;
+}
+
+bool ots_glyf_should_serialise(OpenTypeFile *file) {
+  return file->glyf != NULL;
+}
+
+bool ots_glyf_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeGLYF *glyf = file->glyf;
+
+  for (unsigned i = 0; i < glyf->iov.size(); ++i) {
+    if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
+      return OTS_FAILURE_MSG("Falied to write glyph %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_glyf_free(OpenTypeFile *file) {
+  delete file->glyf;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/glyf.h b/third_party/ots/src/glyf.h
new file mode 100644
index 0000000..9a8baf5
--- /dev/null
+++ b/third_party/ots/src/glyf.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GLYF_H_
+#define OTS_GLYF_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGLYF {
+  std::vector<std::pair<const uint8_t*, size_t> > iov;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GLYF_H_
diff --git a/third_party/ots/src/gpos.cc b/third_party/ots/src/gpos.cc
new file mode 100644
index 0000000..a2b9687
--- /dev/null
+++ b/third_party/ots/src/gpos.cc
@@ -0,0 +1,828 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpos.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GPOS - The Glyph Positioning Table
+// http://www.microsoft.com/typography/otspec/gpos.htm
+
+#define TABLE_NAME "GPOS"
+
+namespace {
+
+enum GPOS_TYPE {
+  GPOS_TYPE_SINGLE_ADJUSTMENT = 1,
+  GPOS_TYPE_PAIR_ADJUSTMENT = 2,
+  GPOS_TYPE_CURSIVE_ATTACHMENT = 3,
+  GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4,
+  GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5,
+  GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6,
+  GPOS_TYPE_CONTEXT_POSITIONING = 7,
+  GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8,
+  GPOS_TYPE_EXTENSION_POSITIONING = 9,
+  GPOS_TYPE_RESERVED = 10
+};
+
+// The size of gpos header.
+const unsigned kGposHeaderSize = 10;
+// The maximum format number for anchor tables.
+const uint16_t kMaxAnchorFormat = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+
+// Lookup type parsers.
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length);
+bool ParsePairAdjustment(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length);
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length);
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length);
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length);
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
+  {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment},
+  {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment},
+  {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment},
+  {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
+  {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
+  {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
+  {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning},
+  {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
+  {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning}
+};
+
+const ots::LookupSubtableParser kGposLookupSubtableParser = {
+  arraysize(kGposTypeParsers),
+  GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
+};
+
+// Shared Tables: ValueRecord, Anchor Table, and MarkArray
+
+bool ParseValueRecord(const ots::OpenTypeFile *file,
+                      ots::Buffer* subtable, const uint8_t *data,
+                      const size_t length, const uint16_t value_format) {
+  // Check existence of adjustment fields.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> i) & 0x1) {
+      // Just read the field since these fileds could take an arbitrary values.
+      if (!subtable->Skip(2)) {
+        return OTS_FAILURE_MSG("Failed to read value reacord component");
+      }
+    }
+  }
+
+  // Check existence of offsets to device table.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> (i + 4)) & 0x1) {
+      uint16_t offset = 0;
+      if (!subtable->ReadU16(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read value record offset");
+      }
+      if (offset) {
+        // TODO(bashi): Is it possible that device tables locate before
+        // this record? No fonts contain such offset AKAIF.
+        if (offset >= length) {
+          return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length);
+        }
+        if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
+          return OTS_FAILURE_MSG("Failed to parse device table in value record");
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseAnchorTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  // Read format and skip 2 2-byte fields that could be arbitrary values.
+  if (!subtable.ReadU16(&format) ||
+      !subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Faled to read anchor table");
+  }
+
+  if (format == 0 || format > kMaxAnchorFormat) {
+    return OTS_FAILURE_MSG("Bad Anchor table format %d", format);
+  }
+
+  // Format 2 and 3 has additional fields.
+  if (format == 2) {
+    // Format 2 provides an index to a glyph contour point, which will take
+    // arbitrary value.
+    uint16_t anchor_point = 0;
+    if (!subtable.ReadU16(&anchor_point)) {
+      return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table");
+    }
+  } else if (format == 3) {
+    uint16_t offset_x_device = 0;
+    uint16_t offset_y_device = 0;
+    if (!subtable.ReadU16(&offset_x_device) ||
+        !subtable.ReadU16(&offset_y_device)) {
+      return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table");
+    }
+    const unsigned format_end = static_cast<unsigned>(10);
+    if (offset_x_device) {
+      if (offset_x_device < format_end || offset_x_device >= length) {
+        return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device);
+      }
+      if (!ots::ParseDeviceTable(file, data + offset_x_device,
+                                 length - offset_x_device)) {
+        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+      }
+    }
+    if (offset_y_device) {
+      if (offset_y_device < format_end || offset_y_device >= length) {
+        return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device);
+      }
+      if (!ots::ParseDeviceTable(file, data + offset_y_device,
+                                 length - offset_y_device)) {
+        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkArrayTable(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t mark_count = 0;
+  if (!subtable.ReadU16(&mark_count)) {
+    return OTS_FAILURE_MSG("Can't read mark table length");
+  }
+
+  // MarkRecord consists of 4-bytes.
+  const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2;
+  if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark table length");
+  }
+  for (unsigned i = 0; i < mark_count; ++i) {
+    uint16_t class_value = 0;
+    uint16_t offset_mark_anchor = 0;
+    if (!subtable.ReadU16(&class_value) ||
+        !subtable.ReadU16(&offset_mark_anchor)) {
+      return OTS_FAILURE_MSG("Can't read mark table %d", i);
+    }
+    // |class_value| may take arbitrary values including 0 here so we don't
+    // check the value.
+    if (offset_mark_anchor < mark_records_end ||
+        offset_mark_anchor >= length) {
+      return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i);
+    }
+    if (!ParseAnchorTable(file, data + offset_mark_anchor,
+                          length - offset_mark_anchor)) {
+      return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 1:
+// Single Adjustment Positioning Subtable
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                           const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format)) {
+    return OTS_FAILURE_MSG("Can't read single adjustment information");
+  }
+
+  if (format == 1) {
+    // Format 1 exactly one value record.
+    if (!ParseValueRecord(file, &subtable, data, length, value_format)) {
+      return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
+    }
+  } else if (format == 2) {
+    uint16_t value_count = 0;
+    if (!subtable.ReadU16(&value_count)) {
+      return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
+    }
+    for (unsigned i = 0; i < value_count; ++i) {
+      if (!ParseValueRecord(file, &subtable, data, length, value_format)) {
+        return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
+      }
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage);
+  }
+
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
+  }
+
+  return true;
+}
+
+bool ParsePairSetTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t value_format1,
+                       const uint16_t value_format2,
+                       const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t value_count = 0;
+  if (!subtable.ReadU16(&value_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair set table structure");
+  }
+  for (unsigned i = 0; i < value_count; ++i) {
+    // Check pair value record.
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i);
+    }
+    if (glyph_id >= num_glyphs) {
+      return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
+    }
+    if (!ParseValueRecord(file, &subtable, data, length, value_format1)) {
+      return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
+    }
+    if (!ParseValueRecord(file, &subtable, data, length, value_format2)) {
+      return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
+    }
+  }
+  return true;
+}
+
+bool ParsePairPosFormat1(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos table structure");
+  }
+
+  uint16_t pair_set_count = 0;
+  if (!subtable.ReadU16(&pair_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos set count");
+  }
+
+  const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10;
+  if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end);
+  }
+  for (unsigned i = 0; i < pair_set_count; ++i) {
+    uint16_t pair_set_offset = 0;
+    if (!subtable.ReadU16(&pair_set_offset)) {
+      return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i);
+    }
+    if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
+      return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i);
+    }
+    // Check pair set tables
+    if (!ParsePairSetTable(file, data + pair_set_offset, length - pair_set_offset,
+                           value_format1, value_format2,
+                           num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair set table %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParsePairPosFormat2(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure");
+  }
+
+  uint16_t offset_class_def1 = 0;
+  uint16_t offset_class_def2 = 0;
+  uint16_t class1_count = 0;
+  uint16_t class2_count = 0;
+  if (!subtable.ReadU16(&offset_class_def1) ||
+      !subtable.ReadU16(&offset_class_def2) ||
+      !subtable.ReadU16(&class1_count) ||
+      !subtable.ReadU16(&class2_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
+  }
+
+  // Check class 1 records.
+  for (unsigned i = 0; i < class1_count; ++i) {
+    // Check class 2 records.
+    for (unsigned j = 0; j < class2_count; ++j) {
+      if (value_format1 && !ParseValueRecord(file, &subtable, data, length,
+                                             value_format1)) {
+        return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
+      }
+      if (value_format2 && !ParseValueRecord(file, &subtable, data, length,
+                                             value_format2)) {
+        return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
+      }
+    }
+  }
+
+  // Check class definition tables.
+  if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
+      offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
+    return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def1,
+                               length - offset_class_def1,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table 1");
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def2,
+                               length - offset_class_def2,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table 2");
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Pair Adjustment Positioning Subtable
+bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                         const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format1 = 0;
+  uint16_t value_format2 = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format1) ||
+      !subtable.ReadU16(&value_format2)) {
+    return OTS_FAILURE_MSG("Failed to read pair adjustment structure");
+  }
+
+  if (format == 1) {
+    if (!ParsePairPosFormat1(file, data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
+    }
+  } else if (format == 2) {
+    if (!ParsePairPosFormat2(file, data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair format 2");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad pos pair format %d", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+// Lookup Type 3
+// Cursive Attachment Positioning Subtable
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data,
+                            const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t entry_exit_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&entry_exit_count)) {
+    return OTS_FAILURE_MSG("Failed to read cursive attachment structure");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad cursive attachment format %d", format);
+  }
+
+  // Check entry exit records.
+  const unsigned entry_exit_records_end =
+      2 * static_cast<unsigned>(entry_exit_count) + 6;
+  if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad entry exit record end %d", entry_exit_records_end);
+  }
+  for (unsigned i = 0; i < entry_exit_count; ++i) {
+    uint16_t offset_entry_anchor = 0;
+    uint16_t offset_exit_anchor = 0;
+    if (!subtable.ReadU16(&offset_entry_anchor) ||
+        !subtable.ReadU16(&offset_exit_anchor)) {
+      return OTS_FAILURE_MSG("Can't read entry exit record %d", i);
+    }
+    // These offsets could be NULL.
+    if (offset_entry_anchor) {
+      if (offset_entry_anchor < entry_exit_records_end ||
+          offset_entry_anchor >= length) {
+        return OTS_FAILURE_MSG("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i);
+      }
+      if (!ParseAnchorTable(file, data + offset_entry_anchor,
+                            length - offset_entry_anchor)) {
+        return OTS_FAILURE_MSG("Failed to parse entry anchor table in entry exit record %d", i);
+      }
+    }
+    if (offset_exit_anchor) {
+      if (offset_exit_anchor < entry_exit_records_end ||
+         offset_exit_anchor >= length) {
+        return OTS_FAILURE_MSG("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i);
+      }
+      if (!ParseAnchorTable(file, data + offset_exit_anchor,
+                            length - offset_exit_anchor)) {
+        return OTS_FAILURE_MSG("Failed to parse exit anchor table in entry exit record %d", i);
+      }
+    }
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
+  }
+
+  return true;
+}
+
+bool ParseAnchorArrayTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t record_count = 0;
+  if (!subtable.ReadU16(&record_count)) {
+    return OTS_FAILURE_MSG("Can't read anchor array length");
+  }
+
+  const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) *
+      static_cast<unsigned>(class_count) + 2;
+  if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end);
+  }
+  for (unsigned i = 0; i < record_count; ++i) {
+    for (unsigned j = 0; j < class_count; ++j) {
+      uint16_t offset_record = 0;
+      if (!subtable.ReadU16(&offset_record)) {
+        return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i);
+      }
+      // |offset_record| could be NULL.
+      if (offset_record) {
+        if (offset_record < anchor_array_end || offset_record >= length) {
+          return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i);
+        }
+        if (!ParseAnchorTable(file, data + offset_record,
+                              length - offset_record)) {
+          return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i);
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseLigatureArrayTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length,
+                             const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligature count");
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature_attach = 0;
+    if (!subtable.ReadU16(&offset_ligature_attach)) {
+      return OTS_FAILURE_MSG("Can't read ligature offset %d", i);
+    }
+    if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
+      return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i);
+    }
+    if (!ParseAnchorArrayTable(file, data + offset_ligature_attach,
+                               length - offset_ligature_attach, class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i);
+    }
+  }
+  return true;
+}
+
+// Common parser for Lookup Type 4, 5 and 6.
+bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length,
+                                    const GPOS_TYPE type) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage1 = 0;
+  uint16_t offset_coverage2 = 0;
+  uint16_t class_count = 0;
+  uint16_t offset_mark_array = 0;
+  uint16_t offset_type_specific_array = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage1) ||
+      !subtable.ReadU16(&offset_coverage2) ||
+      !subtable.ReadU16(&class_count) ||
+      !subtable.ReadU16(&offset_mark_array) ||
+      !subtable.ReadU16(&offset_type_specific_array)) {
+    return OTS_FAILURE_MSG("Failed to read mark attachment subtable header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format);
+  }
+
+  const unsigned header_end = static_cast<unsigned>(subtable.offset());
+  if (header_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end);
+  }
+  if (offset_coverage1 < header_end || offset_coverage1 >= length) {
+    return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage1,
+                               length - offset_coverage1,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse converge 1 table");
+  }
+  if (offset_coverage2 < header_end || offset_coverage2 >= length) {
+    return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage2,
+                               length - offset_coverage2,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table 2");
+  }
+
+  if (offset_mark_array < header_end || offset_mark_array >= length) {
+    return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array);
+  }
+  if (!ParseMarkArrayTable(file, data + offset_mark_array,
+                           length - offset_mark_array, class_count)) {
+    return OTS_FAILURE_MSG("Failed to parse mark array");
+  }
+
+  if (offset_type_specific_array < header_end ||
+      offset_type_specific_array >= length) {
+    return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array);
+  }
+  if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT ||
+      type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) {
+    if (!ParseAnchorArrayTable(file, data + offset_type_specific_array,
+                               length - offset_type_specific_array,
+                               class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse anchor array");
+    }
+  } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) {
+    if (!ParseLigatureArrayTable(file, data + offset_type_specific_array,
+                                 length - offset_type_specific_array,
+                                 class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature array");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad attachment type %d", type);
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// MarkToBase Attachment Positioning Subtable
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
+}
+
+// Lookup Type 5:
+// MarkToLigature Attachment Positioning Subtable
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
+}
+
+// Lookup Type 6:
+// MarkToMark Attachment Positioning Subtable
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
+}
+
+// Lookup Type 7:
+// Contextual Positioning Subtables
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs,
+                                   file->gpos->num_lookups);
+}
+
+// Lookup Type 8:
+// Chaining Contexual Positioning Subtable
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length) {
+  return ots::ParseChainingContextSubtable(file, data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gpos->num_lookups);
+}
+
+// Lookup Type 9:
+// Extension Positioning
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGposLookupSubtableParser);
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gpos->data = 0; \
+    file->gpos->length = 0; \
+  } while (0)
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid GPOS table and
+// OTS will drop their GPOS table.
+//
+// # invalid delta format in device table
+// samanata.ttf
+//
+// # bad size range in device table
+// Sarai_07.ttf
+//
+// # bad offset to PairSetTable
+// chandas1-2.ttf
+//
+// # bad offset to FeatureTable
+// glrso12.ttf
+// gllr12.ttf
+// glbo12.ttf
+// glb12.ttf
+// glro12.ttf
+// glbso12.ttf
+// glrc12.ttf
+// glrsc12.ttf
+// glbs12.ttf
+// glrs12.ttf
+// glr12.ttf
+//
+// # ScriptRecords aren't sorted by tag
+// Garogier_unhinted.otf
+//
+// # bad start coverage index in CoverageFormat2
+// AndBasR.ttf
+// CharisSILB.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILR.ttf
+// DoulosSILR.ttf
+// GenBasBI.ttf
+// GenBasI.ttf
+// GenBkBasI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// Padauk-Bold.ttf
+// Padauk.ttf
+//
+// # Contour point indexes aren't sorted
+// Arial Unicode.ttf
+
+bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing GPOS table requires num_glyphs which is contained in maxp table.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("missing maxp table needed in GPOS");
+  }
+
+  Buffer table(data, length);
+
+  OpenTypeGPOS *gpos = new OpenTypeGPOS;
+  file->gpos = gpos;
+
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
+
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGposLookupSubtableParser,
+                              &gpos->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
+  }
+
+  uint16_t num_features = 0;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gpos->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
+  }
+
+  if (offset_script_list) {
+    if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
+  }
+
+  gpos->data = data;
+  gpos->length = length;
+  return true;
+}
+
+bool ots_gpos_should_serialise(OpenTypeFile *file) {
+  return file->gpos != NULL && file->gpos->data != NULL;
+}
+
+bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gpos->data, file->gpos->length)) {
+    return OTS_FAILURE_MSG("Failed to write GPOS table");
+  }
+
+  return true;
+}
+
+void ots_gpos_free(OpenTypeFile *file) {
+  delete file->gpos;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gpos.h b/third_party/ots/src/gpos.h
new file mode 100644
index 0000000..3a4034f
--- /dev/null
+++ b/third_party/ots/src/gpos.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GPOS_H_
+#define OTS_GPOS_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGPOS {
+  OpenTypeGPOS()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPOS table
+  uint16_t num_lookups;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/gsub.cc b/third_party/ots/src/gsub.cc
new file mode 100644
index 0000000..af31144
--- /dev/null
+++ b/third_party/ots/src/gsub.cc
@@ -0,0 +1,685 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gsub.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GSUB - The Glyph Substitution Table
+// http://www.microsoft.com/typography/otspec/gsub.htm
+
+#define TABLE_NAME "GSUB"
+
+namespace {
+
+// The GSUB header size
+const size_t kGsubHeaderSize = 4 + 3 * 2;
+
+enum GSUB_TYPE {
+  GSUB_TYPE_SINGLE = 1,
+  GSUB_TYPE_MULTIPLE = 2,
+  GSUB_TYPE_ALTERNATE = 3,
+  GSUB_TYPE_LIGATURE = 4,
+  GSUB_TYPE_CONTEXT = 5,
+  GSUB_TYPE_CHANGING_CONTEXT = 6,
+  GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
+  GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
+  GSUB_TYPE_RESERVED = 9
+};
+
+// Lookup type parsers.
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+      const uint8_t *data, const size_t length);
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length);
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
+  {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
+  {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
+  {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
+  {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
+  {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
+  {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
+  {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
+  {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
+    ParseReverseChainingContextSingleSubstitution}
+};
+
+const ots::LookupSubtableParser kGsubLookupSubtableParser = {
+  arraysize(kGsubTypeParsers),
+  GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
+};
+
+// Lookup Type 1:
+// Single Substitution Subtable
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE_MSG("Failed to read single subst table header");
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  if (format == 1) {
+    // Parse SingleSubstFormat1
+    int16_t delta_glyph_id = 0;
+    if (!subtable.ReadS16(&delta_glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table");
+    }
+    if (std::abs(delta_glyph_id) >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
+    }
+  } else if (format == 2) {
+    // Parse SingleSubstFormat2
+    uint16_t glyph_count = 0;
+    if (!subtable.ReadU16(&glyph_count)) {
+      return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table");
+    }
+    if (glyph_count > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
+    }
+    for (unsigned i = 0; i < glyph_count; ++i) {
+      uint16_t substitute = 0;
+      if (!subtable.ReadU16(&substitute)) {
+        return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i);
+      }
+      if (substitute >= num_glyphs) {
+        return OTS_FAILURE_MSG("too large substitute: %u", substitute);
+      }
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad single subst table format %d", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseSequenceTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE_MSG("Failedt o read substitution %d in sequence table", i);
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad subsitution (%d) %d > %d", i, substitute, num_glyphs);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Multiple Substitution Subtable
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&sequence_count)) {
+    return OTS_FAILURE_MSG("Can't read header of multiple subst table");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned sequence_end = static_cast<unsigned>(6) +
+      sequence_count * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end);
+  }
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    uint16_t offset_sequence = 0;
+    if (!subtable.ReadU16(&offset_sequence)) {
+      return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i);
+    }
+    if (offset_sequence < sequence_end || offset_sequence >= length) {
+      return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i);
+    }
+    if (!ParseSequenceTable(file, data + offset_sequence, length - offset_sequence,
+                            num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse sequence table %d", i);
+    }
+  }
+
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseAlternateSetTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read alternate set header");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t alternate = 0;
+    if (!subtable.ReadU16(&alternate)) {
+      return OTS_FAILURE_MSG("Can't read alternate %d", i);
+    }
+    if (alternate >= num_glyphs) {
+      return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
+    }
+  }
+  return true;
+}
+
+// Lookup Type 3:
+// Alternate Substitution Subtable
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t alternate_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&alternate_set_count)) {
+    return OTS_FAILURE_MSG("Can't read alternate subst header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned alternate_set_end = static_cast<unsigned>(6) +
+      alternate_set_count * 2;
+  if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end);
+  }
+  for (unsigned i = 0; i < alternate_set_count; ++i) {
+    uint16_t offset_alternate_set = 0;
+    if (!subtable.ReadU16(&offset_alternate_set)) {
+      return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i);
+    }
+    if (offset_alternate_set < alternate_set_end ||
+        offset_alternate_set >= length) {
+      return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i);
+    }
+    if (!ParseAlternateSetTable(file, data + offset_alternate_set,
+                                length - offset_alternate_set,
+                                num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse alternate set");
+    }
+  }
+
+  if (offset_coverage < alternate_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseLigatureTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lig_glyph = 0;
+  uint16_t comp_count = 0;
+
+  if (!subtable.ReadU16(&lig_glyph) ||
+      !subtable.ReadU16(&comp_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligatuer table header");
+  }
+
+  if (lig_glyph >= num_glyphs) {
+    return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
+  }
+  if (comp_count == 0 || comp_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad component count of %d", comp_count);
+  }
+  for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
+    uint16_t component = 0;
+    if (!subtable.ReadU16(&component)) {
+      return OTS_FAILURE_MSG("Can't read ligature component %d", i);
+    }
+    if (component >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
+    }
+  }
+
+  return true;
+}
+
+bool ParseLigatureSetTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
+  }
+
+  const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
+  if (ligature_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end);
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature = 0;
+    if (!subtable.ReadU16(&offset_ligature)) {
+      return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
+    }
+    if (offset_ligature < ligature_end || offset_ligature >= length) {
+      return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
+    }
+    if (!ParseLigatureTable(file, data + offset_ligature, length - offset_ligature,
+                            num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// Ligature Substitution Subtable
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t lig_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligature substitution header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned ligature_set_end = static_cast<unsigned>(6) +
+      lig_set_count * 2;
+  if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
+  }
+  for (unsigned i = 0; i < lig_set_count; ++i) {
+    uint16_t offset_ligature_set = 0;
+    if (!subtable.ReadU16(&offset_ligature_set)) {
+      return OTS_FAILURE_MSG("Can't read ligature set offset %d", i);
+    }
+    if (offset_ligature_set < ligature_set_end ||
+        offset_ligature_set >= length) {
+      return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i);
+    }
+    if (!ParseLigatureSetTable(file, data + offset_ligature_set,
+                               length - offset_ligature_set, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature set %d", i);
+    }
+  }
+
+  if (offset_coverage < ligature_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+// Lookup Type 5:
+// Contextual Substitution Subtable
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs,
+                                   file->gsub->num_lookups);
+}
+
+// Lookup Type 6:
+// Chaining Contextual Substitution Subtable
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length) {
+  return ots::ParseChainingContextSubtable(file, data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gsub->num_lookups);
+}
+
+// Lookup Type 7:
+// Extension Substition
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGsubLookupSubtableParser);
+}
+
+// Lookup Type 8:
+// Reverse Chaining Contexual Single Substitution Subtable
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE_MSG("Failed to read reverse chaining header");
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  uint16_t backtrack_glyph_count = 0;
+  if (!subtable.ReadU16(&backtrack_glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table");
+  }
+  if (backtrack_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count);
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_glyph_count);
+  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i);
+    }
+    offsets_backtrack.push_back(offset);
+  }
+
+  uint16_t lookahead_glyph_count = 0;
+  if (!subtable.ReadU16(&lookahead_glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read look ahead glyph count");
+  }
+  if (lookahead_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad look ahead glyph count %d", lookahead_glyph_count);
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_glyph_count);
+  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Can't read look ahead offset %d", i);
+    }
+    offsets_lookahead.push_back(offset);
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count of %d", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i);
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
+    }
+  }
+
+  const unsigned substitute_end = static_cast<unsigned>(10) +
+      (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
+  if (substitute_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table");
+  }
+
+  if (offset_coverage < substitute_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table");
+  }
+
+  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+    if (offsets_backtrack[i] < substitute_end ||
+        offsets_backtrack[i] >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i],
+                                 length - offsets_backtrack[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+    if (offsets_lookahead[i] < substitute_end ||
+        offsets_lookahead[i] >= length) {
+      return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i],
+                                 length - offsets_lookahead[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gsub->data = 0; \
+    file->gsub->length = 0; \
+  } while (0)
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid values in GSUB table.
+// OTS will drop their GSUB table.
+//
+// # too large substitute (value is 0xFFFF)
+// kaiu.ttf
+// mingliub2.ttf
+// mingliub1.ttf
+// mingliub0.ttf
+// GraublauWeb.otf
+// GraublauWebBold.otf
+//
+// # too large alternate (value is 0xFFFF)
+// ManchuFont.ttf
+//
+// # bad offset to lang sys table (NULL offset)
+// DejaVuMonoSansBold.ttf
+// DejaVuMonoSansBoldOblique.ttf
+// DejaVuMonoSansOblique.ttf
+// DejaVuSansMono-BoldOblique.ttf
+// DejaVuSansMono-Oblique.ttf
+// DejaVuSansMono-Bold.ttf
+//
+// # bad start coverage index
+// GenBasBI.ttf
+// GenBasI.ttf
+// AndBasR.ttf
+// GenBkBasI.ttf
+// CharisSILR.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILB.ttf
+// DoulosSILR.ttf
+// CharisSILBI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// GenBkBasBI.ttf
+// GenBasB.ttf
+// GenBasR.ttf
+//
+// # glyph range is overlapping
+// KacstTitleL.ttf
+// KacstDecorative.ttf
+// KacstTitle.ttf
+// KacstArt.ttf
+// KacstPoster.ttf
+// KacstQurn.ttf
+// KacstDigital.ttf
+// KacstBook.ttf
+// KacstFarsi.ttf
+
+bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing gsub table requires |file->maxp->num_glyphs|
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
+  }
+
+  Buffer table(data, length);
+
+  OpenTypeGSUB *gsub = new OpenTypeGSUB;
+  file->gsub = gsub;
+
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
+
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGsubLookupSubtableParser,
+                              &gsub->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
+  }
+
+  uint16_t num_features = 0;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gsub->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
+  }
+
+  if (offset_script_list) {
+    if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
+  }
+
+  gsub->data = data;
+  gsub->length = length;
+  return true;
+}
+
+bool ots_gsub_should_serialise(OpenTypeFile *file) {
+  return file->gsub != NULL && file->gsub->data != NULL;
+}
+
+bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gsub->data, file->gsub->length)) {
+    return OTS_FAILURE_MSG("Failed to write GSUB table");
+  }
+
+  return true;
+}
+
+void ots_gsub_free(OpenTypeFile *file) {
+  delete file->gsub;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gsub.h b/third_party/ots/src/gsub.h
new file mode 100644
index 0000000..f6f8cd3
--- /dev/null
+++ b/third_party/ots/src/gsub.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GSUB_H_
+#define OTS_GSUB_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGSUB {
+  OpenTypeGSUB()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPSUB table
+  uint16_t num_lookups;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GSUB_H_
+
diff --git a/third_party/ots/src/hdmx.cc b/third_party/ots/src/hdmx.cc
new file mode 100644
index 0000000..098802b
--- /dev/null
+++ b/third_party/ots/src/hdmx.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hdmx.h"
+#include "head.h"
+#include "maxp.h"
+
+// hdmx - Horizontal Device Metrics
+// http://www.microsoft.com/typography/otspec/hdmx.htm
+
+#define TABLE_NAME "hdmx"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->hdmx; \
+    file->hdmx = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_hdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->hdmx = new OpenTypeHDMX;
+  OpenTypeHDMX * const hdmx = file->hdmx;
+
+  if (!file->head || !file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx");
+  }
+
+  if ((file->head->flags & 0x14) == 0) {
+    // http://www.microsoft.com/typography/otspec/recom.htm
+    DROP_THIS_TABLE("the table should not be present when bit 2 and 4 of the "
+                    "head->flags are not set");
+    return true;
+  }
+
+  int16_t num_recs;
+  if (!table.ReadU16(&hdmx->version) ||
+      !table.ReadS16(&num_recs) ||
+      !table.ReadS32(&hdmx->size_device_record)) {
+    return OTS_FAILURE_MSG("Failed to read hdmx header");
+  }
+  if (hdmx->version != 0) {
+    DROP_THIS_TABLE("bad version: %u", hdmx->version);
+    return true;
+  }
+  if (num_recs <= 0) {
+    DROP_THIS_TABLE("bad num_recs: %d", num_recs);
+    return true;
+  }
+  const int32_t actual_size_device_record = file->maxp->num_glyphs + 2;
+  if (hdmx->size_device_record < actual_size_device_record) {
+    DROP_THIS_TABLE("bad hdmx->size_device_record: %d", hdmx->size_device_record);
+    return true;
+  }
+
+  hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
+  if (hdmx->pad_len > 3) {
+    return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len);
+  }
+
+  uint8_t last_pixel_size = 0;
+  hdmx->records.reserve(num_recs);
+  for (int i = 0; i < num_recs; ++i) {
+    OpenTypeHDMXDeviceRecord rec;
+
+    if (!table.ReadU8(&rec.pixel_size) ||
+        !table.ReadU8(&rec.max_width)) {
+      return OTS_FAILURE_MSG("Failed to read hdmx record %d", i);
+    }
+    if ((i != 0) &&
+        (rec.pixel_size <= last_pixel_size)) {
+      DROP_THIS_TABLE("records are not sorted");
+      return true;
+    }
+    last_pixel_size = rec.pixel_size;
+
+    rec.widths.reserve(file->maxp->num_glyphs);
+    for (unsigned j = 0; j < file->maxp->num_glyphs; ++j) {
+      uint8_t width;
+      if (!table.ReadU8(&width)) {
+        return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i);
+      }
+      rec.widths.push_back(width);
+    }
+
+    if ((hdmx->pad_len > 0) &&
+        !table.Skip(hdmx->pad_len)) {
+      return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len);
+    }
+
+    hdmx->records.push_back(rec);
+  }
+
+  return true;
+}
+
+bool ots_hdmx_should_serialise(OpenTypeFile *file) {
+  if (!file->hdmx) return false;
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return true;
+}
+
+bool ots_hdmx_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeHDMX * const hdmx = file->hdmx;
+
+  const int16_t num_recs = static_cast<int16_t>(hdmx->records.size());
+  if (hdmx->records.size() >
+          static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
+      !out->WriteU16(hdmx->version) ||
+      !out->WriteS16(num_recs) ||
+      !out->WriteS32(hdmx->size_device_record)) {
+    return OTS_FAILURE_MSG("Failed to write hdmx header");
+  }
+
+  for (int16_t i = 0; i < num_recs; ++i) {
+    const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
+    if (!out->Write(&rec.pixel_size, 1) ||
+        !out->Write(&rec.max_width, 1) ||
+        !out->Write(&rec.widths[0], rec.widths.size())) {
+      return OTS_FAILURE_MSG("Failed to write hdmx record %d", i);
+    }
+    if ((hdmx->pad_len > 0) &&
+        !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
+      return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len);
+    }
+  }
+
+  return true;
+}
+
+void ots_hdmx_free(OpenTypeFile *file) {
+  delete file->hdmx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/hdmx.h b/third_party/ots/src/hdmx.h
new file mode 100644
index 0000000..9ec2124
--- /dev/null
+++ b/third_party/ots/src/hdmx.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HDMX_H_
+#define OTS_HDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHDMXDeviceRecord {
+  uint8_t pixel_size;
+  uint8_t max_width;
+  std::vector<uint8_t> widths;
+};
+
+struct OpenTypeHDMX {
+  uint16_t version;
+  int32_t size_device_record;
+  int32_t pad_len;
+  std::vector<OpenTypeHDMXDeviceRecord> records;
+};
+
+}  // namespace ots
+
+#endif
diff --git a/third_party/ots/src/head.cc b/third_party/ots/src/head.cc
new file mode 100644
index 0000000..dcd234d
--- /dev/null
+++ b/third_party/ots/src/head.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "head.h"
+
+#include <cstring>
+
+// head - Font Header
+// http://www.microsoft.com/typography/otspec/head.htm
+
+#define TABLE_NAME "head"
+
+namespace ots {
+
+bool ots_head_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->head = new OpenTypeHEAD;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU32(&file->head->revision)) {
+    return OTS_FAILURE_MSG("Failed to read head header");
+  }
+
+  if (version >> 16 != 1) {
+    return OTS_FAILURE_MSG("Bad head table version of %d", version);
+  }
+
+  // Skip the checksum adjustment
+  if (!table.Skip(4)) {
+    return OTS_FAILURE_MSG("Failed to read checksum");
+  }
+
+  uint32_t magic;
+  if (!table.ReadTag(&magic) ||
+      std::memcmp(&magic, "\x5F\x0F\x3C\xF5", 4)) {
+    return OTS_FAILURE_MSG("Failed to read font magic number");
+  }
+
+  if (!table.ReadU16(&file->head->flags)) {
+    return OTS_FAILURE_MSG("Failed to read head flags");
+  }
+
+  // We allow bits 0..4, 11..13
+  file->head->flags &= 0x381f;
+
+  if (!table.ReadU16(&file->head->ppem)) {
+    return OTS_FAILURE_MSG("Failed to read pixels per em");
+  }
+
+  // ppem must be in range
+  if (file->head->ppem < 16 ||
+      file->head->ppem > 16384) {
+    return OTS_FAILURE_MSG("Bad ppm of %d", file->head->ppem);
+  }
+
+  // ppem must be a power of two
+#if 0
+  // We don't call ots_failure() for now since lots of TrueType fonts are
+  // not following this rule. Putting OTS_WARNING here is too noisy.
+  if ((file->head->ppem - 1) & file->head->ppem) {
+    return OTS_FAILURE_MSG("ppm not a power of two: %d", file->head->ppem);
+  }
+#endif
+
+  if (!table.ReadR64(&file->head->created) ||
+      !table.ReadR64(&file->head->modified)) {
+    return OTS_FAILURE_MSG("Can't read font dates");
+  }
+
+  if (!table.ReadS16(&file->head->xmin) ||
+      !table.ReadS16(&file->head->ymin) ||
+      !table.ReadS16(&file->head->xmax) ||
+      !table.ReadS16(&file->head->ymax)) {
+    return OTS_FAILURE_MSG("Failed to read font bounding box");
+  }
+
+  if (file->head->xmin > file->head->xmax) {
+    return OTS_FAILURE_MSG("Bad x dimension in the font bounding box (%d, %d)", file->head->xmin, file->head->xmax);
+  }
+  if (file->head->ymin > file->head->ymax) {
+    return OTS_FAILURE_MSG("Bad y dimension in the font bounding box (%d, %d)", file->head->ymin, file->head->ymax);
+  }
+
+  if (!table.ReadU16(&file->head->mac_style)) {
+    return OTS_FAILURE_MSG("Failed to read font style");
+  }
+
+  // We allow bits 0..6
+  file->head->mac_style &= 0x7f;
+
+  if (!table.ReadU16(&file->head->min_ppem)) {
+    return OTS_FAILURE_MSG("Failed to read font minimum ppm");
+  }
+
+  // We don't care about the font direction hint
+  if (!table.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip font direction hint");
+  }
+
+  if (!table.ReadS16(&file->head->index_to_loc_format)) {
+    return OTS_FAILURE_MSG("Failed to read index to loc format");
+  }
+  if (file->head->index_to_loc_format < 0 ||
+      file->head->index_to_loc_format > 1) {
+    return OTS_FAILURE_MSG("Bad index to loc format %d", file->head->index_to_loc_format);
+  }
+
+  int16_t glyph_data_format;
+  if (!table.ReadS16(&glyph_data_format) ||
+      glyph_data_format) {
+    return OTS_FAILURE_MSG("Failed to read glyph data format");
+  }
+
+  return true;
+}
+
+bool ots_head_should_serialise(OpenTypeFile *file) {
+  return file->head != NULL;
+}
+
+bool ots_head_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->WriteU32(0x00010000) ||
+      !out->WriteU32(file->head->revision) ||
+      !out->WriteU32(0) ||  // check sum not filled in yet
+      !out->WriteU32(0x5F0F3CF5) ||
+      !out->WriteU16(file->head->flags) ||
+      !out->WriteU16(file->head->ppem) ||
+      !out->WriteR64(file->head->created) ||
+      !out->WriteR64(file->head->modified) ||
+      !out->WriteS16(file->head->xmin) ||
+      !out->WriteS16(file->head->ymin) ||
+      !out->WriteS16(file->head->xmax) ||
+      !out->WriteS16(file->head->ymax) ||
+      !out->WriteU16(file->head->mac_style) ||
+      !out->WriteU16(file->head->min_ppem) ||
+      !out->WriteS16(2) ||
+      !out->WriteS16(file->head->index_to_loc_format) ||
+      !out->WriteS16(0)) {
+    return OTS_FAILURE_MSG("Failed to write head table");
+  }
+
+  return true;
+}
+
+void ots_head_free(OpenTypeFile *file) {
+  delete file->head;
+}
+
+}  // namespace
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/head.h b/third_party/ots/src/head.h
new file mode 100644
index 0000000..5967c4b
--- /dev/null
+++ b/third_party/ots/src/head.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HEAD_H_
+#define OTS_HEAD_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHEAD {
+  uint32_t revision;
+  uint16_t flags;
+  uint16_t ppem;
+  uint64_t created;
+  uint64_t modified;
+
+  int16_t xmin, xmax;
+  int16_t ymin, ymax;
+
+  uint16_t mac_style;
+  uint16_t min_ppem;
+  int16_t index_to_loc_format;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HEAD_H_
diff --git a/third_party/ots/src/hhea.cc b/third_party/ots/src/hhea.cc
new file mode 100644
index 0000000..8430442
--- /dev/null
+++ b/third_party/ots/src/hhea.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hhea.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// hhea - Horizontal Header
+// http://www.microsoft.com/typography/otspec/hhea.htm
+
+#define TABLE_NAME "hhea"
+
+namespace ots {
+
+bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeHHEA *hhea = new OpenTypeHHEA;
+  file->hhea = hhea;
+
+  if (!table.ReadU32(&hhea->header.version)) {
+    return OTS_FAILURE_MSG("Failed to read hhea version");
+  }
+  if (hhea->header.version >> 16 != 1) {
+    return OTS_FAILURE_MSG("Bad hhea version of %d", hhea->header.version);
+  }
+
+  if (!ParseMetricsHeader(file, &table, &hhea->header)) {
+    return OTS_FAILURE_MSG("Failed to parse horizontal metrics");
+  }
+
+  return true;
+}
+
+bool ots_hhea_should_serialise(OpenTypeFile *file) {
+  return file->hhea != NULL;
+}
+
+bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsHeader(file, out, &file->hhea->header)) {
+    return OTS_FAILURE_MSG("Failed to serialise horizontal metrics");
+  }
+  return true;
+}
+
+void ots_hhea_free(OpenTypeFile *file) {
+  delete file->hhea;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/hhea.h b/third_party/ots/src/hhea.h
new file mode 100644
index 0000000..bdea9aa
--- /dev/null
+++ b/third_party/ots/src/hhea.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HHEA_H_
+#define OTS_HHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHHEA {
+  OpenTypeMetricsHeader header;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HHEA_H_
diff --git a/third_party/ots/src/hmtx.cc b/third_party/ots/src/hmtx.cc
new file mode 100644
index 0000000..ae86513
--- /dev/null
+++ b/third_party/ots/src/hmtx.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hmtx.h"
+
+#include "hhea.h"
+#include "maxp.h"
+
+// hmtx - Horizontal Metrics
+// http://www.microsoft.com/typography/otspec/hmtx.htm
+
+#define TABLE_NAME "hmtx"
+
+namespace ots {
+
+bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeHMTX *hmtx = new OpenTypeHMTX;
+  file->hmtx = hmtx;
+
+  if (!file->hhea || !file->maxp) {
+    return OTS_FAILURE_MSG("Missing hhea or maxp tables in font, needed by hmtx");
+  }
+
+  if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs,
+                         &file->hhea->header, &hmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to parse hmtx metrics");
+  }
+
+  return true;
+}
+
+bool ots_hmtx_should_serialise(OpenTypeFile *file) {
+  return file->hmtx != NULL;
+}
+
+bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsTable(file, out, &file->hmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to serialise htmx metrics");
+  }
+  return true;
+}
+
+void ots_hmtx_free(OpenTypeFile *file) {
+  delete file->hmtx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/hmtx.h b/third_party/ots/src/hmtx.h
new file mode 100644
index 0000000..435949c
--- /dev/null
+++ b/third_party/ots/src/hmtx.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HMTX_H_
+#define OTS_HMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHMTX {
+  OpenTypeMetricsTable metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HMTX_H_
diff --git a/third_party/ots/src/kern.cc b/third_party/ots/src/kern.cc
new file mode 100644
index 0000000..744c057
--- /dev/null
+++ b/third_party/ots/src/kern.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "kern.h"
+
+// kern - Kerning
+// http://www.microsoft.com/typography/otspec/kern.htm
+
+#define TABLE_NAME "kern"
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    delete file->kern; \
+    file->kern = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_kern_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeKERN *kern = new OpenTypeKERN;
+  file->kern = kern;
+
+  uint16_t num_tables = 0;
+  if (!table.ReadU16(&kern->version) ||
+      !table.ReadU16(&num_tables)) {
+    return OTS_FAILURE_MSG("Failed to read kern header");
+  }
+
+  if (kern->version > 0) {
+    DROP_THIS_TABLE("bad table version");
+    return true;
+  }
+
+  if (num_tables == 0) {
+    DROP_THIS_TABLE("num_tables is zero");
+    return true;
+  }
+
+  kern->subtables.reserve(num_tables);
+  for (unsigned i = 0; i < num_tables; ++i) {
+    OpenTypeKERNFormat0 subtable;
+    uint16_t sub_length = 0;
+
+    if (!table.ReadU16(&subtable.version) ||
+        !table.ReadU16(&sub_length)) {
+      return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i);
+    }
+
+    if (subtable.version > 0) {
+      OTS_WARNING("Bad subtable version: %d", subtable.version);
+      continue;
+    }
+
+    const size_t current_offset = table.offset();
+    if (current_offset - 4 + sub_length > length) {
+      return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset);
+    }
+
+    if (!table.ReadU16(&subtable.coverage)) {
+      return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i);
+    }
+
+    if (!(subtable.coverage & 0x1)) {
+      OTS_WARNING(
+          "We don't support vertical data as the renderer doesn't support it.");
+      continue;
+    }
+    if (subtable.coverage & 0xF0) {
+      DROP_THIS_TABLE("Reserved fields should zero-filled.");
+      return true;
+    }
+    const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
+    if (format != 0) {
+      OTS_WARNING("Format %d is not supported.", format);
+      continue;
+    }
+
+    // Parse the format 0 field.
+    uint16_t num_pairs = 0;
+    if (!table.ReadU16(&num_pairs) ||
+        !table.ReadU16(&subtable.search_range) ||
+        !table.ReadU16(&subtable.entry_selector) ||
+        !table.ReadU16(&subtable.range_shift)) {
+      return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i);
+    }
+
+    if (!num_pairs) {
+      DROP_THIS_TABLE("Zero length subtable is found.");
+      return true;
+    }
+
+    // Sanity checks for search_range, entry_selector, and range_shift. See the
+    // comment in ots.cc for details.
+    const size_t kFormat0PairSize = 6;  // left, right, and value. 2 bytes each.
+    if (num_pairs > (65536 / kFormat0PairSize)) {
+      // Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
+      DROP_THIS_TABLE("Too large subtable.");
+      return true;
+    }
+    unsigned max_pow2 = 0;
+    while (1u << (max_pow2 + 1) <= num_pairs) {
+      ++max_pow2;
+    }
+    const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
+    if (subtable.search_range != expected_search_range) {
+      OTS_WARNING("bad search range");
+      subtable.search_range = expected_search_range;
+    }
+    if (subtable.entry_selector != max_pow2) {
+      return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector);
+    }
+    const uint16_t expected_range_shift =
+        kFormat0PairSize * num_pairs - subtable.search_range;
+    if (subtable.range_shift != expected_range_shift) {
+      OTS_WARNING("bad range shift");
+      subtable.range_shift = expected_range_shift;
+    }
+
+    // Read kerning pairs.
+    subtable.pairs.reserve(num_pairs);
+    uint32_t last_pair = 0;
+    for (unsigned j = 0; j < num_pairs; ++j) {
+      OpenTypeKERNFormat0Pair kerning_pair;
+      if (!table.ReadU16(&kerning_pair.left) ||
+          !table.ReadU16(&kerning_pair.right) ||
+          !table.ReadS16(&kerning_pair.value)) {
+        return OTS_FAILURE_MSG("Failed to read subtable %d kerning pair %d", i, j);
+      }
+      const uint32_t current_pair
+          = (kerning_pair.left << 16) + kerning_pair.right;
+      if (j != 0 && current_pair <= last_pair) {
+        // Many free fonts don't follow this rule, so we don't call OTS_FAILURE
+        // in order to support these fonts.
+        DROP_THIS_TABLE("Kerning pairs are not sorted.");
+        return true;
+      }
+      last_pair = current_pair;
+      subtable.pairs.push_back(kerning_pair);
+    }
+
+    kern->subtables.push_back(subtable);
+  }
+
+  if (!kern->subtables.size()) {
+    DROP_THIS_TABLE("All subtables are removed.");
+    return true;
+  }
+
+  return true;
+}
+
+bool ots_kern_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->kern != NULL;
+}
+
+bool ots_kern_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeKERN *kern = file->kern;
+
+  const uint16_t num_subtables = static_cast<uint16_t>(kern->subtables.size());
+  if (num_subtables != kern->subtables.size() ||
+      !out->WriteU16(kern->version) ||
+      !out->WriteU16(num_subtables)) {
+    return OTS_FAILURE_MSG("Can't write kern table header");
+  }
+
+  for (uint16_t i = 0; i < num_subtables; ++i) {
+    const size_t length = 14 + (6 * kern->subtables[i].pairs.size());
+    if (length > std::numeric_limits<uint16_t>::max() ||
+        !out->WriteU16(kern->subtables[i].version) ||
+        !out->WriteU16(static_cast<uint16_t>(length)) ||
+        !out->WriteU16(kern->subtables[i].coverage) ||
+        !out->WriteU16(
+            static_cast<uint16_t>(kern->subtables[i].pairs.size())) ||
+        !out->WriteU16(kern->subtables[i].search_range) ||
+        !out->WriteU16(kern->subtables[i].entry_selector) ||
+        !out->WriteU16(kern->subtables[i].range_shift)) {
+      return OTS_FAILURE_MSG("Failed to write kern subtable %d", i);
+    }
+    for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
+      if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
+          !out->WriteU16(kern->subtables[i].pairs[j].right) ||
+          !out->WriteS16(kern->subtables[i].pairs[j].value)) {
+        return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_kern_free(OpenTypeFile *file) {
+  delete file->kern;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/kern.h b/third_party/ots/src/kern.h
new file mode 100644
index 0000000..9350ef7
--- /dev/null
+++ b/third_party/ots/src/kern.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_KERN_H_
+#define OTS_KERN_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeKERNFormat0Pair {
+  uint16_t left;
+  uint16_t right;
+  int16_t value;
+};
+
+struct OpenTypeKERNFormat0 {
+  uint16_t version;
+  uint16_t coverage;
+  uint16_t search_range;
+  uint16_t entry_selector;
+  uint16_t range_shift;
+  std::vector<OpenTypeKERNFormat0Pair> pairs;
+};
+
+// Format 2 is not supported. Since the format is not supported by Windows,
+// WebFonts unlikely use it. I've checked thousands of proprietary fonts and
+// free fonts, and found no font uses the format.
+
+struct OpenTypeKERN {
+  uint16_t version;
+  std::vector<OpenTypeKERNFormat0> subtables;
+};
+
+}  // namespace ots
+
+#endif  // OTS_KERN_H_
diff --git a/third_party/ots/src/layout.cc b/third_party/ots/src/layout.cc
new file mode 100644
index 0000000..856152c
--- /dev/null
+++ b/third_party/ots/src/layout.cc
@@ -0,0 +1,1511 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "layout.h"
+
+#include <limits>
+#include <vector>
+
+#include "gdef.h"
+
+// OpenType Layout Common Table Formats
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+#define TABLE_NAME "Layout" // XXX: use individual table names
+
+namespace {
+
+// The 'DFLT' tag of script table.
+const uint32_t kScriptTableTagDflt = 0x44464c54;
+// The value which represents there is no required feature index.
+const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
+// The lookup flag bit which indicates existence of MarkFilteringSet.
+const uint16_t kUseMarkFilteringSetBit = 0x0010;
+// The lookup flags which require GDEF table.
+const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008;
+// The mask for MarkAttachmentType.
+const uint16_t kMarkAttachmentTypeMask = 0xFF00;
+// The maximum type number of format for device tables.
+const uint16_t kMaxDeltaFormatType = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+
+struct ScriptRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+struct LangSysRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+struct FeatureRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+bool ParseLangSysTable(const ots::OpenTypeFile *file,
+                       ots::Buffer *subtable, const uint32_t tag,
+                       const uint16_t num_features) {
+  uint16_t offset_lookup_order = 0;
+  uint16_t req_feature_index = 0;
+  uint16_t feature_count = 0;
+  if (!subtable->ReadU16(&offset_lookup_order) ||
+      !subtable->ReadU16(&req_feature_index) ||
+      !subtable->ReadU16(&feature_count)) {
+    return OTS_FAILURE_MSG("Failed to read langsys header for tag %4.4s", (char *)&tag);
+  }
+  // |offset_lookup_order| is reserved and should be NULL.
+  if (offset_lookup_order != 0) {
+    return OTS_FAILURE_MSG("Bad lookup offset order %d for langsys tag %4.4s", offset_lookup_order, (char *)&tag);
+  }
+  if (req_feature_index != kNoRequiredFeatureIndexDefined &&
+      req_feature_index >= num_features) {
+    return OTS_FAILURE_MSG("Bad required features index %d for langsys tag %4.4s", req_feature_index, (char *)&tag);
+  }
+  if (feature_count > num_features) {
+    return OTS_FAILURE_MSG("Bad feature count %d for langsys tag %4.4s", feature_count, (char *)&tag);
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    uint16_t feature_index = 0;
+    if (!subtable->ReadU16(&feature_index)) {
+      return OTS_FAILURE_MSG("Failed to read feature index %d for langsys tag %4.4s", i, (char *)&tag);
+    }
+    if (feature_index >= num_features) {
+      return OTS_FAILURE_MSG("Bad feature index %d for feature %d for langsys tag %4.4s", feature_index, i, (char *)&tag);
+    }
+  }
+  return true;
+}
+
+bool ParseScriptTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, const size_t length,
+                      const uint32_t tag, const uint16_t num_features) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_default_lang_sys = 0;
+  uint16_t lang_sys_count = 0;
+  if (!subtable.ReadU16(&offset_default_lang_sys) ||
+      !subtable.ReadU16(&lang_sys_count)) {
+    return OTS_FAILURE_MSG("Failed to read script header for script tag %4.4s", (char *)&tag);
+  }
+
+  // The spec requires a script table for 'DFLT' tag must contain non-NULL
+  // |offset_default_lang_sys| and |lang_sys_count| == 0
+  if (tag == kScriptTableTagDflt &&
+      (offset_default_lang_sys == 0 || lang_sys_count != 0)) {
+    return OTS_FAILURE_MSG("DFLT table doesn't satisfy the spec. for script tag %4.4s", (char *)&tag);
+  }
+
+  const unsigned lang_sys_record_end =
+      6 * static_cast<unsigned>(lang_sys_count) + 4;
+  if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of langsys record %d for script tag %4.4s", lang_sys_record_end, (char *)&tag);
+  }
+
+  std::vector<LangSysRecord> lang_sys_records;
+  lang_sys_records.resize(lang_sys_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    if (!subtable.ReadU32(&lang_sys_records[i].tag) ||
+        !subtable.ReadU16(&lang_sys_records[i].offset)) {
+      return OTS_FAILURE_MSG("Failed to read langsys record header %d for script tag %4.4s", i, (char *)&tag);
+    }
+    // The record array must store the records alphabetically by tag
+    if (last_tag != 0 && last_tag > lang_sys_records[i].tag) {
+      return OTS_FAILURE_MSG("Bad last tag %d for langsys record %d for script tag %4.4s", last_tag, i, (char *)&tag);
+    }
+    if (lang_sys_records[i].offset < lang_sys_record_end ||
+        lang_sys_records[i].offset >= length) {
+      return OTS_FAILURE_MSG("bad offset to lang sys table: %x",
+                  lang_sys_records[i].offset);
+    }
+    last_tag = lang_sys_records[i].tag;
+  }
+
+  // Check lang sys tables
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    subtable.set_offset(lang_sys_records[i].offset);
+    if (!ParseLangSysTable(file, &subtable, lang_sys_records[i].tag, num_features)) {
+      return OTS_FAILURE_MSG("Failed to parse langsys table %d (%4.4s) for script tag %4.4s", i, (char *)&lang_sys_records[i].tag, (char *)&tag);
+    }
+  }
+
+  return true;
+}
+
+bool ParseFeatureTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_feature_params = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&offset_feature_params) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read feature table header");
+  }
+
+  const unsigned feature_table_end =
+      2 * static_cast<unsigned>(lookup_count) + 4;
+  if (feature_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of feature table %d", feature_table_end);
+  }
+  // |offset_feature_params| is generally set to NULL.
+  if (offset_feature_params != 0 &&
+      (offset_feature_params < feature_table_end ||
+       offset_feature_params >= length)) {
+    return OTS_FAILURE_MSG("Bad feature params offset %d", offset_feature_params);
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    uint16_t lookup_index = 0;
+    if (!subtable.ReadU16(&lookup_index)) {
+      return OTS_FAILURE_MSG("Failed to read lookup index for lookup %d", i);
+    }
+    // lookup index starts with 0.
+    if (lookup_index >= num_lookups) {
+      return OTS_FAILURE_MSG("Bad lookup index %d for lookup %d", lookup_index, i);
+    }
+  }
+  return true;
+}
+
+bool ParseLookupTable(ots::OpenTypeFile *file, const uint8_t *data,
+                      const size_t length,
+                      const ots::LookupSubtableParser* parser) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lookup_type = 0;
+  uint16_t lookup_flag = 0;
+  uint16_t subtable_count = 0;
+  if (!subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU16(&lookup_flag) ||
+      !subtable.ReadU16(&subtable_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup table header");
+  }
+
+  if (lookup_type == 0 || lookup_type > parser->num_types) {
+    return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
+  }
+
+  // Check lookup flags.
+  if ((lookup_flag & kGdefRequiredFlags) &&
+      (!file->gdef || !file->gdef->has_glyph_class_def)) {
+    return OTS_FAILURE_MSG("Bad lookup flags %d", lookup_flag);
+  }
+  if ((lookup_flag & kMarkAttachmentTypeMask) &&
+      (!file->gdef || !file->gdef->has_mark_attachment_class_def)) {
+    return OTS_FAILURE_MSG("lookup flag asks for mark attachment that is bad %d", lookup_flag);
+  }
+  bool use_mark_filtering_set = false;
+  if (lookup_flag & kUseMarkFilteringSetBit) {
+    if (!file->gdef || !file->gdef->has_mark_glyph_sets_def) {
+      return OTS_FAILURE_MSG("lookup flag asks for mark filtering that is bad %d", lookup_flag);
+    }
+    use_mark_filtering_set = true;
+  }
+
+  std::vector<uint16_t> subtables;
+  subtables.reserve(subtable_count);
+  // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set,
+  // extra 2 bytes will follow after subtable offset array.
+  const unsigned lookup_table_end = 2 * static_cast<unsigned>(subtable_count) +
+      (use_mark_filtering_set ? 8 : 6);
+  if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup %d", lookup_table_end);
+  }
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    uint16_t offset_subtable = 0;
+    if (!subtable.ReadU16(&offset_subtable)) {
+      return OTS_FAILURE_MSG("Failed to read subtable offset %d", i);
+    }
+    if (offset_subtable < lookup_table_end ||
+        offset_subtable >= length) {
+      return OTS_FAILURE_MSG("Bad subtable offset %d for subtable %d", offset_subtable, i);
+    }
+    subtables.push_back(offset_subtable);
+  }
+  if (subtables.size() != subtable_count) {
+    return OTS_FAILURE_MSG("Bad subtable size %ld", subtables.size());
+  }
+
+  if (use_mark_filtering_set) {
+    uint16_t mark_filtering_set = 0;
+    if (!subtable.ReadU16(&mark_filtering_set)) {
+      return OTS_FAILURE_MSG("Failed to read mark filtering set");
+    }
+    if (file->gdef->num_mark_glyph_sets == 0 ||
+        mark_filtering_set >= file->gdef->num_mark_glyph_sets) {
+      return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
+    }
+  }
+
+  // Parse lookup subtables for this lookup type.
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    if (!parser->Parse(file, data + subtables[i], length - subtables[i],
+                       lookup_type)) {
+      return OTS_FAILURE_MSG("Failed to parse subtable %d", i);
+    }
+  }
+  return true;
+}
+
+bool ParseClassDefFormat1(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip class definition header");
+  }
+
+  uint16_t start_glyph = 0;
+  if (!subtable.ReadU16(&start_glyph)) {
+    return OTS_FAILURE_MSG("Failed to read starting glyph of class definition");
+  }
+  if (start_glyph > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad starting glyph %d in class definition", start_glyph);
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in class definition");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE_MSG("Failed to read class value for glyph %d in class definition", i);
+    }
+    if (class_value > num_classes) {
+      return OTS_FAILURE_MSG("Bad class value %d for glyph %d in class definition", class_value, i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefFormat2(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip format of class defintion header");
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE_MSG("Failed to read range count in class definition");
+  }
+  if (range_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad range count: %u", range_count);
+  }
+
+  uint16_t last_end = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE_MSG("Failed to read class definition reange %d", i);
+    }
+    if (start > end || (last_end && start <= last_end)) {
+      return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i);
+    }
+    if (class_value > num_classes) {
+      return OTS_FAILURE_MSG("bad class value: %u", class_value);
+    }
+    last_end = end;
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat1(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t expected_num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip coverage format");
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in coverage");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t glyph = 0;
+    if (!subtable.ReadU16(&glyph)) {
+      return OTS_FAILURE_MSG("Failed to read glyph %d in coverage", i);
+    }
+    if (glyph > num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+  }
+
+  if (expected_num_glyphs && expected_num_glyphs != glyph_count) {
+      return OTS_FAILURE_MSG("unexpected number of glyphs: %u", glyph_count);
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat2(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t expected_num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip format of coverage type 2");
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE_MSG("Failed to read range count in coverage");
+  }
+  if (range_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad range count: %u", range_count);
+  }
+  uint16_t last_end = 0;
+  uint16_t last_start_coverage_index = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t start_coverage_index = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&start_coverage_index)) {
+      return OTS_FAILURE_MSG("Failed to read range %d in coverage", i);
+    }
+
+    // Some of the Adobe Pro fonts have ranges that overlap by one element: the
+    // start of one range is equal to the end of the previous range. Therefore
+    // the < in the following condition should be <= were it not for this.
+    // See crbug.com/134135.
+    if (start > end || (last_end && start < last_end)) {
+      return OTS_FAILURE_MSG("glyph range is overlapping.");
+    }
+    if (start_coverage_index != last_start_coverage_index) {
+      return OTS_FAILURE_MSG("bad start coverage index.");
+    }
+    last_end = end;
+    last_start_coverage_index += end - start + 1;
+  }
+
+  if (expected_num_glyphs &&
+      expected_num_glyphs != last_start_coverage_index) {
+      return OTS_FAILURE_MSG("unexpected number of glyphs: %u", last_start_coverage_index);
+  }
+
+  return true;
+}
+
+// Parsers for Contextual subtables in GSUB/GPOS tables.
+
+bool ParseLookupRecord(const ots::OpenTypeFile *file,
+                       ots::Buffer *subtable, const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  uint16_t sequence_index = 0;
+  uint16_t lookup_list_index = 0;
+  if (!subtable->ReadU16(&sequence_index) ||
+      !subtable->ReadU16(&lookup_list_index)) {
+    return OTS_FAILURE_MSG("Failed to read header for lookup record");
+  }
+  if (sequence_index >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad sequence index %d in lookup record", sequence_index);
+  }
+  if (lookup_list_index >= num_lookups) {
+    return OTS_FAILURE_MSG("Bad lookup list index %d in lookup record", lookup_list_index);
+  }
+  return true;
+}
+
+bool ParseRuleSubtable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule subtable header");
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in rule subtable", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph %d", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph %d for entry %d", glyph_id, i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d", i);
+    }
+  }
+  return true;
+}
+
+bool ParseRuleSetTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t rule_count = 0;
+  if (!subtable.ReadU16(&rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in rule set");
+  }
+  const unsigned rule_end = 2 * static_cast<unsigned>(rule_count) + 2;
+  if (rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of rule %d in rule set", rule_end);
+  }
+
+  for (unsigned i = 0; i < rule_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE_MSG("Failed to read rule offset for rule set %d", i);
+    }
+    if (offset_rule < rule_end || offset_rule >= length) {
+      return OTS_FAILURE_MSG("Bad rule offset %d in set %d", offset_rule, i);
+    }
+    if (!ParseRuleSubtable(file, data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse rule set %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat1(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&rule_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of context format 1");
+  }
+
+  const unsigned rule_set_end = static_cast<unsigned>(6) +
+      rule_set_count * 2;
+  if (rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of rule set %d of context format 1", rule_set_end);
+  }
+  if (offset_coverage < rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in context format 1", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in context format 1");
+  }
+
+  for (unsigned i = 0; i < rule_set_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE_MSG("Failed to read rule offset %d in context format 1", i);
+    }
+    if (offset_rule < rule_set_end || offset_rule >= length) {
+      return OTS_FAILURE_MSG("Bad rule offset %d in rule %d in context format 1", offset_rule, i);
+    }
+    if (!ParseRuleSetTable(file, data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse rule set %d in context format 1", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassRuleTable(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of class rule table");
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in class rule table", glyph_count);
+  }
+
+  // ClassRule table contains an array of classes. Each value of classes
+  // could take arbitrary values including zero so we don't check these value.
+  const unsigned num_classes = glyph_count - static_cast<unsigned>(1);
+  if (!subtable.Skip(2 * num_classes)) {
+    return OTS_FAILURE_MSG("Failed to skip classes in class rule table");
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in class rule table", i);
+    }
+  }
+  return true;
+}
+
+bool ParseClassSetTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t class_rule_count = 0;
+  if (!subtable.ReadU16(&class_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read class rule count in class set table");
+  }
+  const unsigned class_rule_end =
+      2 * static_cast<unsigned>(class_rule_count) + 2;
+  if (class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("bad class rule end %d in class set table", class_rule_end);
+  }
+  for (unsigned i = 0; i < class_rule_count; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read class rule offset %d in class set table", i);
+    }
+    if (offset_class_rule < class_rule_end || offset_class_rule >= length) {
+      return OTS_FAILURE_MSG("Bad class rule offset %d in class %d", offset_class_rule, i);
+    }
+    if (!ParseClassRuleTable(file, data + offset_class_rule,
+                             length - offset_class_rule, num_glyphs,
+                             num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse class rule table %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat2(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_class_def = 0;
+  uint16_t class_set_cnt = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_class_def) ||
+      !subtable.ReadU16(&class_set_cnt)) {
+    return OTS_FAILURE_MSG("Failed to read header for context format 2");
+  }
+
+  const unsigned class_set_end = 2 * static_cast<unsigned>(class_set_cnt) + 8;
+  if (class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of class set %d for context format 2", class_set_end);
+  }
+  if (offset_coverage < class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in context format 2", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in context format 2");
+  }
+
+  if (offset_class_def < class_set_end || offset_class_def >= length) {
+    return OTS_FAILURE_MSG("bad class definition offset %d in context format 2", offset_class_def);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def,
+                               length - offset_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2");
+  }
+
+  for (unsigned i = 0; i < class_set_cnt; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read class rule offset %d in context format 2", i);
+    }
+    if (offset_class_rule) {
+      if (offset_class_rule < class_set_end || offset_class_rule >= length) {
+        return OTS_FAILURE_MSG("Bad class rule offset %d for rule %d in context format 2", offset_class_rule, i);
+      }
+      if (!ParseClassSetTable(file, data + offset_class_rule,
+                              length - offset_class_rule, num_glyphs,
+                              num_lookups)) {
+        return OTS_FAILURE_MSG("Failed to parse class set %d in context format 2", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat3(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read header in context format 3");
+  }
+
+  if (glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in context format 3", glyph_count);
+  }
+  const unsigned lookup_record_end = 2 * static_cast<unsigned>(glyph_count) +
+      4 * static_cast<unsigned>(lookup_count) + 6;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup %d in context format 3", lookup_record_end);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t offset_coverage = 0;
+    if (!subtable.ReadU16(&offset_coverage)) {
+      return OTS_FAILURE_MSG("Failed to read coverage offset %d in conxtext format 3", i);
+    }
+    if (offset_coverage < lookup_record_end || offset_coverage >= length) {
+      return OTS_FAILURE_MSG("Bad coverage offset %d for glyph %d in context format 3", offset_coverage, i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for glyph %d in context format 3", i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in context format 3", i);
+    }
+  }
+
+  return true;
+}
+
+// Parsers for Chaning Contextual subtables in GSUB/GPOS tables.
+
+bool ParseChainRuleSubtable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain rule subtable");
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain rule subtable", backtrack_count);
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for bactrack glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain rule subtable");
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain rule subtable", input_count);
+  }
+  for (unsigned i = 0; i < input_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read input glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for input glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookahead count in chain rule subtable");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain rule subtable", lookahead_count);
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read lookahead glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for lookadhead glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain rule subtable");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain rule subtable", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainRuleSetTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_rule_count = 0;
+  if (!subtable.ReadU16(&chain_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in chain rule set");
+  }
+  const unsigned chain_rule_end =
+      2 * static_cast<unsigned>(chain_rule_count) + 2;
+  if (chain_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of chain rule %d in chain rule set", chain_rule_end);
+  }
+  for (unsigned i = 0; i < chain_rule_count; ++i) {
+    uint16_t offset_chain_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_rule)) {
+      return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain rule set", i);
+    }
+    if (offset_chain_rule < chain_rule_end || offset_chain_rule >= length) {
+      return OTS_FAILURE_MSG("Bad chain rule offset %d for chain rule %d in chain rule set", offset_chain_rule, i);
+    }
+    if (!ParseChainRuleSubtable(file, data + offset_chain_rule,
+                                length - offset_chain_rule,
+                                num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain rule %d in chain rule set", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat1(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t chain_rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&chain_rule_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of chain context format 1");
+  }
+
+  const unsigned chain_rule_set_end =
+      2 * static_cast<unsigned>(chain_rule_set_count) + 6;
+  if (chain_rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad chain rule end %d in chain context format 1", chain_rule_set_end);
+  }
+  if (offset_coverage < chain_rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 1", chain_rule_set_end);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table for chain context format 1");
+  }
+
+  for (unsigned i = 0; i < chain_rule_set_count; ++i) {
+    uint16_t offset_chain_rule_set = 0;
+    if (!subtable.ReadU16(&offset_chain_rule_set)) {
+      return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain context format 1", i);
+    }
+    if (offset_chain_rule_set < chain_rule_set_end ||
+        offset_chain_rule_set >= length) {
+      return OTS_FAILURE_MSG("Bad chain rule set offset %d for chain rule set %d in chain context format 1", offset_chain_rule_set, i);
+    }
+    if (!ParseChainRuleSetTable(file, data + offset_chain_rule_set,
+                                   length - offset_chain_rule_set,
+                                   num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain rule set %d in chain context format 1", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassRuleSubtable(const ots::OpenTypeFile *file,
+                                 const uint8_t *data, const size_t length,
+                                 const uint16_t num_glyphs,
+                                 const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  // In this subtable, we don't check the value of classes for now since
+  // these could take arbitrary values.
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain class rule subtable");
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain class rule subtable", backtrack_count);
+  }
+  if (!subtable.Skip(2 * backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to skip backtrack offsets in chain class rule subtable");
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain class rule subtable");
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain class rule subtable", input_count);
+  }
+  if (!subtable.Skip(2 * (input_count - 1))) {
+    return OTS_FAILURE_MSG("Failed to skip input offsets in chain class rule subtable");
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookahead count in chain class rule subtable");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain class rule subtable", lookahead_count);
+  }
+  if (!subtable.Skip(2 * lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to skip lookahead offsets in chain class rule subtable");
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain class rule subtable");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain class rule subtable", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassSetTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length,
+                             const uint16_t num_glyphs,
+                             const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_class_rule_count = 0;
+  if (!subtable.ReadU16(&chain_class_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in chain class set");
+  }
+  const unsigned chain_class_rule_end =
+      2 * static_cast<unsigned>(chain_class_rule_count) + 2;
+  if (chain_class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of chain class set %d in chain class set", chain_class_rule_end);
+  }
+  for (unsigned i = 0; i < chain_class_rule_count; ++i) {
+    uint16_t offset_chain_class_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read chain class rule offset %d in chain class set", i);
+    }
+    if (offset_chain_class_rule < chain_class_rule_end ||
+        offset_chain_class_rule >= length) {
+      return OTS_FAILURE_MSG("Bad chain class rule offset %d for chain class %d in chain class set", offset_chain_class_rule, i);
+    }
+    if (!ParseChainClassRuleSubtable(file, data + offset_chain_class_rule,
+                                     length - offset_chain_class_rule,
+                                     num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain class rule %d in chain class set", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat2(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_backtrack_class_def = 0;
+  uint16_t offset_input_class_def = 0;
+  uint16_t offset_lookahead_class_def = 0;
+  uint16_t chain_class_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_backtrack_class_def) ||
+      !subtable.ReadU16(&offset_input_class_def) ||
+      !subtable.ReadU16(&offset_lookahead_class_def) ||
+      !subtable.ReadU16(&chain_class_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of chain context format 2");
+  }
+
+  const unsigned chain_class_set_end =
+      2 * static_cast<unsigned>(chain_class_set_count) + 12;
+  if (chain_class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad chain class set end %d in chain context format 2", chain_class_set_end);
+  }
+  if (offset_coverage < chain_class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 2", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in chain context format 2");
+  }
+
+  // Classes for backtrack/lookahead sequences might not be defined.
+  if (offset_backtrack_class_def) {
+    if (offset_backtrack_class_def < chain_class_set_end ||
+        offset_backtrack_class_def >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack class offset %d in chain context format 2", offset_backtrack_class_def);
+    }
+    if (!ots::ParseClassDefTable(file, data + offset_backtrack_class_def,
+                                 length - offset_backtrack_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2");
+    }
+  }
+
+  if (offset_input_class_def < chain_class_set_end ||
+      offset_input_class_def >= length) {
+    return OTS_FAILURE_MSG("Bad input class defn offset %d in chain context format 2", offset_input_class_def);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_input_class_def,
+                               length - offset_input_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2");
+  }
+
+  if (offset_lookahead_class_def) {
+    if (offset_lookahead_class_def < chain_class_set_end ||
+        offset_lookahead_class_def >= length) {
+      return OTS_FAILURE_MSG("Bad lookahead class defn offset %d in chain context format 2", offset_lookahead_class_def);
+    }
+    if (!ots::ParseClassDefTable(file, data + offset_lookahead_class_def,
+                                 length - offset_lookahead_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2");
+    }
+  }
+
+  for (unsigned i = 0; i < chain_class_set_count; ++i) {
+    uint16_t offset_chain_class_set = 0;
+    if (!subtable.ReadU16(&offset_chain_class_set)) {
+      return OTS_FAILURE_MSG("Failed to read chain class set offset %d", i);
+    }
+    // |offset_chain_class_set| could be NULL.
+    if (offset_chain_class_set) {
+      if (offset_chain_class_set < chain_class_set_end ||
+          offset_chain_class_set >= length) {
+        return OTS_FAILURE_MSG("Bad chain set class offset %d for chain set %d in chain context format 2", offset_chain_class_set, i);
+      }
+      if (!ParseChainClassSetTable(file, data + offset_chain_class_set,
+                                   length - offset_chain_class_set,
+                                   num_glyphs, num_lookups)) {
+        return OTS_FAILURE_MSG("Failed to parse chain class set table %d in chain context format 2", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat3(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain context format 3");
+  }
+
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain context format 3", backtrack_count);
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_count);
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack offset %d in chain context format 3", i);
+    }
+    offsets_backtrack.push_back(offset);
+  }
+  if (offsets_backtrack.size() != backtrack_count) {
+    return OTS_FAILURE_MSG("Bad backtrack offsets size %ld in chain context format 3", offsets_backtrack.size());
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain context format 3");
+  }
+  if (input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain context format 3", input_count);
+  }
+  std::vector<uint16_t> offsets_input;
+  offsets_input.reserve(input_count);
+  for (unsigned i = 0; i < input_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read input offset %d in chain context format 3", i);
+    }
+    offsets_input.push_back(offset);
+  }
+  if (offsets_input.size() != input_count) {
+    return OTS_FAILURE_MSG("Bad input offsets size %ld in chain context format 3", offsets_input.size());
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed ot read lookahead count in chain context format 3");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain context format 3", lookahead_count);
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_count);
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read lookahead offset %d in chain context format 3", i);
+    }
+    offsets_lookahead.push_back(offset);
+  }
+  if (offsets_lookahead.size() != lookahead_count) {
+    return OTS_FAILURE_MSG("Bad lookahead offsets size %ld in chain context format 3", offsets_lookahead.size());
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain context format 3");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup %d in chain context format 3", i);
+    }
+  }
+
+  const unsigned lookup_record_end =
+      2 * (static_cast<unsigned>(backtrack_count) +
+           static_cast<unsigned>(input_count) +
+           static_cast<unsigned>(lookahead_count)) +
+      4 * static_cast<unsigned>(lookup_count) + 10;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup record %d in chain context format 3", lookup_record_end);
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    if (offsets_backtrack[i] < lookup_record_end ||
+        offsets_backtrack[i] >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack offset of %d for backtrack %d in chain context format 3", offsets_backtrack[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i],
+                                 length - offsets_backtrack[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse backtrack coverage %d in chain context format 3", i);
+    }
+  }
+  for (unsigned i = 0; i < input_count; ++i) {
+    if (offsets_input[i] < lookup_record_end || offsets_input[i] >= length) {
+      return OTS_FAILURE_MSG("Bad input offset %d for input %d in chain context format 3", offsets_input[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_input[i],
+                                 length - offsets_input[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse input coverage table %d in chain context format 3", i);
+    }
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    if (offsets_lookahead[i] < lookup_record_end ||
+        offsets_lookahead[i] >= length) {
+      return OTS_FAILURE_MSG("Bad lookadhead offset %d for lookahead %d in chain context format 3", offsets_lookahead[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i],
+                                 length - offsets_lookahead[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in chain context format 3", i);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool LookupSubtableParser::Parse(const OpenTypeFile *file, const uint8_t *data,
+                                 const size_t length,
+                                 const uint16_t lookup_type) const {
+  for (unsigned i = 0; i < num_types; ++i) {
+    if (parsers[i].type == lookup_type && parsers[i].parse) {
+      if (!parsers[i].parse(file, data, length)) {
+        return OTS_FAILURE_MSG("Failed to parse lookup subtable %d", i);
+      }
+      return true;
+    }
+  }
+  return OTS_FAILURE_MSG("No lookup subtables to parse");
+}
+
+// Parsing ScriptListTable requires number of features so we need to
+// parse FeatureListTable before calling this function.
+bool ParseScriptListTable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t script_count = 0;
+  if (!subtable.ReadU16(&script_count)) {
+    return OTS_FAILURE_MSG("Failed to read script count in script list table");
+  }
+
+  const unsigned script_record_end =
+      6 * static_cast<unsigned>(script_count) + 2;
+  if (script_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of script record %d in script list table", script_record_end);
+  }
+  std::vector<ScriptRecord> script_list;
+  script_list.reserve(script_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < script_count; ++i) {
+    ScriptRecord record;
+    if (!subtable.ReadU32(&record.tag) ||
+        !subtable.ReadU16(&record.offset)) {
+      return OTS_FAILURE_MSG("Failed to read script record %d in script list table", i);
+    }
+    // Script tags should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > record.tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = record.tag;
+    if (record.offset < script_record_end || record.offset >= length) {
+      return OTS_FAILURE_MSG("Bad record offset %d for script %4.4s entry %d in script list table", record.offset, (char *)&record.tag, i);
+    }
+    script_list.push_back(record);
+  }
+  if (script_list.size() != script_count) {
+    return OTS_FAILURE_MSG("Bad script list size %ld in script list table", script_list.size());
+  }
+
+  // Check script records.
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!ParseScriptTable(file, data + script_list[i].offset,
+                          length - script_list[i].offset,
+                          script_list[i].tag, num_features)) {
+      return OTS_FAILURE_MSG("Failed to parse script table %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Parsing FeatureListTable requires number of lookups so we need to parse
+// LookupListTable before calling this function.
+bool ParseFeatureListTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t* num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t feature_count = 0;
+  if (!subtable.ReadU16(&feature_count)) {
+    return OTS_FAILURE_MSG("Failed to read feature count");
+  }
+
+  std::vector<FeatureRecord> feature_records;
+  feature_records.resize(feature_count);
+  const unsigned feature_record_end =
+      6 * static_cast<unsigned>(feature_count) + 2;
+  if (feature_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of feature record %d", feature_record_end);
+  }
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!subtable.ReadU32(&feature_records[i].tag) ||
+        !subtable.ReadU16(&feature_records[i].offset)) {
+      return OTS_FAILURE_MSG("Failed to read feature header %d", i);
+    }
+    // Feature record array should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > feature_records[i].tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = feature_records[i].tag;
+    if (feature_records[i].offset < feature_record_end ||
+        feature_records[i].offset >= length) {
+      return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %4.4s", feature_records[i].offset, i, (char *)&feature_records[i].tag);
+    }
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!ParseFeatureTable(file, data + feature_records[i].offset,
+                           length - feature_records[i].offset, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse feature table %d", i);
+    }
+  }
+  *num_features = feature_count;
+  return true;
+}
+
+// For parsing GPOS/GSUB tables, this function should be called at first to
+// obtain the number of lookups because parsing FeatureTableList requires
+// the number.
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t *num_lookups) {
+  Buffer subtable(data, length);
+
+  if (!subtable.ReadU16(num_lookups)) {
+    return OTS_FAILURE_MSG("Failed to read number of lookups");
+  }
+
+  std::vector<uint16_t> lookups;
+  lookups.reserve(*num_lookups);
+  const unsigned lookup_end =
+      2 * static_cast<unsigned>(*num_lookups) + 2;
+  if (lookup_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookups %d", lookup_end);
+  }
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read lookup offset %d", i);
+    }
+    if (offset < lookup_end || offset >= length) {
+      return OTS_FAILURE_MSG("Bad lookup offset %d for lookup %d", offset, i);
+    }
+    lookups.push_back(offset);
+  }
+  if (lookups.size() != *num_lookups) {
+    return OTS_FAILURE_MSG("Bad lookup offsets list size %ld", lookups.size());
+  }
+
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    if (!ParseLookupTable(file, data + lookups[i], length - lookups[i],
+                          parser)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read class defn format");
+  }
+  if (format == 1) {
+    return ParseClassDefFormat1(file, data, length, num_glyphs, num_classes);
+  } else if (format == 2) {
+    return ParseClassDefFormat2(file, data, length, num_glyphs, num_classes);
+  }
+
+  return OTS_FAILURE_MSG("Bad class defn format %d", format);
+}
+
+bool ParseCoverageTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t expected_num_glyphs) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read coverage table format");
+  }
+  if (format == 1) {
+    return ParseCoverageFormat1(file, data, length, num_glyphs, expected_num_glyphs);
+  } else if (format == 2) {
+    return ParseCoverageFormat2(file, data, length, num_glyphs, expected_num_glyphs);
+  }
+
+  return OTS_FAILURE_MSG("Bad coverage table format %d", format);
+}
+
+bool ParseDeviceTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, size_t length) {
+  Buffer subtable(data, length);
+
+  uint16_t start_size = 0;
+  uint16_t end_size = 0;
+  uint16_t delta_format = 0;
+  if (!subtable.ReadU16(&start_size) ||
+      !subtable.ReadU16(&end_size) ||
+      !subtable.ReadU16(&delta_format)) {
+    return OTS_FAILURE_MSG("Failed to read device table header");
+  }
+  if (start_size > end_size) {
+    return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size);
+  }
+  if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
+    return OTS_FAILURE_MSG("bad delta format: %u", delta_format);
+  }
+  // The number of delta values per uint16. The device table should contain
+  // at least |num_units| * 2 bytes compressed data.
+  const unsigned num_units = (end_size - start_size) /
+      (1 << (4 - delta_format)) + 1;
+  // Just skip |num_units| * 2 bytes since the compressed data could take
+  // arbitrary values.
+  if (!subtable.Skip(num_units * 2)) {
+    return OTS_FAILURE_MSG("Failed to skip data in device table");
+  }
+  return true;
+}
+
+bool ParseContextSubtable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read context subtable format");
+  }
+
+  if (format == 1) {
+    if (!ParseContextFormat1(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 1 subtable");
+    }
+  } else if (format == 2) {
+    if (!ParseContextFormat2(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 2 subtable");
+    }
+  } else if (format == 3) {
+    if (!ParseContextFormat3(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 3 subtable");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad context subtable format %d", format);
+  }
+
+  return true;
+}
+
+bool ParseChainingContextSubtable(const ots::OpenTypeFile *file,
+                                  const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read chaining context subtable format");
+  }
+
+  if (format == 1) {
+    if (!ParseChainContextFormat1(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 1 subtable");
+    }
+  } else if (format == 2) {
+    if (!ParseChainContextFormat2(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 2 subtable");
+    }
+  } else if (format == 3) {
+    if (!ParseChainContextFormat3(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 3 subtable");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad chaining context subtable format %d", format);
+  }
+
+  return true;
+}
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t lookup_type = 0;
+  uint32_t offset_extension = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU32(&offset_extension)) {
+    return OTS_FAILURE_MSG("Failed to read extension table header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad extension table format %d", format);
+  }
+  // |lookup_type| should be other than |parser->extension_type|.
+  if (lookup_type < 1 || lookup_type > parser->num_types ||
+      lookup_type == parser->extension_type) {
+    return OTS_FAILURE_MSG("Bad lookup type %d in extension table", lookup_type);
+  }
+
+  const unsigned format_end = static_cast<unsigned>(8);
+  if (offset_extension < format_end ||
+      offset_extension >= length) {
+    return OTS_FAILURE_MSG("Bad extension offset %d", offset_extension);
+  }
+
+  // Parse the extension subtable of |lookup_type|.
+  if (!parser->Parse(file, data + offset_extension, length - offset_extension,
+                     lookup_type)) {
+    return OTS_FAILURE_MSG("Failed to parse lookup from extension lookup");
+  }
+
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/layout.h b/third_party/ots/src/layout.h
new file mode 100644
index 0000000..3b94589
--- /dev/null
+++ b/third_party/ots/src/layout.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LAYOUT_H_
+#define OTS_LAYOUT_H_
+
+#include "ots.h"
+
+// Utility functions for OpenType layout common table formats.
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+namespace ots {
+
+
+struct LookupSubtableParser {
+  struct TypeParser {
+    uint16_t type;
+    bool (*parse)(const OpenTypeFile *file, const uint8_t *data,
+                  const size_t length);
+  };
+  size_t num_types;
+  uint16_t extension_type;
+  const TypeParser *parsers;
+
+  bool Parse(const OpenTypeFile *file, const uint8_t *data,
+             const size_t length, const uint16_t lookup_type) const;
+};
+
+bool ParseScriptListTable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_features);
+
+bool ParseFeatureListTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t *num_features);
+
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t* num_lookups);
+
+bool ParseClassDefTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes);
+
+bool ParseCoverageTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t expected_num_glyphs = 0);
+
+bool ParseDeviceTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, size_t length);
+
+// Parser for 'Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseContextSubtable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups);
+
+// Parser for 'Chaining Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseChainingContextSubtable(const ots::OpenTypeFile *file,
+                                  const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups);
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser);
+
+}  // namespace ots
+
+#endif  // OTS_LAYOUT_H_
+
diff --git a/third_party/ots/src/loca.cc b/third_party/ots/src/loca.cc
new file mode 100644
index 0000000..4b291f0
--- /dev/null
+++ b/third_party/ots/src/loca.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "loca.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// loca - Index to Location
+// http://www.microsoft.com/typography/otspec/loca.htm
+
+#define TABLE_NAME "loca"
+
+namespace ots {
+
+bool ots_loca_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  // We can't do anything useful in validating this data except to ensure that
+  // the values are monotonically increasing.
+
+  OpenTypeLOCA *loca = new OpenTypeLOCA;
+  file->loca = loca;
+
+  if (!file->maxp || !file->head) {
+    return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca");
+  }
+
+  const unsigned num_glyphs = file->maxp->num_glyphs;
+  unsigned last_offset = 0;
+  loca->offsets.resize(num_glyphs + 1);
+  // maxp->num_glyphs is uint16_t, thus the addition never overflows.
+
+  if (file->head->index_to_loc_format == 0) {
+    // Note that the <= here (and below) is correct. There is one more offset
+    // than the number of glyphs in order to give the length of the final
+    // glyph.
+    for (unsigned i = 0; i <= num_glyphs; ++i) {
+      uint16_t offset = 0;
+      if (!table.ReadU16(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset * 2;
+    }
+  } else {
+    for (unsigned i = 0; i <= num_glyphs; ++i) {
+      uint32_t offset = 0;
+      if (!table.ReadU32(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset;
+    }
+  }
+
+  return true;
+}
+
+bool ots_loca_should_serialise(OpenTypeFile *file) {
+  return file->loca != NULL;
+}
+
+bool ots_loca_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLOCA *loca = file->loca;
+  const OpenTypeHEAD *head = file->head;
+
+  if (!head) {
+    return OTS_FAILURE_MSG("Missing head table in font needed by loca");
+  }
+
+  if (head->index_to_loc_format == 0) {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      const uint16_t offset = static_cast<uint16_t>(loca->offsets[i] >> 1);
+      if ((offset != (loca->offsets[i] >> 1)) ||
+          !out->WriteU16(offset)) {
+        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+      }
+    }
+  } else {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      if (!out->WriteU32(loca->offsets[i])) {
+        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_loca_free(OpenTypeFile *file) {
+  delete file->loca;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/loca.h b/third_party/ots/src/loca.h
new file mode 100644
index 0000000..255ef06
--- /dev/null
+++ b/third_party/ots/src/loca.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LOCA_H_
+#define OTS_LOCA_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLOCA {
+  std::vector<uint32_t> offsets;
+};
+
+}  // namespace ots
+
+#endif  // OTS_LOCA_H_
diff --git a/third_party/ots/src/ltsh.cc b/third_party/ots/src/ltsh.cc
new file mode 100644
index 0000000..418c159
--- /dev/null
+++ b/third_party/ots/src/ltsh.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ltsh.h"
+
+#include "maxp.h"
+
+// LTSH - Linear Threshold
+// http://www.microsoft.com/typography/otspec/ltsh.htm
+
+#define TABLE_NAME "LTSH"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->ltsh; \
+    file->ltsh = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_ltsh_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh");
+  }
+
+  OpenTypeLTSH *ltsh = new OpenTypeLTSH;
+  file->ltsh = ltsh;
+
+  uint16_t num_glyphs = 0;
+  if (!table.ReadU16(&ltsh->version) ||
+      !table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read ltsh header");
+  }
+
+  if (ltsh->version != 0) {
+    DROP_THIS_TABLE("bad version: %u", ltsh->version);
+    return true;
+  }
+
+  if (num_glyphs != file->maxp->num_glyphs) {
+    DROP_THIS_TABLE("bad num_glyphs: %u", num_glyphs);
+    return true;
+  }
+
+  ltsh->ypels.reserve(num_glyphs);
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    uint8_t pel = 0;
+    if (!table.ReadU8(&pel)) {
+      return OTS_FAILURE_MSG("Failed to read pixels for glyph %d", i);
+    }
+    ltsh->ypels.push_back(pel);
+  }
+
+  return true;
+}
+
+bool ots_ltsh_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->ltsh != NULL;
+}
+
+bool ots_ltsh_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLTSH *ltsh = file->ltsh;
+
+  const uint16_t num_ypels = static_cast<uint16_t>(ltsh->ypels.size());
+  if (num_ypels != ltsh->ypels.size() ||
+      !out->WriteU16(ltsh->version) ||
+      !out->WriteU16(num_ypels)) {
+    return OTS_FAILURE_MSG("Failed to write pels size");
+  }
+  for (uint16_t i = 0; i < num_ypels; ++i) {
+    if (!out->Write(&(ltsh->ypels[i]), 1)) {
+      return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_ltsh_free(OpenTypeFile *file) {
+  delete file->ltsh;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/ltsh.h b/third_party/ots/src/ltsh.h
new file mode 100644
index 0000000..23d97d7
--- /dev/null
+++ b/third_party/ots/src/ltsh.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LTSH_H_
+#define OTS_LTSH_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLTSH {
+  uint16_t version;
+  std::vector<uint8_t> ypels;
+};
+
+}  // namespace ots
+
+#endif  // OTS_LTSH_H_
diff --git a/third_party/ots/src/math.cc b/third_party/ots/src/math.cc
new file mode 100644
index 0000000..9124a88
--- /dev/null
+++ b/third_party/ots/src/math.cc
@@ -0,0 +1,609 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// We use an underscore to avoid confusion with the standard math.h library.
+#include "math_.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// MATH - The MATH Table
+// The specification is not yet public but has been submitted to the MPEG group
+// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
+// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
+// contact Microsoft's engineer Murray Sargent to obtain a copy.
+
+#define TABLE_NAME "MATH"
+
+namespace {
+
+// The size of MATH header.
+// Version
+// MathConstants
+// MathGlyphInfo
+// MathVariants
+const unsigned kMathHeaderSize = 4 + 3 * 2;
+
+// The size of the MathGlyphInfo header.
+// MathItalicsCorrectionInfo
+// MathTopAccentAttachment
+// ExtendedShapeCoverage
+// MathKernInfo
+const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
+
+// The size of the MathValueRecord.
+// Value
+// DeviceTable
+const unsigned kMathValueRecordSize = 2 * 2;
+
+// The size of the GlyphPartRecord.
+// glyph
+// StartConnectorLength
+// EndConnectorLength
+// FullAdvance
+// PartFlags
+const unsigned kGlyphPartRecordSize = 5 * 2;
+
+// Shared Table: MathValueRecord
+
+bool ParseMathValueRecord(const ots::OpenTypeFile *file,
+                          ots::Buffer* subtable, const uint8_t *data,
+                          const size_t length) {
+  // Check the Value field.
+  if (!subtable->Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  // Check the offset to device table.
+  uint16_t offset = 0;
+  if (!subtable->ReadU16(&offset)) {
+    return OTS_FAILURE();
+  }
+  if (offset) {
+    if (offset >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathConstantsTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, size_t length) {
+  ots::Buffer subtable(data, length);
+
+  // Part 1: int16 or uint16 constants.
+  //  ScriptPercentScaleDown
+  //  ScriptScriptPercentScaleDown
+  //  DelimitedSubFormulaMinHeight
+  //  DisplayOperatorMinHeight
+  if (!subtable.Skip(4 * 2)) {
+    return OTS_FAILURE();
+  }
+
+  // Part 2: MathValueRecord constants.
+  // MathLeading
+  // AxisHeight
+  // AccentBaseHeight
+  // FlattenedAccentBaseHeight
+  // SubscriptShiftDown
+  // SubscriptTopMax
+  // SubscriptBaselineDropMin
+  // SuperscriptShiftUp
+  // SuperscriptShiftUpCramped
+  // SuperscriptBottomMin
+  //
+  // SuperscriptBaselineDropMax
+  // SubSuperscriptGapMin
+  // SuperscriptBottomMaxWithSubscript
+  // SpaceAfterScript
+  // UpperLimitGapMin
+  // UpperLimitBaselineRiseMin
+  // LowerLimitGapMin
+  // LowerLimitBaselineDropMin
+  // StackTopShiftUp
+  // StackTopDisplayStyleShiftUp
+  //
+  // StackBottomShiftDown
+  // StackBottomDisplayStyleShiftDown
+  // StackGapMin
+  // StackDisplayStyleGapMin
+  // StretchStackTopShiftUp
+  // StretchStackBottomShiftDown
+  // StretchStackGapAboveMin
+  // StretchStackGapBelowMin
+  // FractionNumeratorShiftUp
+  // FractionNumeratorDisplayStyleShiftUp
+  //
+  // FractionDenominatorShiftDown
+  // FractionDenominatorDisplayStyleShiftDown
+  // FractionNumeratorGapMin
+  // FractionNumDisplayStyleGapMin
+  // FractionRuleThickness
+  // FractionDenominatorGapMin
+  // FractionDenomDisplayStyleGapMin
+  // SkewedFractionHorizontalGap
+  // SkewedFractionVerticalGap
+  // OverbarVerticalGap
+  //
+  // OverbarRuleThickness
+  // OverbarExtraAscender
+  // UnderbarVerticalGap
+  // UnderbarRuleThickness
+  // UnderbarExtraDescender
+  // RadicalVerticalGap
+  // RadicalDisplayStyleVerticalGap
+  // RadicalRuleThickness
+  // RadicalExtraAscender
+  // RadicalKernBeforeDegree
+  //
+  // RadicalKernAfterDegree
+  for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Part 3: uint16 constant
+  // RadicalDegreeBottomRaisePercent
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile *file,
+                                           ots::Buffer* subtable,
+                                           const uint8_t *data,
+                                           const size_t length,
+                                           const uint16_t num_glyphs) {
+  // Check the header.
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+  if (!subtable->ReadU16(&offset_coverage) ||
+      !subtable->ReadU16(&sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+      sequence_count * kMathValueRecordSize;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               num_glyphs, sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence.
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    if (!ParseMathValueRecord(file, subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile *file,
+                                         const uint8_t *data,
+                                         size_t length,
+                                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
+                                               num_glyphs);
+}
+
+bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile *file,
+                                       const uint8_t *data,
+                                       size_t length,
+                                       const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
+                                               num_glyphs);
+}
+
+bool ParseMathKernTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length) {
+  ots::Buffer subtable(data, length);
+
+  // Check the Height count.
+  uint16_t height_count = 0;
+  if (!subtable.ReadU16(&height_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check the Correction Heights.
+  for (unsigned i = 0; i < height_count; ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check the Kern Values.
+  for (unsigned i = 0; i <= height_count; ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathKernInfoTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, size_t length,
+                            const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+    sequence_count * 4 * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage,
+                               num_glyphs, sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence of MathKernInfoRecord
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
+    for (unsigned j = 0; j < 4; ++j) {
+      uint16_t offset_math_kern = 0;
+      if (!subtable.ReadU16(&offset_math_kern)) {
+        return OTS_FAILURE();
+      }
+      if (offset_math_kern) {
+        if (offset_math_kern < sequence_end || offset_math_kern >= length ||
+            !ParseMathKernTable(file, data + offset_math_kern,
+                                length - offset_math_kern)) {
+          return OTS_FAILURE();
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphInfoTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, size_t length,
+                             const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check Header.
+  uint16_t offset_math_italics_correction_info = 0;
+  uint16_t offset_math_top_accent_attachment = 0;
+  uint16_t offset_extended_shaped_coverage = 0;
+  uint16_t offset_math_kern_info = 0;
+  if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
+      !subtable.ReadU16(&offset_math_top_accent_attachment) ||
+      !subtable.ReadU16(&offset_extended_shaped_coverage) ||
+      !subtable.ReadU16(&offset_math_kern_info)) {
+    return OTS_FAILURE();
+  }
+
+  // Check subtables.
+  // The specification does not say whether the offsets for
+  // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
+  // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
+  if (offset_math_italics_correction_info) {
+    if (offset_math_italics_correction_info >= length ||
+        offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
+        !ParseMathItalicsCorrectionInfoTable(
+            file, data + offset_math_italics_correction_info,
+            length - offset_math_italics_correction_info,
+            num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_math_top_accent_attachment) {
+    if (offset_math_top_accent_attachment >= length ||
+        offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
+        !ParseMathTopAccentAttachmentTable(file, data +
+                                           offset_math_top_accent_attachment,
+                                           length -
+                                           offset_math_top_accent_attachment,
+                                           num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_extended_shaped_coverage) {
+    if (offset_extended_shaped_coverage >= length ||
+        offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
+        !ots::ParseCoverageTable(file, data + offset_extended_shaped_coverage,
+                                 length - offset_extended_shaped_coverage,
+                                 num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_math_kern_info) {
+    if (offset_math_kern_info >= length ||
+        offset_math_kern_info < kMathGlyphInfoHeaderSize ||
+        !ParseMathKernInfoTable(file, data + offset_math_kern_info,
+                                length - offset_math_kern_info, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseGlyphAssemblyTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data,
+                             size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t part_count = 0;
+  if (!ParseMathValueRecord(file, &subtable, data, length) ||
+      !subtable.ReadU16(&part_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = kMathValueRecordSize +
+    static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check the sequence of GlyphPartRecord.
+  for (unsigned i = 0; i < part_count; ++i) {
+    uint16_t glyph = 0;
+    uint16_t part_flags = 0;
+    if (!subtable.ReadU16(&glyph) ||
+        !subtable.Skip(2 * 3) ||
+        !subtable.ReadU16(&part_flags)) {
+      return OTS_FAILURE();
+    }
+    if (glyph >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+    if (part_flags & ~0x00000001) {
+      return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file,
+                                     const uint8_t *data,
+                                     size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_glyph_assembly = 0;
+  uint16_t variant_count = 0;
+  if (!subtable.ReadU16(&offset_glyph_assembly) ||
+      !subtable.ReadU16(&variant_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+    variant_count * 2 * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check the GlyphAssembly offset.
+  if (offset_glyph_assembly) {
+    if (offset_glyph_assembly >= length ||
+        offset_glyph_assembly < sequence_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseGlyphAssemblyTable(file, data + offset_glyph_assembly,
+                                 length - offset_glyph_assembly, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check the sequence of MathGlyphVariantRecord.
+  for (unsigned i = 0; i < variant_count; ++i) {
+    uint16_t glyph = 0;
+    if (!subtable.ReadU16(&glyph) ||
+        !subtable.Skip(2)) {
+      return OTS_FAILURE();
+    }
+    if (glyph >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file,
+                                        ots::Buffer* subtable,
+                                        const uint8_t *data,
+                                        size_t length,
+                                        const uint16_t num_glyphs,
+                                        uint16_t offset_coverage,
+                                        uint16_t glyph_count,
+                                        const unsigned sequence_end) {
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               num_glyphs, glyph_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence of MathGlyphConstruction.
+  for (unsigned i = 0; i < glyph_count; ++i) {
+      uint16_t offset_glyph_construction = 0;
+      if (!subtable->ReadU16(&offset_glyph_construction)) {
+        return OTS_FAILURE();
+      }
+      if (offset_glyph_construction < sequence_end ||
+          offset_glyph_construction >= length ||
+          !ParseMathGlyphConstructionTable(file, data + offset_glyph_construction,
+                                           length - offset_glyph_construction,
+                                           num_glyphs)) {
+        return OTS_FAILURE();
+      }
+  }
+
+  return true;
+}
+
+bool ParseMathVariantsTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data,
+                            size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_vert_glyph_coverage = 0;
+  uint16_t offset_horiz_glyph_coverage = 0;
+  uint16_t vert_glyph_count = 0;
+  uint16_t horiz_glyph_count = 0;
+  if (!subtable.Skip(2) ||  // MinConnectorOverlap
+      !subtable.ReadU16(&offset_vert_glyph_coverage) ||
+      !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
+      !subtable.ReadU16(&vert_glyph_count) ||
+      !subtable.ReadU16(&horiz_glyph_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
+    horiz_glyph_count * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  if (!ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
+                                          offset_vert_glyph_coverage,
+                                          vert_glyph_count,
+                                          sequence_end) ||
+      !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
+                                          offset_horiz_glyph_coverage,
+                                          horiz_glyph_count,
+                                          sequence_end)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->math->data = 0; \
+    file->math->length = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Grab the number of glyphs in the file from the maxp table to check
+  // GlyphIDs in MATH table.
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  Buffer table(data, length);
+
+  OpenTypeMATH* math = new OpenTypeMATH;
+  file->math = math;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("bad MATH version");
+    return true;
+  }
+
+  uint16_t offset_math_constants = 0;
+  uint16_t offset_math_glyph_info = 0;
+  uint16_t offset_math_variants = 0;
+  if (!table.ReadU16(&offset_math_constants) ||
+      !table.ReadU16(&offset_math_glyph_info) ||
+      !table.ReadU16(&offset_math_variants)) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_math_constants >= length ||
+      offset_math_constants < kMathHeaderSize ||
+      offset_math_glyph_info >= length ||
+      offset_math_glyph_info < kMathHeaderSize ||
+      offset_math_variants >= length ||
+      offset_math_variants < kMathHeaderSize) {
+    DROP_THIS_TABLE("bad offset in MATH header");
+    return true;
+  }
+
+  if (!ParseMathConstantsTable(file, data + offset_math_constants,
+                               length - offset_math_constants)) {
+    DROP_THIS_TABLE("failed to parse MathConstants table");
+    return true;
+  }
+  if (!ParseMathGlyphInfoTable(file, data + offset_math_glyph_info,
+                               length - offset_math_glyph_info, num_glyphs)) {
+    DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
+    return true;
+  }
+  if (!ParseMathVariantsTable(file, data + offset_math_variants,
+                              length - offset_math_variants, num_glyphs)) {
+    DROP_THIS_TABLE("failed to parse MathVariants table");
+    return true;
+  }
+
+  math->data = data;
+  math->length = length;
+  return true;
+}
+
+bool ots_math_should_serialise(OpenTypeFile *file) {
+  return file->math != NULL && file->math->data != NULL;
+}
+
+bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->math->data, file->math->length)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_math_free(OpenTypeFile *file) {
+  delete file->math;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/math_.h b/third_party/ots/src/math_.h
new file mode 100644
index 0000000..91c54db
--- /dev/null
+++ b/third_party/ots/src/math_.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MATH_H_
+#define OTS_MATH_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMATH {
+  OpenTypeMATH()
+      : data(NULL),
+        length(0) {
+  }
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/maxp.cc b/third_party/ots/src/maxp.cc
new file mode 100644
index 0000000..aaf0076
--- /dev/null
+++ b/third_party/ots/src/maxp.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "maxp.h"
+
+// maxp - Maximum Profile
+// http://www.microsoft.com/typography/otspec/maxp.htm
+
+#define TABLE_NAME "maxp"
+
+namespace ots {
+
+bool ots_maxp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeMAXP *maxp = new OpenTypeMAXP;
+  file->maxp = maxp;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE_MSG("Failed to read version of maxp table");
+  }
+
+  if (version >> 16 > 1) {
+    return OTS_FAILURE_MSG("Bad maxp version %d", version);
+  }
+
+  if (!table.ReadU16(&maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table");
+  }
+
+  if (!maxp->num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table");
+  }
+
+  if (version >> 16 == 1) {
+    maxp->version_1 = true;
+    if (!table.ReadU16(&maxp->max_points) ||
+        !table.ReadU16(&maxp->max_contours) ||
+        !table.ReadU16(&maxp->max_c_points) ||
+        !table.ReadU16(&maxp->max_c_contours) ||
+        !table.ReadU16(&maxp->max_zones) ||
+        !table.ReadU16(&maxp->max_t_points) ||
+        !table.ReadU16(&maxp->max_storage) ||
+        !table.ReadU16(&maxp->max_fdefs) ||
+        !table.ReadU16(&maxp->max_idefs) ||
+        !table.ReadU16(&maxp->max_stack) ||
+        !table.ReadU16(&maxp->max_size_glyf_instructions) ||
+        !table.ReadU16(&maxp->max_c_components) ||
+        !table.ReadU16(&maxp->max_c_depth)) {
+      return OTS_FAILURE_MSG("Failed to read maxp table");
+    }
+
+    if (maxp->max_zones == 0) {
+      // workaround for ipa*.ttf Japanese fonts.
+      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+      maxp->max_zones = 1;
+    } else if (maxp->max_zones == 3) {
+      // workaround for Ecolier-*.ttf fonts.
+      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+      maxp->max_zones = 2;
+    }
+
+    if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
+      return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones);
+    }
+  } else {
+    maxp->version_1 = false;
+  }
+
+  return true;
+}
+
+bool ots_maxp_should_serialise(OpenTypeFile *file) {
+  return file->maxp != NULL;
+}
+
+bool ots_maxp_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeMAXP *maxp = file->maxp;
+
+  if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
+      !out->WriteU16(maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs");
+  }
+
+  if (!maxp->version_1) return true;
+
+  if (!out->WriteU16(maxp->max_points) ||
+      !out->WriteU16(maxp->max_contours) ||
+      !out->WriteU16(maxp->max_c_points) ||
+      !out->WriteU16(maxp->max_c_contours)) {
+    return OTS_FAILURE_MSG("Failed to write maxp");
+  }
+
+  if (!out->WriteU16(maxp->max_zones) ||
+      !out->WriteU16(maxp->max_t_points) ||
+      !out->WriteU16(maxp->max_storage) ||
+      !out->WriteU16(maxp->max_fdefs) ||
+      !out->WriteU16(maxp->max_idefs) ||
+      !out->WriteU16(maxp->max_stack) ||
+      !out->WriteU16(maxp->max_size_glyf_instructions)) {
+    return OTS_FAILURE_MSG("Failed to write more maxp");
+  }
+
+  if (!out->WriteU16(maxp->max_c_components) ||
+      !out->WriteU16(maxp->max_c_depth)) {
+    return OTS_FAILURE_MSG("Failed to write yet more maxp");
+  }
+
+  return true;
+}
+
+void ots_maxp_free(OpenTypeFile *file) {
+  delete file->maxp;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/maxp.h b/third_party/ots/src/maxp.h
new file mode 100644
index 0000000..efca0c9
--- /dev/null
+++ b/third_party/ots/src/maxp.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MAXP_H_
+#define OTS_MAXP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMAXP {
+  uint16_t num_glyphs;
+  bool version_1;
+
+  uint16_t max_points;
+  uint16_t max_contours;
+  uint16_t max_c_points;
+  uint16_t max_c_contours;
+
+  uint16_t max_zones;
+  uint16_t max_t_points;
+  uint16_t max_storage;
+  uint16_t max_fdefs;
+  uint16_t max_idefs;
+  uint16_t max_stack;
+  uint16_t max_size_glyf_instructions;
+
+  uint16_t max_c_components;
+  uint16_t max_c_depth;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MAXP_H_
diff --git a/third_party/ots/src/metrics.cc b/third_party/ots/src/metrics.cc
new file mode 100644
index 0000000..8d59b95
--- /dev/null
+++ b/third_party/ots/src/metrics.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "metrics.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// OpenType horizontal and vertical common header format
+// http://www.microsoft.com/typography/otspec/hhea.htm
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "metrics" // XXX: use individual table names
+
+namespace ots {
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header) {
+  if (!table->ReadS16(&header->ascent) ||
+      !table->ReadS16(&header->descent) ||
+      !table->ReadS16(&header->linegap) ||
+      !table->ReadU16(&header->adv_width_max) ||
+      !table->ReadS16(&header->min_sb1) ||
+      !table->ReadS16(&header->min_sb2) ||
+      !table->ReadS16(&header->max_extent) ||
+      !table->ReadS16(&header->caret_slope_rise) ||
+      !table->ReadS16(&header->caret_slope_run) ||
+      !table->ReadS16(&header->caret_offset)) {
+    return OTS_FAILURE_MSG("Failed to read metrics header");
+  }
+
+  if (header->ascent < 0) {
+    OTS_WARNING("bad ascent: %d", header->ascent);
+    header->ascent = 0;
+  }
+  if (header->linegap < 0) {
+    OTS_WARNING("bad linegap: %d", header->linegap);
+    header->linegap = 0;
+  }
+
+  if (!file->head) {
+    return OTS_FAILURE_MSG("Missing head font table");
+  }
+
+  // if the font is non-slanted, caret_offset should be zero.
+  if (!(file->head->mac_style & 2) &&
+      (header->caret_offset != 0)) {
+    OTS_WARNING("bad caret offset: %d", header->caret_offset);
+    header->caret_offset = 0;
+  }
+
+  // skip the reserved bytes
+  if (!table->Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to skip reserverd bytes");
+  }
+
+  int16_t data_format;
+  if (!table->ReadS16(&data_format)) {
+    return OTS_FAILURE_MSG("Failed to read data format");
+  }
+  if (data_format) {
+    return OTS_FAILURE_MSG("Bad data format %d", data_format);
+  }
+
+  if (!table->ReadU16(&header->num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to read number of metrics");
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp font table");
+  }
+
+  if (header->num_metrics > file->maxp->num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics);
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsHeader(const ots::OpenTypeFile *file,
+                            OTSStream *out,
+                            const OpenTypeMetricsHeader *header) {
+  if (!out->WriteU32(header->version) ||
+      !out->WriteS16(header->ascent) ||
+      !out->WriteS16(header->descent) ||
+      !out->WriteS16(header->linegap) ||
+      !out->WriteU16(header->adv_width_max) ||
+      !out->WriteS16(header->min_sb1) ||
+      !out->WriteS16(header->min_sb2) ||
+      !out->WriteS16(header->max_extent) ||
+      !out->WriteS16(header->caret_slope_rise) ||
+      !out->WriteS16(header->caret_slope_run) ||
+      !out->WriteS16(header->caret_offset) ||
+      !out->WriteR64(0) ||  // reserved
+      !out->WriteS16(0) ||  // metric data format
+      !out->WriteU16(header->num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to write metrics");
+  }
+
+  return true;
+}
+
+bool ParseMetricsTable(const ots::OpenTypeFile *file,
+                       Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics) {
+  // |num_metrics| is a uint16_t, so it's bounded < 65536. This limits that
+  // amount of memory that we'll allocate for this to a sane amount.
+  const unsigned num_metrics = header->num_metrics;
+
+  if (num_metrics > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of metrics %d", num_metrics);
+  }
+  if (!num_metrics) {
+    return OTS_FAILURE_MSG("No metrics!");
+  }
+  const unsigned num_sbs = num_glyphs - num_metrics;
+
+  metrics->entries.reserve(num_metrics);
+  for (unsigned i = 0; i < num_metrics; ++i) {
+    uint16_t adv = 0;
+    int16_t sb = 0;
+    if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
+      return OTS_FAILURE_MSG("Failed to read metric %d", i);
+    }
+
+    // This check is bogus, see https://github.com/khaledhosny/ots/issues/36
+#if 0
+    // Since so many fonts don't have proper value on |adv| and |sb|,
+    // we should not call ots_failure() here. For example, about 20% of fonts
+    // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
+    if (adv > header->adv_width_max) {
+      OTS_WARNING("bad adv: %u > %u", adv, header->adv_width_max);
+      adv = header->adv_width_max;
+    }
+
+    if (sb < header->min_sb1) {
+      OTS_WARNING("bad sb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+#endif
+
+    metrics->entries.push_back(std::make_pair(adv, sb));
+  }
+
+  metrics->sbs.reserve(num_sbs);
+  for (unsigned i = 0; i < num_sbs; ++i) {
+    int16_t sb;
+    if (!table->ReadS16(&sb)) {
+      // Some Japanese fonts (e.g., mona.ttf) fail this test.
+      return OTS_FAILURE_MSG("Failed to read side bearing %d", i + num_metrics);
+    }
+
+    // This check is bogus, see https://github.com/khaledhosny/ots/issues/36
+#if 0
+    if (sb < header->min_sb1) {
+      // The same as above. Three fonts in http://www.fontsquirrel.com/fontface
+      // (e.g., Notice2Std.otf) have weird lsb values.
+      OTS_WARNING("bad lsb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+#endif
+
+    metrics->sbs.push_back(sb);
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsTable(const ots::OpenTypeFile *file,
+                           OTSStream *out,
+                           const OpenTypeMetricsTable *metrics) {
+  for (unsigned i = 0; i < metrics->entries.size(); ++i) {
+    if (!out->WriteU16(metrics->entries[i].first) ||
+        !out->WriteS16(metrics->entries[i].second)) {
+      return OTS_FAILURE_MSG("Failed to write metric %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
+    if (!out->WriteS16(metrics->sbs[i])) {
+      return OTS_FAILURE_MSG("Failed to write side bearing %ld", i + metrics->entries.size());
+    }
+  }
+
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/metrics.h b/third_party/ots/src/metrics.h
new file mode 100644
index 0000000..f0b4ee8
--- /dev/null
+++ b/third_party/ots/src/metrics.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_METRICS_H_
+#define OTS_METRICS_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMetricsHeader {
+  uint32_t version;
+  int16_t ascent;
+  int16_t descent;
+  int16_t linegap;
+  uint16_t adv_width_max;
+  int16_t min_sb1;
+  int16_t min_sb2;
+  int16_t max_extent;
+  int16_t caret_slope_rise;
+  int16_t caret_slope_run;
+  int16_t caret_offset;
+  uint16_t num_metrics;
+};
+
+struct OpenTypeMetricsTable {
+  std::vector<std::pair<uint16_t, int16_t> > entries;
+  std::vector<int16_t> sbs;
+};
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header);
+bool SerialiseMetricsHeader(const ots::OpenTypeFile *file,
+                            OTSStream *out,
+                            const OpenTypeMetricsHeader *header);
+
+bool ParseMetricsTable(const ots::OpenTypeFile *file,
+                       Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics);
+bool SerialiseMetricsTable(const ots::OpenTypeFile *file,
+                           OTSStream *out,
+                           const OpenTypeMetricsTable *metrics);
+
+}  // namespace ots
+
+#endif  // OTS_METRICS_H_
+
diff --git a/third_party/ots/src/name.cc b/third_party/ots/src/name.cc
new file mode 100644
index 0000000..2ea10dc
--- /dev/null
+++ b/third_party/ots/src/name.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "name.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "cff.h"
+
+// name - Naming Table
+// http://www.microsoft.com/typography/otspec/name.htm
+
+#define TABLE_NAME "name"
+
+namespace {
+
+bool ValidInPsName(char c) {
+  return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
+}
+
+bool CheckPsNameAscii(const std::string& name) {
+  for (unsigned i = 0; i < name.size(); ++i) {
+    if (!ValidInPsName(name[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool CheckPsNameUtf16Be(const std::string& name) {
+  if ((name.size() & 1) != 0)
+    return false;
+
+  for (unsigned i = 0; i < name.size(); i += 2) {
+    if (name[i] != 0) {
+      return false;
+    }
+    if (!ValidInPsName(name[i+1])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void AssignToUtf16BeFromAscii(std::string* target,
+                              const std::string& source) {
+  target->resize(source.size() * 2);
+  for (unsigned i = 0, j = 0; i < source.size(); i++) {
+    (*target)[j++] = '\0';
+    (*target)[j++] = source[i];
+  }
+}
+
+}  // namespace
+
+
+namespace ots {
+
+bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeNAME* name = new OpenTypeNAME;
+  file->name = name;
+
+  uint16_t format = 0;
+  if (!table.ReadU16(&format) || format > 1) {
+    return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
+  }
+
+  uint16_t count = 0;
+  if (!table.ReadU16(&count)) {
+    return OTS_FAILURE_MSG("Failed to read name count");
+  }
+
+  uint16_t string_offset = 0;
+  if (!table.ReadU16(&string_offset) || string_offset > length) {
+    return OTS_FAILURE_MSG("Failed to read strings offset");
+  }
+  const char* string_base = reinterpret_cast<const char*>(data) +
+      string_offset;
+
+  NameRecord prev_record;
+  bool sort_required = false;
+
+  // Read all the names, discarding any with invalid IDs,
+  // and any where the offset/length would be outside the table.
+  // A stricter alternative would be to reject the font if there
+  // are invalid name records, but it's not clear that is necessary.
+  for (unsigned i = 0; i < count; ++i) {
+    NameRecord rec;
+    uint16_t name_length, name_offset = 0;
+    if (!table.ReadU16(&rec.platform_id) ||
+        !table.ReadU16(&rec.encoding_id) ||
+        !table.ReadU16(&rec.language_id) ||
+        !table.ReadU16(&rec.name_id) ||
+        !table.ReadU16(&name_length) ||
+        !table.ReadU16(&name_offset)) {
+      return OTS_FAILURE_MSG("Failed to read name entry %d", i);
+    }
+    // check platform & encoding, discard names with unknown values
+    switch (rec.platform_id) {
+      case 0:  // Unicode
+        if (rec.encoding_id > 6) {
+          continue;
+        }
+        break;
+      case 1:  // Macintosh
+        if (rec.encoding_id > 32) {
+          continue;
+        }
+        break;
+      case 2:  // ISO
+        if (rec.encoding_id > 2) {
+          continue;
+        }
+        break;
+      case 3:  // Windows: IDs 7 to 9 are "reserved"
+        if (rec.encoding_id > 6 && rec.encoding_id != 10) {
+          continue;
+        }
+        break;
+      case 4:  // Custom (OTF Windows NT compatibility)
+        if (rec.encoding_id > 255) {
+          continue;
+        }
+        break;
+      default:  // unknown platform
+        continue;
+    }
+
+    const unsigned name_end = static_cast<unsigned>(string_offset) +
+        name_offset + name_length;
+    if (name_end > length) {
+      continue;
+    }
+    rec.text.resize(name_length);
+    rec.text.assign(string_base + name_offset, name_length);
+
+    if (rec.name_id == 6) {
+      // PostScript name: check that it is valid, if not then discard it
+      if (rec.platform_id == 1) {
+        if (file->cff && !file->cff->name.empty()) {
+          rec.text = file->cff->name;
+        } else if (!CheckPsNameAscii(rec.text)) {
+          continue;
+        }
+      } else if (rec.platform_id == 0 || rec.platform_id == 3) {
+        if (file->cff && !file->cff->name.empty()) {
+          AssignToUtf16BeFromAscii(&rec.text, file->cff->name);
+        } else if (!CheckPsNameUtf16Be(rec.text)) {
+          continue;
+        }
+      }
+    }
+
+    if ((i > 0) && !(prev_record < rec)) {
+      OTS_WARNING("name records are not sorted.");
+      sort_required = true;
+    }
+
+    name->names.push_back(rec);
+    prev_record = rec;
+  }
+
+  if (format == 1) {
+    // extended name table format with language tags
+    uint16_t lang_tag_count;
+    if (!table.ReadU16(&lang_tag_count)) {
+      return OTS_FAILURE_MSG("Failed to read language tag count");
+    }
+    for (unsigned i = 0; i < lang_tag_count; ++i) {
+      uint16_t tag_length = 0;
+      uint16_t tag_offset = 0;
+      if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
+        return OTS_FAILURE_MSG("Faile to read tag length or offset");
+      }
+      const unsigned tag_end = static_cast<unsigned>(string_offset) +
+          tag_offset + tag_length;
+      if (tag_end > length) {
+        return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
+      }
+      std::string tag(string_base + tag_offset, tag_length);
+      name->lang_tags.push_back(tag);
+    }
+  }
+
+  if (table.offset() > string_offset) {
+    // the string storage apparently overlapped the name/tag records;
+    // consider this font to be badly broken
+    return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
+  }
+
+  // check existence of required name strings (synthesize if necessary)
+  //  [0 - copyright - skip]
+  //   1 - family
+  //   2 - subfamily
+  //  [3 - unique ID - skip]
+  //   4 - full name
+  //   5 - version
+  //   6 - postscript name
+  static const uint16_t kStdNameCount = 7;
+  static const char* kStdNames[kStdNameCount] = {
+    NULL,
+    "OTS derived font",
+    "Unspecified",
+    NULL,
+    "OTS derived font",
+    "1.000",
+    "OTS-derived-font"
+  };
+  // The spec says that "In CFF OpenType fonts, these two name strings, when
+  // translated to ASCII, must also be identical to the font name as stored in
+  // the CFF's Name INDEX." And actually, Mac OS X's font parser requires that.
+  if (file->cff && !file->cff->name.empty()) {
+    kStdNames[6] = file->cff->name.c_str();
+  }
+
+  // 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 = name->names.begin();
+       name_iter != name->names.end(); name_iter++) {
+    const uint16_t id = name_iter->name_id;
+    if (id >= kStdNameCount || kStdNames[id] == NULL) {
+      continue;
+    }
+    if (name_iter->platform_id == 1) {
+      mac_name[id] = true;
+      continue;
+    }
+    if (name_iter->platform_id == 3) {
+      win_name[id] = true;
+      continue;
+    }
+  }
+
+  for (uint16_t i = 0; i < kStdNameCount; ++i) {
+    if (kStdNames[i] == NULL) {
+      continue;
+    }
+    if (!mac_name[i]) {
+      NameRecord rec(1 /* platform_id */, 0 /* encoding_id */,
+                     0 /* language_id */ , i /* name_id */);
+      rec.text.assign(kStdNames[i]);
+      name->names.push_back(rec);
+      sort_required = true;
+    }
+    if (!win_name[i]) {
+      NameRecord rec(3 /* platform_id */, 1 /* encoding_id */,
+                     1033 /* language_id */ , i /* name_id */);
+      AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i]));
+      name->names.push_back(rec);
+      sort_required = true;
+    }
+  }
+
+  if (sort_required) {
+    std::sort(name->names.begin(), name->names.end());
+  }
+
+  return true;
+}
+
+bool ots_name_should_serialise(OpenTypeFile* file) {
+  return file->name != NULL;
+}
+
+bool ots_name_serialise(OTSStream* out, OpenTypeFile* file) {
+  const OpenTypeNAME* name = file->name;
+
+  uint16_t name_count = static_cast<uint16_t>(name->names.size());
+  uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
+  uint16_t format = 0;
+  size_t string_offset = 6 + name_count * 12;
+
+  if (name->lang_tags.size() > 0) {
+    // lang tags require a format-1 name table
+    format = 1;
+    string_offset += 2 + lang_tag_count * 4;
+  }
+  if (string_offset > 0xffff) {
+    return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
+  }
+  if (!out->WriteU16(format) ||
+      !out->WriteU16(name_count) ||
+      !out->WriteU16(static_cast<uint16_t>(string_offset))) {
+    return OTS_FAILURE_MSG("Failed to write name header");
+  }
+
+  std::string string_data;
+  for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
+       name_iter != name->names.end(); name_iter++) {
+    const NameRecord& rec = *name_iter;
+    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())) ) {
+      return OTS_FAILURE_MSG("Faile to write name entry");
+    }
+    string_data.append(rec.text);
+  }
+
+  if (format == 1) {
+    if (!out->WriteU16(lang_tag_count)) {
+      return OTS_FAILURE_MSG("Faile to write language tag count");
+    }
+    for (std::vector<std::string>::const_iterator tag_iter =
+             name->lang_tags.begin();
+         tag_iter != name->lang_tags.end(); tag_iter++) {
+      if (string_data.size() + tag_iter->size() >
+              std::numeric_limits<uint16_t>::max() ||
+          !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
+          !out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
+        return OTS_FAILURE_MSG("Failed to write string");
+      }
+      string_data.append(*tag_iter);
+    }
+  }
+
+  if (!out->Write(string_data.data(), string_data.size())) {
+    return OTS_FAILURE_MSG("Faile to write string data");
+  }
+
+  return true;
+}
+
+void ots_name_free(OpenTypeFile* file) {
+  delete file->name;
+}
+
+}  // namespace
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/name.h b/third_party/ots/src/name.h
new file mode 100644
index 0000000..a11965f
--- /dev/null
+++ b/third_party/ots/src/name.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_NAME_H_
+#define OTS_NAME_H_
+
+#include <new>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct NameRecord {
+  NameRecord() {
+  }
+
+  NameRecord(uint16_t platformID, uint16_t encodingID,
+             uint16_t languageID, uint16_t nameID)
+    : platform_id(platformID),
+      encoding_id(encodingID),
+      language_id(languageID),
+      name_id(nameID) {
+  }
+
+  uint16_t platform_id;
+  uint16_t encoding_id;
+  uint16_t language_id;
+  uint16_t name_id;
+  std::string text;
+
+  bool operator<(const NameRecord& rhs) const {
+    if (platform_id < rhs.platform_id) return true;
+    if (platform_id > rhs.platform_id) return false;
+    if (encoding_id < rhs.encoding_id) return true;
+    if (encoding_id > rhs.encoding_id) return false;
+    if (language_id < rhs.language_id) return true;
+    if (language_id > rhs.language_id) return false;
+    return name_id < rhs.name_id;
+  }
+};
+
+struct OpenTypeNAME {
+  std::vector<NameRecord> names;
+  std::vector<std::string> lang_tags;
+};
+
+}  // namespace ots
+
+#endif  // OTS_NAME_H_
diff --git a/third_party/ots/src/os2.cc b/third_party/ots/src/os2.cc
new file mode 100644
index 0000000..915877e
--- /dev/null
+++ b/third_party/ots/src/os2.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "os2.h"
+
+#include "head.h"
+
+// OS/2 - OS/2 and Windows Metrics
+// http://www.microsoft.com/typography/otspec/os2.htm
+
+#define TABLE_NAME "OS/2"
+
+namespace ots {
+
+bool ots_os2_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeOS2 *os2 = new OpenTypeOS2;
+  file->os2 = os2;
+
+  if (!table.ReadU16(&os2->version) ||
+      !table.ReadS16(&os2->avg_char_width) ||
+      !table.ReadU16(&os2->weight_class) ||
+      !table.ReadU16(&os2->width_class) ||
+      !table.ReadU16(&os2->type) ||
+      !table.ReadS16(&os2->subscript_x_size) ||
+      !table.ReadS16(&os2->subscript_y_size) ||
+      !table.ReadS16(&os2->subscript_x_offset) ||
+      !table.ReadS16(&os2->subscript_y_offset) ||
+      !table.ReadS16(&os2->superscript_x_size) ||
+      !table.ReadS16(&os2->superscript_y_size) ||
+      !table.ReadS16(&os2->superscript_x_offset) ||
+      !table.ReadS16(&os2->superscript_y_offset) ||
+      !table.ReadS16(&os2->strikeout_size) ||
+      !table.ReadS16(&os2->strikeout_position) ||
+      !table.ReadS16(&os2->family_class)) {
+    return OTS_FAILURE_MSG("Failed toi read basic os2 elements");
+  }
+
+  if (os2->version > 4) {
+    return OTS_FAILURE_MSG("os2 version too high %d", os2->version);
+  }
+
+  // Some linux fonts (e.g., Kedage-t.ttf and LucidaSansDemiOblique.ttf) have
+  // weird weight/width classes. Overwrite them with FW_NORMAL/1/9.
+  if (os2->weight_class < 100 ||
+      os2->weight_class > 900 ||
+      os2->weight_class % 100) {
+    OTS_WARNING("bad weight: %u", os2->weight_class);
+    os2->weight_class = 400;  // FW_NORMAL
+  }
+  if (os2->width_class < 1) {
+    OTS_WARNING("bad width: %u", os2->width_class);
+    os2->width_class = 1;
+  } else if (os2->width_class > 9) {
+    OTS_WARNING("bad width: %u", os2->width_class);
+    os2->width_class = 9;
+  }
+
+  // lowest 3 bits of fsType are exclusive.
+  if (os2->type & 0x2) {
+    // mask bits 2 & 3.
+    os2->type &= 0xfff3u;
+  } else if (os2->type & 0x4) {
+    // mask bits 1 & 3.
+    os2->type &= 0xfff4u;
+  } else if (os2->type & 0x8) {
+    // mask bits 1 & 2.
+    os2->type &= 0xfff9u;
+  }
+
+  // mask reserved bits. use only 0..3, 8, 9 bits.
+  os2->type &= 0x30f;
+
+  if (os2->subscript_x_size < 0) {
+    OTS_WARNING("bad subscript_x_size: %d", os2->subscript_x_size);
+    os2->subscript_x_size = 0;
+  }
+  if (os2->subscript_y_size < 0) {
+    OTS_WARNING("bad subscript_y_size: %d", os2->subscript_y_size);
+    os2->subscript_y_size = 0;
+  }
+  if (os2->superscript_x_size < 0) {
+    OTS_WARNING("bad superscript_x_size: %d", os2->superscript_x_size);
+    os2->superscript_x_size = 0;
+  }
+  if (os2->superscript_y_size < 0) {
+    OTS_WARNING("bad superscript_y_size: %d", os2->superscript_y_size);
+    os2->superscript_y_size = 0;
+  }
+  if (os2->strikeout_size < 0) {
+    OTS_WARNING("bad strikeout_size: %d", os2->strikeout_size);
+    os2->strikeout_size = 0;
+  }
+
+  for (unsigned i = 0; i < 10; ++i) {
+    if (!table.ReadU8(&os2->panose[i])) {
+      return OTS_FAILURE_MSG("Failed to read panose in os2 table");
+    }
+  }
+
+  if (!table.ReadU32(&os2->unicode_range_1) ||
+      !table.ReadU32(&os2->unicode_range_2) ||
+      !table.ReadU32(&os2->unicode_range_3) ||
+      !table.ReadU32(&os2->unicode_range_4) ||
+      !table.ReadU32(&os2->vendor_id) ||
+      !table.ReadU16(&os2->selection) ||
+      !table.ReadU16(&os2->first_char_index) ||
+      !table.ReadU16(&os2->last_char_index) ||
+      !table.ReadS16(&os2->typo_ascender) ||
+      !table.ReadS16(&os2->typo_descender) ||
+      !table.ReadS16(&os2->typo_linegap) ||
+      !table.ReadU16(&os2->win_ascent) ||
+      !table.ReadU16(&os2->win_descent)) {
+    return OTS_FAILURE_MSG("Failed to read more basic os2 fields");
+  }
+
+  // If bit 6 is set, then bits 0 and 5 must be clear.
+  if (os2->selection & 0x40) {
+    os2->selection &= 0xffdeu;
+  }
+
+  // the settings of bits 0 and 1 must be reflected in the macStyle bits
+  // in the 'head' table.
+  if (!file->head) {
+    return OTS_FAILURE_MSG("Head table missing from font as needed by os2 table");
+  }
+  if ((os2->selection & 0x1) &&
+      !(file->head->mac_style & 0x2)) {
+    OTS_WARNING("adjusting Mac style (italic)");
+    file->head->mac_style |= 0x2;
+  }
+  if ((os2->selection & 0x2) &&
+      !(file->head->mac_style & 0x4)) {
+    OTS_WARNING("adjusting Mac style (underscore)");
+    file->head->mac_style |= 0x4;
+  }
+
+  // While bit 6 on implies that bits 0 and 1 of macStyle are clear,
+  // the reverse is not true.
+  if ((os2->selection & 0x40) &&
+      (file->head->mac_style & 0x3)) {
+    OTS_WARNING("adjusting Mac style (regular)");
+    file->head->mac_style &= 0xfffcu;
+  }
+
+  if ((os2->version < 4) &&
+      (os2->selection & 0x300)) {
+    // bit 8 and 9 must be unset in OS/2 table versions less than 4.
+    return OTS_FAILURE_MSG("OS2 version %d incompatible with selection %d", os2->version, os2->selection);
+  }
+
+  // mask reserved bits. use only 0..9 bits.
+  os2->selection &= 0x3ff;
+
+  if (os2->first_char_index > os2->last_char_index) {
+    return OTS_FAILURE_MSG("first char index %d > last char index %d in os2", os2->first_char_index, os2->last_char_index);
+  }
+  if (os2->typo_linegap < 0) {
+    OTS_WARNING("bad linegap: %d", os2->typo_linegap);
+    os2->typo_linegap = 0;
+  }
+
+  if (os2->version < 1) {
+    // http://www.microsoft.com/typography/otspec/os2ver0.htm
+    return true;
+  }
+
+  if (length < offsetof(OpenTypeOS2, code_page_range_2)) {
+    OTS_WARNING("bad version number: %u", os2->version);
+    // Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version
+    // numbers. Fix them.
+    os2->version = 0;
+    return true;
+  }
+
+  if (!table.ReadU32(&os2->code_page_range_1) ||
+      !table.ReadU32(&os2->code_page_range_2)) {
+    return OTS_FAILURE_MSG("Failed to read codepage ranges");
+  }
+
+  if (os2->version < 2) {
+    // http://www.microsoft.com/typography/otspec/os2ver1.htm
+    return true;
+  }
+
+  if (length < offsetof(OpenTypeOS2, max_context)) {
+    OTS_WARNING("bad version number: %u", os2->version);
+    // some Japanese fonts (e.g., mona.ttf) have weird version number.
+    // fix them.
+    os2->version = 1;
+    return true;
+  }
+
+  if (!table.ReadS16(&os2->x_height) ||
+      !table.ReadS16(&os2->cap_height) ||
+      !table.ReadU16(&os2->default_char) ||
+      !table.ReadU16(&os2->break_char) ||
+      !table.ReadU16(&os2->max_context)) {
+    return OTS_FAILURE_MSG("Failed to read os2 version 2 information");
+  }
+
+  if (os2->x_height < 0) {
+    OTS_WARNING("bad x_height: %d", os2->x_height);
+    os2->x_height = 0;
+  }
+  if (os2->cap_height < 0) {
+    OTS_WARNING("bad cap_height: %d", os2->cap_height);
+    os2->cap_height = 0;
+  }
+
+  return true;
+}
+
+bool ots_os2_should_serialise(OpenTypeFile *file) {
+  return file->os2 != NULL;
+}
+
+bool ots_os2_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeOS2 *os2 = file->os2;
+
+  if (!out->WriteU16(os2->version) ||
+      !out->WriteS16(os2->avg_char_width) ||
+      !out->WriteU16(os2->weight_class) ||
+      !out->WriteU16(os2->width_class) ||
+      !out->WriteU16(os2->type) ||
+      !out->WriteS16(os2->subscript_x_size) ||
+      !out->WriteS16(os2->subscript_y_size) ||
+      !out->WriteS16(os2->subscript_x_offset) ||
+      !out->WriteS16(os2->subscript_y_offset) ||
+      !out->WriteS16(os2->superscript_x_size) ||
+      !out->WriteS16(os2->superscript_y_size) ||
+      !out->WriteS16(os2->superscript_x_offset) ||
+      !out->WriteS16(os2->superscript_y_offset) ||
+      !out->WriteS16(os2->strikeout_size) ||
+      !out->WriteS16(os2->strikeout_position) ||
+      !out->WriteS16(os2->family_class)) {
+    return OTS_FAILURE_MSG("Failed to write basic OS2 information");
+  }
+
+  for (unsigned i = 0; i < 10; ++i) {
+    if (!out->Write(&os2->panose[i], 1)) {
+      return OTS_FAILURE_MSG("Failed to write os2 panose information");
+    }
+  }
+
+  if (!out->WriteU32(os2->unicode_range_1) ||
+      !out->WriteU32(os2->unicode_range_2) ||
+      !out->WriteU32(os2->unicode_range_3) ||
+      !out->WriteU32(os2->unicode_range_4) ||
+      !out->WriteU32(os2->vendor_id) ||
+      !out->WriteU16(os2->selection) ||
+      !out->WriteU16(os2->first_char_index) ||
+      !out->WriteU16(os2->last_char_index) ||
+      !out->WriteS16(os2->typo_ascender) ||
+      !out->WriteS16(os2->typo_descender) ||
+      !out->WriteS16(os2->typo_linegap) ||
+      !out->WriteU16(os2->win_ascent) ||
+      !out->WriteU16(os2->win_descent)) {
+    return OTS_FAILURE_MSG("Failed to write os2 version 1 information");
+  }
+
+  if (os2->version < 1) {
+    return true;
+  }
+
+  if (!out->WriteU32(os2->code_page_range_1) ||
+      !out->WriteU32(os2->code_page_range_2)) {
+    return OTS_FAILURE_MSG("Failed to write codepage ranges");
+  }
+
+  if (os2->version < 2) {
+    return true;
+  }
+
+  if (!out->WriteS16(os2->x_height) ||
+      !out->WriteS16(os2->cap_height) ||
+      !out->WriteU16(os2->default_char) ||
+      !out->WriteU16(os2->break_char) ||
+      !out->WriteU16(os2->max_context)) {
+    return OTS_FAILURE_MSG("Failed to write os2 version 2 information");
+  }
+
+  return true;
+}
+
+void ots_os2_free(OpenTypeFile *file) {
+  delete file->os2;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/os2.h b/third_party/ots/src/os2.h
new file mode 100644
index 0000000..9e0fc34
--- /dev/null
+++ b/third_party/ots/src/os2.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_OS2_H_
+#define OTS_OS2_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeOS2 {
+  uint16_t version;
+  int16_t avg_char_width;
+  uint16_t weight_class;
+  uint16_t width_class;
+  uint16_t type;
+  int16_t subscript_x_size;
+  int16_t subscript_y_size;
+  int16_t subscript_x_offset;
+  int16_t subscript_y_offset;
+  int16_t superscript_x_size;
+  int16_t superscript_y_size;
+  int16_t superscript_x_offset;
+  int16_t superscript_y_offset;
+  int16_t strikeout_size;
+  int16_t strikeout_position;
+  int16_t family_class;
+  uint8_t panose[10];
+  uint32_t unicode_range_1;
+  uint32_t unicode_range_2;
+  uint32_t unicode_range_3;
+  uint32_t unicode_range_4;
+  uint32_t vendor_id;
+  uint16_t selection;
+  uint16_t first_char_index;
+  uint16_t last_char_index;
+  int16_t typo_ascender;
+  int16_t typo_descender;
+  int16_t typo_linegap;
+  uint16_t win_ascent;
+  uint16_t win_descent;
+  uint32_t code_page_range_1;
+  uint32_t code_page_range_2;
+  int16_t x_height;
+  int16_t cap_height;
+  uint16_t default_char;
+  uint16_t break_char;
+  uint16_t max_context;
+};
+
+}  // namespace ots
+
+#endif  // OTS_OS2_H_
diff --git a/third_party/ots/src/ots.cc b/third_party/ots/src/ots.cc
new file mode 100644
index 0000000..197c649
--- /dev/null
+++ b/third_party/ots/src/ots.cc
@@ -0,0 +1,824 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ots.h"
+
+#include <sys/types.h>
+#include <zlib.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <map>
+#include <vector>
+
+#include "woff2.h"
+
+// The OpenType Font File
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+namespace {
+
+// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
+#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
+#define OTS_FAILURE_MSG_HDR(msg_)      OTS_FAILURE_MSG_(header, msg_)
+
+
+struct OpenTypeTable {
+  uint32_t tag;
+  uint32_t chksum;
+  uint32_t offset;
+  uint32_t length;
+  uint32_t uncompressed_length;
+};
+
+bool CheckTag(uint32_t tag_value) {
+  for (unsigned i = 0; i < 4; ++i) {
+    const uint32_t check = tag_value & 0xff;
+    if (check < 32 || check > 126) {
+      return false;  // non-ASCII character found.
+    }
+    tag_value >>= 8;
+  }
+  return true;
+}
+
+uint32_t Tag(const char *tag_str) {	
+  uint32_t ret;	
+  std::memcpy(&ret, tag_str, 4);	
+  return ret;	
+}
+
+struct OutputTable {
+  uint32_t tag;
+  size_t offset;
+  size_t length;
+  uint32_t chksum;
+
+  static bool SortByTag(const OutputTable& a, const OutputTable& b) {
+    const uint32_t atag = ntohl(a.tag);
+    const uint32_t btag = ntohl(b.tag);
+    return atag < btag;
+  }
+};
+
+struct Arena {
+ public:
+  ~Arena() {
+    for (std::vector<uint8_t*>::iterator
+         i = hunks_.begin(); i != hunks_.end(); ++i) {
+      delete[] *i;
+    }
+  }
+
+  uint8_t* Allocate(size_t length) {
+    uint8_t* p = new uint8_t[length];
+    hunks_.push_back(p);
+    return p;
+  }
+
+ private:
+  std::vector<uint8_t*> hunks_;
+};
+
+const struct {
+  const char* tag;
+  bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
+  bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
+  bool (*should_serialise)(ots::OpenTypeFile *file);
+  void (*free)(ots::OpenTypeFile *file);
+  bool required;
+} table_parsers[] = {
+  { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise,
+    ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
+  { "head", ots::ots_head_parse, ots::ots_head_serialise,
+    ots::ots_head_should_serialise, ots::ots_head_free, true },
+  { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise,
+    ots::ots_os2_should_serialise, ots::ots_os2_free, true },
+  { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise,
+    ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
+  { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise,
+    ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
+  { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
+    ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
+  { "name", ots::ots_name_parse, ots::ots_name_serialise,
+    ots::ots_name_should_serialise, ots::ots_name_free, true },
+  { "post", ots::ots_post_parse, ots::ots_post_serialise,
+    ots::ots_post_should_serialise, ots::ots_post_free, true },
+  { "loca", ots::ots_loca_parse, ots::ots_loca_serialise,
+    ots::ots_loca_should_serialise, ots::ots_loca_free, false },
+  { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise,
+    ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
+  { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise,
+    ots::ots_cff_should_serialise, ots::ots_cff_free, false },
+  { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
+    ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
+  { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
+    ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
+  { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise,
+    ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
+  { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise,
+    ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
+  { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
+    ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
+  { "prep", ots::ots_prep_parse, ots::ots_prep_serialise,
+    ots::ots_prep_should_serialise, ots::ots_prep_free, false },
+  { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
+    ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
+  { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise,
+    ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
+  { "kern", ots::ots_kern_parse, ots::ots_kern_serialise,
+    ots::ots_kern_should_serialise, ots::ots_kern_free, false },
+  // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
+  // because they could refer GDEF table.
+  { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise,
+    ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
+  { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
+    ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
+  { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
+    ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
+  { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
+    ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
+  { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+    ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
+  { "MATH", ots::ots_math_parse, ots::ots_math_serialise,
+    ots::ots_math_should_serialise, ots::ots_math_free, false },
+  // TODO(bashi): Support mort, base, and jstf tables.
+  { 0, NULL, NULL, NULL, NULL, false },
+};
+
+bool ProcessGeneric(ots::OpenTypeFile *header,
+                    uint32_t signature,
+                    ots::OTSStream *output,
+                    const uint8_t *data, size_t length,
+                    const std::vector<OpenTypeTable>& tables,
+                    ots::Buffer& file);
+
+bool ProcessTTF(ots::OpenTypeFile *header,
+                ots::OTSStream *output, const uint8_t *data, size_t length) {
+  ots::Buffer file(data, length);
+
+  // we disallow all files > 1GB in size for sanity.
+  if (length > 1024 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+  }
+
+  if (!file.ReadTag(&header->version)) {
+    return OTS_FAILURE_MSG_HDR("error reading version tag");
+  }
+  if (!ots::IsValidVersionTag(header->version)) {
+      return OTS_FAILURE_MSG_HDR("invalid version tag");
+  }
+
+  if (!file.ReadU16(&header->num_tables) ||
+      !file.ReadU16(&header->search_range) ||
+      !file.ReadU16(&header->entry_selector) ||
+      !file.ReadU16(&header->range_shift)) {
+    return OTS_FAILURE_MSG_HDR("error reading table directory search header");
+  }
+
+  // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
+  // overflow num_tables is, at most, 2^16 / 16 = 2^12
+  if (header->num_tables >= 4096 || header->num_tables < 1) {
+    return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
+  }
+
+  unsigned max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= header->num_tables) {
+    max_pow2++;
+  }
+  const uint16_t expected_search_range = (1u << max_pow2) << 4;
+
+  // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
+  // http://www.princexml.com/fonts/ have unexpected search_range value.
+  if (header->search_range != expected_search_range) {
+    OTS_FAILURE_MSG_HDR("bad search range");
+    header->search_range = expected_search_range;  // Fix the value.
+  }
+
+  // entry_selector is Log2(maximum power of 2 <= numTables)
+  if (header->entry_selector != max_pow2) {
+    return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory");
+  }
+
+  // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
+  // doesn't over flow because we range checked it above. Also, we know that
+  // it's > header->search_range by construction of search_range.
+  const uint16_t expected_range_shift =
+      16 * header->num_tables - header->search_range;
+  if (header->range_shift != expected_range_shift) {
+    OTS_FAILURE_MSG_HDR("bad range shift");
+    header->range_shift = expected_range_shift;  // the same as above.
+  }
+
+  // Next up is the list of tables.
+  std::vector<OpenTypeTable> tables;
+
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    OpenTypeTable table;
+    if (!file.ReadTag(&table.tag) ||
+        !file.ReadU32(&table.chksum) ||
+        !file.ReadU32(&table.offset) ||
+        !file.ReadU32(&table.length)) {
+      return OTS_FAILURE_MSG_HDR("error reading table directory");
+    }
+
+    table.uncompressed_length = table.length;
+    tables.push_back(table);
+  }
+
+  return ProcessGeneric(header, header->version, output, data, length,
+                        tables, file);
+}
+
+bool ProcessWOFF(ots::OpenTypeFile *header,
+                 ots::OTSStream *output, const uint8_t *data, size_t length) {
+  ots::Buffer file(data, length);
+
+  // we disallow all files > 1GB in size for sanity.
+  if (length > 1024 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+  }
+
+  uint32_t woff_tag;
+  if (!file.ReadTag(&woff_tag)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
+  }
+
+  if (woff_tag != Tag("wOFF")) {
+    return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
+  }
+
+  if (!file.ReadTag(&header->version)) {
+    return OTS_FAILURE_MSG_HDR("error reading version tag");
+  }
+  if (!ots::IsValidVersionTag(header->version)) {
+    return OTS_FAILURE_MSG_HDR("invalid version tag");
+  }
+
+  header->search_range = 0;
+  header->entry_selector = 0;
+  header->range_shift = 0;
+
+  uint32_t reported_length;
+  if (!file.ReadU32(&reported_length) || length != reported_length) {
+    return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
+  }
+
+  if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
+    return OTS_FAILURE_MSG_HDR("error reading number of tables");
+  }
+
+  uint16_t reserved_value;
+  if (!file.ReadU16(&reserved_value) || reserved_value) {
+    return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
+  }
+
+  uint32_t reported_total_sfnt_size;
+  if (!file.ReadU32(&reported_total_sfnt_size)) {
+    return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
+  }
+
+  // We don't care about these fields of the header:
+  //   uint16_t major_version, minor_version
+  if (!file.Skip(2 * 2)) {
+    return OTS_FAILURE_MSG_HDR("error skipping WOFF header fields");
+  }
+
+  // Checks metadata block size.
+  uint32_t meta_offset;
+  uint32_t meta_length;
+  uint32_t meta_length_orig;
+  if (!file.ReadU32(&meta_offset) ||
+      !file.ReadU32(&meta_length) ||
+      !file.ReadU32(&meta_length_orig)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+  }
+  if (meta_offset) {
+    if (meta_offset >= length || length - meta_offset < meta_length) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block location/size");
+    }
+  }
+
+  // Checks private data block size.
+  uint32_t priv_offset;
+  uint32_t priv_length;
+  if (!file.ReadU32(&priv_offset) ||
+      !file.ReadU32(&priv_length)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+  }
+  if (priv_offset) {
+    if (priv_offset >= length || length - priv_offset < priv_length) {
+      return OTS_FAILURE_MSG_HDR("invalid private block location/size");
+    }
+  }
+
+  // Next up is the list of tables.
+  std::vector<OpenTypeTable> tables;
+
+  uint32_t first_index = 0;
+  uint32_t last_index = 0;
+  // Size of sfnt header plus size of table records.
+  uint64_t total_sfnt_size = 12 + 16 * header->num_tables;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    OpenTypeTable table;
+    if (!file.ReadTag(&table.tag) ||
+        !file.ReadU32(&table.offset) ||
+        !file.ReadU32(&table.length) ||
+        !file.ReadU32(&table.uncompressed_length) ||
+        !file.ReadU32(&table.chksum)) {
+      return OTS_FAILURE_MSG_HDR("error reading table directory");
+    }
+
+    total_sfnt_size += ots::Round4(table.uncompressed_length);
+    if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("sfnt size overflow");
+    }
+    tables.push_back(table);
+    if (i == 0 || tables[first_index].offset > table.offset)
+      first_index = i;
+    if (i == 0 || tables[last_index].offset < table.offset)
+      last_index = i;
+  }
+
+  if (reported_total_sfnt_size != total_sfnt_size) {
+    return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
+  }
+
+  // Table data must follow immediately after the header.
+  if (tables[first_index].offset != ots::Round4(file.offset())) {
+    return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
+  }
+
+  if (tables[last_index].offset >= length ||
+      length - tables[last_index].offset < tables[last_index].length) {
+    return OTS_FAILURE_MSG_HDR("invalid table location/size");
+  }
+  // Blocks must follow immediately after the previous block.
+  // (Except for padding with a maximum of three null bytes)
+  uint64_t block_end = ots::Round4(
+      static_cast<uint64_t>(tables[last_index].offset) +
+      static_cast<uint64_t>(tables[last_index].length));
+  if (block_end > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE_MSG_HDR("invalid table location/size");
+  }
+  if (meta_offset) {
+    if (block_end != meta_offset) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block location");
+    }
+    block_end = ots::Round4(static_cast<uint64_t>(meta_offset) +
+                            static_cast<uint64_t>(meta_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block size");
+    }
+  }
+  if (priv_offset) {
+    if (block_end != priv_offset) {
+      return OTS_FAILURE_MSG_HDR("invalid private block location");
+    }
+    block_end = ots::Round4(static_cast<uint64_t>(priv_offset) +
+                            static_cast<uint64_t>(priv_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("invalid private block size");
+    }
+  }
+  if (block_end != ots::Round4(length)) {
+    return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
+  }
+
+  return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
+}
+
+bool ProcessWOFF2(ots::OpenTypeFile *header,
+                  ots::OTSStream *output, const uint8_t *data, size_t length) {
+  size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
+  if (decompressed_size == 0) {
+    return OTS_FAILURE();
+  }
+  // decompressed font must be <= 30MB
+  if (decompressed_size > 30 * 1024 * 1024) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<uint8_t> decompressed_buffer(decompressed_size);
+  if (!ots::ConvertWOFF2ToTTF(header, &decompressed_buffer[0], decompressed_size,
+                              data, length)) {
+    return OTS_FAILURE();
+  }
+  return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
+}
+
+ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
+  ots::TableAction action = ots::TABLE_ACTION_DEFAULT;
+
+  action = header->context->GetTableAction(htonl(tag));
+
+  if (action == ots::TABLE_ACTION_DEFAULT) {
+    action = ots::TABLE_ACTION_DROP;
+
+    for (unsigned i = 0; ; ++i) {
+      if (table_parsers[i].parse == NULL) break;
+
+      if (Tag(table_parsers[i].tag) == tag) {
+        action = ots::TABLE_ACTION_SANITIZE;
+        break;
+      }
+    }
+  }
+
+  assert(action != ots::TABLE_ACTION_DEFAULT); // Should never return this.
+  return action;
+}
+
+bool GetTableData(const uint8_t *data,
+                  const OpenTypeTable table,
+                  Arena *arena,
+                  size_t *table_length,
+                  const uint8_t **table_data) {
+  if (table.uncompressed_length != table.length) {
+    // Compressed table. Need to uncompress into memory first.
+    *table_length = table.uncompressed_length;
+    *table_data = (*arena).Allocate(*table_length);
+    uLongf dest_len = *table_length;
+    int r = uncompress((Bytef*) *table_data, &dest_len,
+                       data + table.offset, table.length);
+    if (r != Z_OK || dest_len != *table_length) {
+      return false;
+    }
+  } else {
+    // Uncompressed table. We can process directly from memory.
+    *table_data = data + table.offset;
+    *table_length = table.length;
+  }
+
+  return true;
+}
+
+bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature,
+                    ots::OTSStream *output,
+                    const uint8_t *data, size_t length,
+                    const std::vector<OpenTypeTable>& tables,
+                    ots::Buffer& file) {
+  const size_t data_offset = file.offset();
+
+  uint32_t uncompressed_sum = 0;
+
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    // the tables must be sorted by tag (when taken as big-endian numbers).
+    // This also remove the possibility of duplicate tables.
+    if (i) {
+      const uint32_t this_tag = ntohl(tables[i].tag);
+      const uint32_t prev_tag = ntohl(tables[i - 1].tag);
+      if (this_tag <= prev_tag) {
+        return OTS_FAILURE_MSG_HDR("table directory not correctly ordered");
+      }
+    }
+
+    // all tag names must be built from printable ASCII characters
+    if (!CheckTag(tables[i].tag)) {
+      return OTS_FAILURE_MSG_TAG("invalid table tag", &tables[i].tag);
+    }
+
+    // tables must be 4-byte aligned
+    if (tables[i].offset & 3) {
+      return OTS_FAILURE_MSG_TAG("misaligned table", &tables[i].tag);
+    }
+
+    // and must be within the file
+    if (tables[i].offset < data_offset || tables[i].offset >= length) {
+      return OTS_FAILURE_MSG_TAG("invalid table offset", &tables[i].tag);
+    }
+    // disallow all tables with a zero length
+    if (tables[i].length < 1) {
+      // Note: malayalam.ttf has zero length CVT table...
+      return OTS_FAILURE_MSG_TAG("zero-length table", &tables[i].tag);
+    }
+    // disallow all tables with a length > 1GB
+    if (tables[i].length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", &tables[i].tag);
+    }
+    // disallow tables where the uncompressed size is < the compressed size.
+    if (tables[i].uncompressed_length < tables[i].length) {
+      return OTS_FAILURE_MSG_TAG("invalid compressed table", &tables[i].tag);
+    }
+    if (tables[i].uncompressed_length > tables[i].length) {
+      // We'll probably be decompressing this table.
+
+      // disallow all tables which uncompress to > 30 MB
+      if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
+        return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", &tables[i].tag);
+      }
+      if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
+        return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", &tables[i].tag);
+      }
+
+      uncompressed_sum += tables[i].uncompressed_length;
+    }
+    // since we required that the file be < 1GB in length, and that the table
+    // length is < 1GB, the following addtion doesn't overflow
+    uint32_t end_byte = tables[i].offset + tables[i].length;
+    // Tables in the WOFF file must be aligned 4-byte boundary.
+    if (signature == Tag("wOFF")) {
+        end_byte = ots::Round4(end_byte);
+    }
+    if (!end_byte || end_byte > length) {
+      return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables[i].tag);
+    }
+  }
+
+  // All decompressed tables uncompressed must be <= 30MB.
+  if (uncompressed_sum > 30 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
+  }
+
+  std::map<uint32_t, OpenTypeTable> table_map;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    table_map[tables[i].tag] = tables[i];
+  }
+
+  // check that the tables are not overlapping.
+  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    overlap_checker.push_back(
+        std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */));
+    overlap_checker.push_back(
+        std::make_pair(tables[i].offset + tables[i].length,
+                       static_cast<uint8_t>(0) /* end */));
+  }
+  std::sort(overlap_checker.begin(), overlap_checker.end());
+  int overlap_count = 0;
+  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+    overlap_count += (overlap_checker[i].second ? 1 : -1);
+    if (overlap_count > 1) {
+      return OTS_FAILURE_MSG_HDR("overlapping tables");
+    }
+  }
+
+  Arena arena;
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+
+    uint32_t tag = Tag(table_parsers[i].tag);
+    const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(tag);
+
+    ots::TableAction action = GetTableAction(header, tag);
+    if (it == table_map.end()) {
+      if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
+        return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
+      }
+      continue;
+    }
+
+    const uint8_t* table_data;
+    size_t table_length;
+
+    if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+      return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
+    }
+
+    if (action == ots::TABLE_ACTION_SANITIZE &&
+        !table_parsers[i].parse(header, table_data, table_length)) {
+      // TODO: parsers should generate specific messages detailing the failure;
+      // once those are all added, we won't need a generic failure message here
+      return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers[i].tag);
+    }
+  }
+
+  if (header->cff) {
+    // font with PostScript glyph
+    if (header->version != Tag("OTTO")) {
+      return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
+    }
+    if (header->glyf || header->loca) {
+      // mixing outline formats is not recommended
+      return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
+    }
+  } else {
+    if (!header->glyf || !header->loca) {
+      // No TrueType glyph found.
+      // Note: bitmap-only fonts are not supported.
+      return OTS_FAILURE_MSG_HDR("neither PS nor TT glyphs present");
+    }
+  }
+
+  uint16_t num_output_tables = 0;
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (table_parsers[i].should_serialise(header)) {
+      num_output_tables++;
+    }
+  }
+
+  for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+       it != table_map.end(); ++it) {
+    ots::TableAction action = GetTableAction(header, it->first);
+    if (action == ots::TABLE_ACTION_PASSTHRU) {
+      num_output_tables++;
+    }
+  }
+
+  uint16_t max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_output_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+
+  // most of the errors here are highly unlikely - they'd only occur if the
+  // output stream returns a failure, e.g. lack of space to write
+  output->ResetChecksum();
+  if (!output->WriteTag(header->version) ||
+      !output->WriteU16(num_output_tables) ||
+      !output->WriteU16(output_search_range) ||
+      !output->WriteU16(max_pow2) ||
+      !output->WriteU16((num_output_tables << 4) - output_search_range)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+  const uint32_t offset_table_chksum = output->chksum();
+
+  const size_t table_record_offset = output->Tell();
+  if (!output->Pad(16 * num_output_tables)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  std::vector<OutputTable> out_tables;
+
+  size_t head_table_offset = 0;
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (!table_parsers[i].should_serialise(header)) {
+      continue;
+    }
+
+    OutputTable out;
+    uint32_t tag = Tag(table_parsers[i].tag);
+    out.tag = tag;
+    out.offset = output->Tell();
+
+    output->ResetChecksum();
+    if (tag == Tag("head")) {
+      head_table_offset = out.offset;
+    }
+    if (!table_parsers[i].serialise(output, header)) {
+      return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
+    }
+
+    const size_t end_offset = output->Tell();
+    if (end_offset <= out.offset) {
+      // paranoid check. |end_offset| is supposed to be greater than the offset,
+      // as long as the Tell() interface is implemented correctly.
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    out.length = end_offset - out.offset;
+
+    // align tables to four bytes
+    if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    out.chksum = output->chksum();
+    out_tables.push_back(out);
+  }
+
+  for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+       it != table_map.end(); ++it) {
+    ots::TableAction action = GetTableAction(header, it->first);
+    if (action == ots::TABLE_ACTION_PASSTHRU) {
+      OutputTable out;
+      out.tag = it->second.tag;
+      out.offset = output->Tell();
+
+      output->ResetChecksum();
+      if (it->second.tag == Tag("head")) {
+        head_table_offset = out.offset;
+      }
+
+      const uint8_t* table_data;
+      size_t table_length;
+
+      if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+        return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
+      }
+
+      if (!output->Write(table_data, table_length)) {
+        return OTS_FAILURE_MSG_HDR("Failed to serialize table");
+      }
+
+      const size_t end_offset = output->Tell();
+      if (end_offset <= out.offset) {
+        // paranoid check. |end_offset| is supposed to be greater than the offset,
+        // as long as the Tell() interface is implemented correctly.
+        return OTS_FAILURE_MSG_HDR("error writing output");
+      }
+      out.length = end_offset - out.offset;
+
+      // align tables to four bytes
+      if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+        return OTS_FAILURE_MSG_HDR("error writing output");
+      }
+      out.chksum = output->chksum();
+      out_tables.push_back(out);
+    }
+  }
+
+  const size_t end_of_file = output->Tell();
+
+  // Need to sort the output tables for inclusion in the file
+  std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
+  if (!output->Seek(table_record_offset)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  output->ResetChecksum();
+  uint32_t tables_chksum = 0;
+  for (unsigned i = 0; i < out_tables.size(); ++i) {
+    if (!output->WriteTag(out_tables[i].tag) ||
+        !output->WriteU32(out_tables[i].chksum) ||
+        !output->WriteU32(out_tables[i].offset) ||
+        !output->WriteU32(out_tables[i].length)) {
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    tables_chksum += out_tables[i].chksum;
+  }
+  const uint32_t table_record_chksum = output->chksum();
+
+  // http://www.microsoft.com/typography/otspec/otff.htm
+  const uint32_t file_chksum
+      = offset_table_chksum + tables_chksum + table_record_chksum;
+  const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum;
+
+  // seek into the 'head' table and write in the checksum magic value
+  if (!head_table_offset) {
+    return OTS_FAILURE_MSG_HDR("internal error!");
+  }
+  if (!output->Seek(head_table_offset + 8)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+  if (!output->WriteU32(chksum_magic)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  if (!output->Seek(end_of_file)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+void EnableWOFF2() {
+}
+
+bool IsValidVersionTag(uint32_t tag) {
+  return tag == Tag("\x00\x01\x00\x00") ||
+         // OpenType fonts with CFF data have 'OTTO' tag.
+         tag == Tag("OTTO") ||
+         // Older Mac fonts might have 'true' or 'typ1' tag.
+         tag == Tag("true") ||
+         tag == Tag("typ1");
+}
+
+bool OTSContext::Process(OTSStream *output,
+                         const uint8_t *data,
+                         size_t length) {
+  OpenTypeFile header;
+
+  header.context = this;
+
+  if (length < 4) {
+    return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
+  }
+
+  bool result;
+  if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
+    result = ProcessWOFF(&header, output, data, length);
+  } else if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') {
+    result = ProcessWOFF2(&header, output, data, length);
+  } else {
+    result = ProcessTTF(&header, output, data, length);
+  }
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+    table_parsers[i].free(&header);
+  }
+  return result;
+}
+
+// For backward compatibility
+bool Process(OTSStream *output, const uint8_t *data, size_t length) {
+  static OTSContext context;
+  return context.Process(output, data, length);
+}
+
+}  // namespace ots
diff --git a/third_party/ots/src/ots.h b/third_party/ots/src/ots.h
new file mode 100644
index 0000000..ba3ba77
--- /dev/null
+++ b/third_party/ots/src/ots.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_H_
+#define OTS_H_
+
+#include <stddef.h>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+
+#include "opentype-sanitiser.h"
+
+// arraysize borrowed from base/basictypes.h
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+namespace ots {
+
+#if !defined(OTS_DEBUG)
+#define OTS_FAILURE() false
+#else
+#define OTS_FAILURE() \
+  (\
+    std::fprintf(stderr, "ERROR at %s:%d (%s)\n", \
+                 __FILE__, __LINE__, __FUNCTION__) \
+    && false\
+  )
+#endif
+
+// All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original
+// message-less OTS_FAILURE(), so that the current parser will return 'false' as
+// its result (indicating a failure).
+
+#if !defined(OTS_DEBUG)
+#define OTS_MESSAGE_(level,otf_,...) \
+  (otf_)->context->Message(level,__VA_ARGS__)
+#else
+#define OTS_MESSAGE_(level,otf_,...) \
+  OTS_FAILURE(), \
+  (otf_)->context->Message(level,__VA_ARGS__)
+#endif
+
+// Generate a simple message
+#define OTS_FAILURE_MSG_(otf_,...) \
+  (OTS_MESSAGE_(0,otf_,__VA_ARGS__), false)
+
+#define OTS_WARNING_MSG_(otf_,...) \
+  OTS_MESSAGE_(1,otf_,__VA_ARGS__)
+
+// Generate a message with an associated table tag
+#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
+  (OTS_MESSAGE_(0,otf_,"%4.4s: %s", tag_, msg_), false)
+
+// Convenience macros for use in files that only handle a single table tag,
+// defined as TABLE_NAME at the top of the file; the 'file' variable is
+// expected to be the current OpenTypeFile pointer.
+#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__)
+
+#define OTS_WARNING(...) OTS_WARNING_MSG_(file, TABLE_NAME ": " __VA_ARGS__)
+
+// -----------------------------------------------------------------------------
+// Buffer helper class
+//
+// This class perform some trival buffer operations while checking for
+// out-of-bounds errors. As a family they return false if anything is amiss,
+// updating the current offset otherwise.
+// -----------------------------------------------------------------------------
+class Buffer {
+ public:
+  Buffer(const uint8_t *buf, size_t len)
+      : buffer_(buf),
+        length_(len),
+        offset_(0) { }
+
+  bool Skip(size_t n_bytes) {
+    return Read(NULL, n_bytes);
+  }
+
+  bool Read(uint8_t *buf, size_t n_bytes) {
+    if (n_bytes > 1024 * 1024 * 1024) {
+      return OTS_FAILURE();
+    }
+    if ((offset_ + n_bytes > length_) ||
+        (offset_ > length_ - n_bytes)) {
+      return OTS_FAILURE();
+    }
+    if (buf) {
+      std::memcpy(buf, buffer_ + offset_, n_bytes);
+    }
+    offset_ += n_bytes;
+    return true;
+  }
+
+  inline bool ReadU8(uint8_t *value) {
+    if (offset_ + 1 > length_) {
+      return OTS_FAILURE();
+    }
+    *value = buffer_[offset_];
+    ++offset_;
+    return true;
+  }
+
+  bool ReadU16(uint16_t *value) {
+    if (offset_ + 2 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
+    *value = ntohs(*value);
+    offset_ += 2;
+    return true;
+  }
+
+  bool ReadS16(int16_t *value) {
+    return ReadU16(reinterpret_cast<uint16_t*>(value));
+  }
+
+  bool ReadU24(uint32_t *value) {
+    if (offset_ + 3 > length_) {
+      return OTS_FAILURE();
+    }
+    *value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
+        static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
+        static_cast<uint32_t>(buffer_[offset_ + 2]);
+    offset_ += 3;
+    return true;
+  }
+
+  bool ReadU32(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    *value = ntohl(*value);
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadS32(int32_t *value) {
+    return ReadU32(reinterpret_cast<uint32_t*>(value));
+  }
+
+  bool ReadTag(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadR64(uint64_t *value) {
+    if (offset_ + 8 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
+    offset_ += 8;
+    return true;
+  }
+
+  const uint8_t *buffer() const { return buffer_; }
+  size_t offset() const { return offset_; }
+  size_t length() const { return length_; }
+
+  void set_offset(size_t newoffset) { offset_ = newoffset; }
+
+ private:
+  const uint8_t * const buffer_;
+  const size_t length_;
+  size_t offset_;
+};
+
+// Round a value up to the nearest multiple of 4. Don't round the value in the
+// case that rounding up overflows.
+template<typename T> T Round4(T value) {
+  if (std::numeric_limits<T>::max() - value < 3) {
+    return value;
+  }
+  return (value + 3) & ~3;
+}
+
+template<typename T> T Round2(T value) {
+  if (value == std::numeric_limits<T>::max()) {
+    return value;
+  }
+  return (value + 1) & ~1;
+}
+
+bool IsValidVersionTag(uint32_t tag);
+
+#define FOR_EACH_TABLE_TYPE \
+  F(cff, CFF) \
+  F(cmap, CMAP) \
+  F(cvt, CVT) \
+  F(fpgm, FPGM) \
+  F(gasp, GASP) \
+  F(gdef, GDEF) \
+  F(glyf, GLYF) \
+  F(gpos, GPOS) \
+  F(gsub, GSUB) \
+  F(hdmx, HDMX) \
+  F(head, HEAD) \
+  F(hhea, HHEA) \
+  F(hmtx, HMTX) \
+  F(kern, KERN) \
+  F(loca, LOCA) \
+  F(ltsh, LTSH) \
+  F(math, MATH) \
+  F(maxp, MAXP) \
+  F(name, NAME) \
+  F(os2, OS2) \
+  F(post, POST) \
+  F(prep, PREP) \
+  F(vdmx, VDMX) \
+  F(vorg, VORG) \
+  F(vhea, VHEA) \
+  F(vmtx, VMTX)
+
+#define F(name, capname) struct OpenType##capname;
+FOR_EACH_TABLE_TYPE
+#undef F
+
+struct OpenTypeFile {
+  OpenTypeFile() {
+#define F(name, capname) name = NULL;
+    FOR_EACH_TABLE_TYPE
+#undef F
+  }
+
+  uint32_t version;
+  uint16_t num_tables;
+  uint16_t search_range;
+  uint16_t entry_selector;
+  uint16_t range_shift;
+
+  OTSContext *context;
+
+#define F(name, capname) OpenType##capname *name;
+FOR_EACH_TABLE_TYPE
+#undef F
+};
+
+#define F(name, capname) \
+bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
+bool ots_##name##_should_serialise(OpenTypeFile *f); \
+bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
+void ots_##name##_free(OpenTypeFile *f);
+// TODO(yusukes): change these function names to follow Chromium coding rule.
+FOR_EACH_TABLE_TYPE
+#undef F
+
+}  // namespace ots
+
+#endif  // OTS_H_
diff --git a/third_party/ots/src/post.cc b/third_party/ots/src/post.cc
new file mode 100644
index 0000000..338d869
--- /dev/null
+++ b/third_party/ots/src/post.cc
@@ -0,0 +1,188 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "post.h"
+
+#include "maxp.h"
+
+// post - PostScript
+// http://www.microsoft.com/typography/otspec/post.htm
+
+#define TABLE_NAME "post"
+
+namespace ots {
+
+bool ots_post_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypePOST *post = new OpenTypePOST;
+  file->post = post;
+
+  if (!table.ReadU32(&post->version) ||
+      !table.ReadU32(&post->italic_angle) ||
+      !table.ReadS16(&post->underline) ||
+      !table.ReadS16(&post->underline_thickness) ||
+      !table.ReadU32(&post->is_fixed_pitch)) {
+    return OTS_FAILURE_MSG("Failed to read post header");
+  }
+
+  if (post->underline_thickness < 0) {
+    post->underline_thickness = 1;
+  }
+
+  if (post->version == 0x00010000) {
+    return true;
+  } else if (post->version == 0x00030000) {
+    return true;
+  } else if (post->version != 0x00020000) {
+    // 0x00025000 is deprecated. We don't accept it.
+    return OTS_FAILURE_MSG("Bad post version %x", post->version);
+  }
+
+  // We have a version 2 table with a list of Pascal strings at the end
+
+  // We don't care about the memory usage fields. We'll set all these to zero
+  // when serialising
+  if (!table.Skip(16)) {
+    return OTS_FAILURE_MSG("Failed to skip memory usage in post table");
+  }
+
+  uint16_t num_glyphs = 0;
+  if (!table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read number of glyphs");
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table required by post table");
+  }
+
+  if (num_glyphs == 0) {
+    if (file->maxp->num_glyphs > 258) {
+      return OTS_FAILURE_MSG("Can't have no glyphs in the post table if there are more than 256 glyphs in the font");
+    }
+    OTS_WARNING("table version is 1, but no glyf names are found");
+    // workaround for fonts in http://www.fontsquirrel.com/fontface
+    // (e.g., yataghan.ttf).
+    post->version = 0x00010000;
+    return true;
+  }
+
+  if (num_glyphs != file->maxp->num_glyphs) {
+    // Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
+    return OTS_FAILURE_MSG("Bad number of glyphs in post table %d", num_glyphs);
+  }
+
+  post->glyph_name_index.resize(num_glyphs);
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    if (!table.ReadU16(&post->glyph_name_index[i])) {
+      return OTS_FAILURE_MSG("Failed to read post information for glyph %d", i);
+    }
+    // Note: A strict interpretation of the specification requires name indexes
+    // are less than 32768. This, however, excludes fonts like unifont.ttf
+    // which cover all of unicode.
+  }
+
+  // Now we have an array of Pascal strings. We have to check that they are all
+  // valid and read them in.
+  const size_t strings_offset = table.offset();
+  const uint8_t *strings = data + strings_offset;
+  const uint8_t *strings_end = data + length;
+
+  for (;;) {
+    if (strings == strings_end) break;
+    const unsigned string_length = *strings;
+    if (strings + 1 + string_length > strings_end) {
+      return OTS_FAILURE_MSG("Bad string length %d", string_length);
+    }
+    if (std::memchr(strings + 1, '\0', string_length)) {
+      return OTS_FAILURE_MSG("Bad string of length %d", string_length);
+    }
+    post->names.push_back(
+        std::string(reinterpret_cast<const char*>(strings + 1), string_length));
+    strings += 1 + string_length;
+  }
+  const unsigned num_strings = post->names.size();
+
+  // check that all the references are within bounds
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    unsigned offset = post->glyph_name_index[i];
+    if (offset < 258) {
+      continue;
+    }
+
+    offset -= 258;
+    if (offset >= num_strings) {
+      return OTS_FAILURE_MSG("Bad string index %d", offset);
+    }
+  }
+
+  return true;
+}
+
+bool ots_post_should_serialise(OpenTypeFile *file) {
+  return file->post != NULL;
+}
+
+bool ots_post_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypePOST *post = file->post;
+
+  // OpenType with CFF glyphs must have v3 post table.
+  if (file->post && file->cff && file->post->version != 0x00030000) {
+    return OTS_FAILURE_MSG("Bad post version %x", post->version);
+  }
+
+  if (!out->WriteU32(post->version) ||
+      !out->WriteU32(post->italic_angle) ||
+      !out->WriteS16(post->underline) ||
+      !out->WriteS16(post->underline_thickness) ||
+      !out->WriteU32(post->is_fixed_pitch) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0)) {
+    return OTS_FAILURE_MSG("Failed to write post header");
+  }
+
+  if (post->version != 0x00020000) {
+    return true;  // v1.0 and v3.0 does not have glyph names.
+  }
+
+  const uint16_t num_indexes =
+      static_cast<uint16_t>(post->glyph_name_index.size());
+  if (num_indexes != post->glyph_name_index.size() ||
+      !out->WriteU16(num_indexes)) {
+    return OTS_FAILURE_MSG("Failed to write number of indices");
+  }
+
+  for (uint16_t i = 0; i < num_indexes; ++i) {
+    if (!out->WriteU16(post->glyph_name_index[i])) {
+      return OTS_FAILURE_MSG("Failed to write name index %d", i);
+    }
+  }
+
+  // Now we just have to write out the strings in the correct order
+  for (unsigned i = 0; i < post->names.size(); ++i) {
+    const std::string& s = post->names[i];
+    const uint8_t string_length = static_cast<uint8_t>(s.size());
+    if (string_length != s.size() ||
+        !out->Write(&string_length, 1)) {
+      return OTS_FAILURE_MSG("Failed to write string %d", i);
+    }
+    // Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
+    // We allow them.
+    if (string_length > 0 && !out->Write(s.data(), string_length)) {
+      return OTS_FAILURE_MSG("Failed to write string length for string %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_post_free(OpenTypeFile *file) {
+  delete file->post;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/post.h b/third_party/ots/src/post.h
new file mode 100644
index 0000000..f220d4f
--- /dev/null
+++ b/third_party/ots/src/post.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_POST_H_
+#define OTS_POST_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct OpenTypePOST {
+  uint32_t version;
+  uint32_t italic_angle;
+  int16_t underline;
+  int16_t underline_thickness;
+  uint32_t is_fixed_pitch;
+
+  std::vector<uint16_t> glyph_name_index;
+  std::vector<std::string> names;
+};
+
+}  // namespace ots
+
+#endif  // OTS_POST_H_
diff --git a/third_party/ots/src/prep.cc b/third_party/ots/src/prep.cc
new file mode 100644
index 0000000..ea3dec0
--- /dev/null
+++ b/third_party/ots/src/prep.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "prep.h"
+
+// prep - Control Value Program
+// http://www.microsoft.com/typography/otspec/prep.htm
+
+#define TABLE_NAME "prep"
+
+namespace ots {
+
+bool ots_prep_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypePREP *prep = new OpenTypePREP;
+  file->prep = prep;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("table length %ld > 120K", length);  // almost all prep tables are less than 9k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Failed to read table of length %ld", length);
+  }
+
+  prep->data = data;
+  prep->length = length;
+  return true;
+}
+
+bool ots_prep_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->prep != NULL;
+}
+
+bool ots_prep_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypePREP *prep = file->prep;
+
+  if (!out->Write(prep->data, prep->length)) {
+    return OTS_FAILURE_MSG("Failed to write table length");
+  }
+
+  return true;
+}
+
+void ots_prep_free(OpenTypeFile *file) {
+  delete file->prep;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/prep.h b/third_party/ots/src/prep.h
new file mode 100644
index 0000000..935ca11
--- /dev/null
+++ b/third_party/ots/src/prep.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_PREP_H_
+#define OTS_PREP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypePREP {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_PREP_H_
diff --git a/third_party/ots/src/vdmx.cc b/third_party/ots/src/vdmx.cc
new file mode 100644
index 0000000..4853d63
--- /dev/null
+++ b/third_party/ots/src/vdmx.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vdmx.h"
+
+// VDMX - Vertical Device Metrics
+// http://www.microsoft.com/typography/otspec/vdmx.htm
+
+#define TABLE_NAME "VDMX"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->vdmx; \
+    file->vdmx = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->vdmx = new OpenTypeVDMX;
+  OpenTypeVDMX * const vdmx = file->vdmx;
+
+  if (!table.ReadU16(&vdmx->version) ||
+      !table.ReadU16(&vdmx->num_recs) ||
+      !table.ReadU16(&vdmx->num_ratios)) {
+    return OTS_FAILURE_MSG("Failed to read table header");
+  }
+
+  if (vdmx->version > 1) {
+    DROP_THIS_TABLE("bad version: %u", vdmx->version);
+    return true;  // continue transcoding
+  }
+
+  vdmx->rat_ranges.reserve(vdmx->num_ratios);
+  for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+    OpenTypeVDMXRatioRecord rec;
+
+    if (!table.ReadU8(&rec.charset) ||
+        !table.ReadU8(&rec.x_ratio) ||
+        !table.ReadU8(&rec.y_start_ratio) ||
+        !table.ReadU8(&rec.y_end_ratio)) {
+      return OTS_FAILURE_MSG("Failed to read ratio header %d", i);
+    }
+
+    if (rec.charset > 1) {
+      DROP_THIS_TABLE("bad charset: %u", rec.charset);
+      return true;
+    }
+
+    if (rec.y_start_ratio > rec.y_end_ratio) {
+      DROP_THIS_TABLE("bad y ratio");
+      return true;
+    }
+
+    // All values set to zero signal the default grouping to use;
+    // if present, this must be the last Ratio group in the table.
+    if ((i < vdmx->num_ratios - 1u) &&
+        (rec.x_ratio == 0) &&
+        (rec.y_start_ratio == 0) &&
+        (rec.y_end_ratio == 0)) {
+      // workaround for fonts which have 2 or more {0, 0, 0} terminators.
+      DROP_THIS_TABLE("superfluous terminator found");
+      return true;
+    }
+
+    vdmx->rat_ranges.push_back(rec);
+  }
+
+  vdmx->offsets.reserve(vdmx->num_ratios);
+  const size_t current_offset = table.offset();
+  // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
+  for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+    uint16_t offset;
+    if (!table.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read ratio offset %d", i);
+    }
+    if (current_offset + offset >= length) {  // thus doesn't overflow.
+      return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i);
+    }
+
+    vdmx->offsets.push_back(offset);
+  }
+
+  vdmx->groups.reserve(vdmx->num_recs);
+  for (unsigned i = 0; i < vdmx->num_recs; ++i) {
+    OpenTypeVDMXGroup group;
+    if (!table.ReadU16(&group.recs) ||
+        !table.ReadU8(&group.startsz) ||
+        !table.ReadU8(&group.endsz)) {
+      return OTS_FAILURE_MSG("Failed to read record header %d", i);
+    }
+    group.entries.reserve(group.recs);
+    for (unsigned j = 0; j < group.recs; ++j) {
+      OpenTypeVDMXVTable vt;
+      if (!table.ReadU16(&vt.y_pel_height) ||
+          !table.ReadS16(&vt.y_max) ||
+          !table.ReadS16(&vt.y_min)) {
+        return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j);
+      }
+      if (vt.y_max < vt.y_min) {
+        DROP_THIS_TABLE("bad y min/max");
+        return true;
+      }
+
+      // This table must appear in sorted order (sorted by yPelHeight),
+      // but need not be continuous.
+      if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
+        DROP_THIS_TABLE("the table is not sorted");
+        return true;
+      }
+
+      group.entries.push_back(vt);
+    }
+    vdmx->groups.push_back(group);
+  }
+
+  return true;
+}
+
+bool ots_vdmx_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->vdmx != NULL;
+}
+
+bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeVDMX * const vdmx = file->vdmx;
+
+  if (!out->WriteU16(vdmx->version) ||
+      !out->WriteU16(vdmx->num_recs) ||
+      !out->WriteU16(vdmx->num_ratios)) {
+    return OTS_FAILURE_MSG("Failed to write table header");
+  }
+
+  for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
+    const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
+    if (!out->Write(&rec.charset, 1) ||
+        !out->Write(&rec.x_ratio, 1) ||
+        !out->Write(&rec.y_start_ratio, 1) ||
+        !out->Write(&rec.y_end_ratio, 1)) {
+      return OTS_FAILURE_MSG("Failed to write ratio %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
+    if (!out->WriteU16(vdmx->offsets[i])) {
+      return OTS_FAILURE_MSG("Failed to write ratio offset %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
+    const OpenTypeVDMXGroup& group = vdmx->groups[i];
+    if (!out->WriteU16(group.recs) ||
+        !out->Write(&group.startsz, 1) ||
+        !out->Write(&group.endsz, 1)) {
+      return OTS_FAILURE_MSG("Failed to write group %d", i);
+    }
+    for (unsigned j = 0; j < group.entries.size(); ++j) {
+      const OpenTypeVDMXVTable& vt = group.entries[j];
+      if (!out->WriteU16(vt.y_pel_height) ||
+          !out->WriteS16(vt.y_max) ||
+          !out->WriteS16(vt.y_min)) {
+        return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_vdmx_free(OpenTypeFile *file) {
+  delete file->vdmx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/vdmx.h b/third_party/ots/src/vdmx.h
new file mode 100644
index 0000000..1d959ef
--- /dev/null
+++ b/third_party/ots/src/vdmx.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VDMX_H_
+#define OTS_VDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVDMXRatioRecord {
+  uint8_t charset;
+  uint8_t x_ratio;
+  uint8_t y_start_ratio;
+  uint8_t y_end_ratio;
+};
+
+struct OpenTypeVDMXVTable {
+  uint16_t y_pel_height;
+  int16_t y_max;
+  int16_t y_min;
+};
+
+struct OpenTypeVDMXGroup {
+  uint16_t recs;
+  uint8_t startsz;
+  uint8_t endsz;
+  std::vector<OpenTypeVDMXVTable> entries;
+};
+
+struct OpenTypeVDMX {
+  uint16_t version;
+  uint16_t num_recs;
+  uint16_t num_ratios;
+  std::vector<OpenTypeVDMXRatioRecord> rat_ranges;
+  std::vector<uint16_t> offsets;
+  std::vector<OpenTypeVDMXGroup> groups;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VDMX_H_
diff --git a/third_party/ots/src/vhea.cc b/third_party/ots/src/vhea.cc
new file mode 100644
index 0000000..c689a83
--- /dev/null
+++ b/third_party/ots/src/vhea.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vhea.h"
+
+#include "gsub.h"
+#include "head.h"
+#include "maxp.h"
+
+// vhea - Vertical Header Table
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "vhea"
+
+namespace ots {
+
+bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVHEA *vhea = new OpenTypeVHEA;
+  file->vhea = vhea;
+
+  if (!table.ReadU32(&vhea->header.version)) {
+    return OTS_FAILURE_MSG("Failed to read version");
+  }
+  if (vhea->header.version != 0x00010000 &&
+      vhea->header.version != 0x00011000) {
+    return OTS_FAILURE_MSG("Bad vhea version %x", vhea->header.version);
+  }
+
+  if (!ParseMetricsHeader(file, &table, &vhea->header)) {
+    return OTS_FAILURE_MSG("Failed to parse metrics in vhea");
+  }
+
+  return true;
+}
+
+bool ots_vhea_should_serialise(OpenTypeFile *file) {
+  // vhea should'nt serialise when vmtx doesn't exist.
+  // Firefox developer pointed out that vhea/vmtx should serialise iff GSUB is
+  // preserved. See http://crbug.com/77386
+  return file->vhea != NULL && file->vmtx != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsHeader(file, out, &file->vhea->header)) {
+    return OTS_FAILURE_MSG("Failed to write vhea metrics");
+  }
+  return true;
+}
+
+void ots_vhea_free(OpenTypeFile *file) {
+  delete file->vhea;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/vhea.h b/third_party/ots/src/vhea.h
new file mode 100644
index 0000000..f8efde7
--- /dev/null
+++ b/third_party/ots/src/vhea.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VHEA_H_
+#define OTS_VHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVHEA {
+  OpenTypeMetricsHeader header;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VHEA_H_
+
diff --git a/third_party/ots/src/vmtx.cc b/third_party/ots/src/vmtx.cc
new file mode 100644
index 0000000..e29d32b
--- /dev/null
+++ b/third_party/ots/src/vmtx.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vmtx.h"
+
+#include "gsub.h"
+#include "maxp.h"
+#include "vhea.h"
+
+// vmtx - Vertical Metrics Table
+// http://www.microsoft.com/typography/otspec/vmtx.htm
+
+#define TABLE_NAME "vmtx"
+
+namespace ots {
+
+bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVMTX *vmtx = new OpenTypeVMTX;
+  file->vmtx = vmtx;
+
+  if (!file->vhea || !file->maxp) {
+    return OTS_FAILURE_MSG("vhea or maxp table missing as needed by vmtx");
+  }
+
+  if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs,
+                         &file->vhea->header, &vmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to parse vmtx metrics");
+  }
+
+  return true;
+}
+
+bool ots_vmtx_should_serialise(OpenTypeFile *file) {
+  // vmtx should serialise when vhea and GSUB are preserved.
+  // See the comment in ots_vhea_should_serialise().
+  return file->vmtx != NULL && file->vhea != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsTable(file, out, &file->vmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to write vmtx metrics");
+  }
+  return true;
+}
+
+void ots_vmtx_free(OpenTypeFile *file) {
+  delete file->vmtx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/vmtx.h b/third_party/ots/src/vmtx.h
new file mode 100644
index 0000000..061dc73
--- /dev/null
+++ b/third_party/ots/src/vmtx.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VMTX_H_
+#define OTS_VMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVMTX {
+  OpenTypeMetricsTable metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VMTX_H_
+
diff --git a/third_party/ots/src/vorg.cc b/third_party/ots/src/vorg.cc
new file mode 100644
index 0000000..2662067
--- /dev/null
+++ b/third_party/ots/src/vorg.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vorg.h"
+
+#include <vector>
+
+// VORG - Vertical Origin Table
+// http://www.microsoft.com/typography/otspec/vorg.htm
+
+#define TABLE_NAME "VORG"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->vorg; \
+    file->vorg = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_vorg_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->vorg = new OpenTypeVORG;
+  OpenTypeVORG * const vorg = file->vorg;
+
+  uint16_t num_recs;
+  if (!table.ReadU16(&vorg->major_version) ||
+      !table.ReadU16(&vorg->minor_version) ||
+      !table.ReadS16(&vorg->default_vert_origin_y) ||
+      !table.ReadU16(&num_recs)) {
+    return OTS_FAILURE_MSG("Failed to read header");
+  }
+  if (vorg->major_version != 1) {
+    DROP_THIS_TABLE("bad major version: %u", vorg->major_version);
+    return true;
+  }
+  if (vorg->minor_version != 0) {
+    DROP_THIS_TABLE("bad minor version: %u", vorg->minor_version);
+    return true;
+  }
+
+  // num_recs might be zero (e.g., DFHSMinchoPro5-W3-Demo.otf).
+  if (!num_recs) {
+    return true;
+  }
+
+  uint16_t last_glyph_index = 0;
+  vorg->metrics.reserve(num_recs);
+  for (unsigned i = 0; i < num_recs; ++i) {
+    OpenTypeVORGMetrics rec;
+
+    if (!table.ReadU16(&rec.glyph_index) ||
+        !table.ReadS16(&rec.vert_origin_y)) {
+      return OTS_FAILURE_MSG("Failed to read record %d", i);
+    }
+    if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
+      DROP_THIS_TABLE("the table is not sorted");
+      return true;
+    }
+    last_glyph_index = rec.glyph_index;
+
+    vorg->metrics.push_back(rec);
+  }
+
+  return true;
+}
+
+bool ots_vorg_should_serialise(OpenTypeFile *file) {
+  if (!file->cff) return false;  // this table is not for fonts with TT glyphs.
+  return file->vorg != NULL;
+}
+
+bool ots_vorg_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeVORG * const vorg = file->vorg;
+  
+  const uint16_t num_metrics = static_cast<uint16_t>(vorg->metrics.size());
+  if (num_metrics != vorg->metrics.size() ||
+      !out->WriteU16(vorg->major_version) ||
+      !out->WriteU16(vorg->minor_version) ||
+      !out->WriteS16(vorg->default_vert_origin_y) ||
+      !out->WriteU16(num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to write table header");
+  }
+
+  for (uint16_t i = 0; i < num_metrics; ++i) {
+    const OpenTypeVORGMetrics& rec = vorg->metrics[i];
+    if (!out->WriteU16(rec.glyph_index) ||
+        !out->WriteS16(rec.vert_origin_y)) {
+      return OTS_FAILURE_MSG("Failed to write record %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_vorg_free(OpenTypeFile *file) {
+  delete file->vorg;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/vorg.h b/third_party/ots/src/vorg.h
new file mode 100644
index 0000000..c3d3ffd
--- /dev/null
+++ b/third_party/ots/src/vorg.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VORG_H_
+#define OTS_VORG_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVORGMetrics {
+  uint16_t glyph_index;
+  int16_t vert_origin_y;
+};
+
+struct OpenTypeVORG {
+  uint16_t major_version;
+  uint16_t minor_version;
+  int16_t default_vert_origin_y;
+  std::vector<OpenTypeVORGMetrics> metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VORG_H_
diff --git a/third_party/ots/src/woff2.cc b/third_party/ots/src/woff2.cc
new file mode 100644
index 0000000..31b562d
--- /dev/null
+++ b/third_party/ots/src/woff2.cc
@@ -0,0 +1,991 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the implementation of decompression of the proposed WOFF Ultra
+// Condensed file format.
+
+#include <cassert>
+#include <cstdlib>
+#include <vector>
+
+#include "third_party/brotli/src/brotli/dec/decode.h"
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+#include "ots.h"
+#include "woff2.h"
+
+#define TABLE_NAME "WOFF2"
+
+namespace {
+
+// simple glyph flags
+const uint8_t kGlyfOnCurve = 1 << 0;
+const uint8_t kGlyfXShort = 1 << 1;
+const uint8_t kGlyfYShort = 1 << 2;
+const uint8_t kGlyfRepeat = 1 << 3;
+const uint8_t kGlyfThisXIsSame = 1 << 4;
+const uint8_t kGlyfThisYIsSame = 1 << 5;
+
+// composite glyph flags
+const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const int FLAG_WE_HAVE_A_SCALE = 1 << 3;
+const int FLAG_MORE_COMPONENTS = 1 << 5;
+const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+const size_t kSfntHeaderSize = 12;
+const size_t kSfntEntrySize = 16;
+const size_t kCheckSumAdjustmentOffset = 8;
+
+const size_t kEndPtsOfContoursOffset = 10;
+const size_t kCompositeGlyphBegin = 10;
+
+// Note that the byte order is big-endian, not the same as ots.cc
+#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d)
+
+const unsigned int kWoff2FlagsTransform = 1 << 5;
+
+const uint32_t kKnownTags[] = {
+  TAG('c', 'm', 'a', 'p'),  // 0
+  TAG('h', 'e', 'a', 'd'),  // 1
+  TAG('h', 'h', 'e', 'a'),  // 2
+  TAG('h', 'm', 't', 'x'),  // 3
+  TAG('m', 'a', 'x', 'p'),  // 4
+  TAG('n', 'a', 'm', 'e'),  // 5
+  TAG('O', 'S', '/', '2'),  // 6
+  TAG('p', 'o', 's', 't'),  // 7
+  TAG('c', 'v', 't', ' '),  // 8
+  TAG('f', 'p', 'g', 'm'),  // 9
+  TAG('g', 'l', 'y', 'f'),  // 10
+  TAG('l', 'o', 'c', 'a'),  // 11
+  TAG('p', 'r', 'e', 'p'),  // 12
+  TAG('C', 'F', 'F', ' '),  // 13
+  TAG('V', 'O', 'R', 'G'),  // 14
+  TAG('E', 'B', 'D', 'T'),  // 15
+  TAG('E', 'B', 'L', 'C'),  // 16
+  TAG('g', 'a', 's', 'p'),  // 17
+  TAG('h', 'd', 'm', 'x'),  // 18
+  TAG('k', 'e', 'r', 'n'),  // 19
+  TAG('L', 'T', 'S', 'H'),  // 20
+  TAG('P', 'C', 'L', 'T'),  // 21
+  TAG('V', 'D', 'M', 'X'),  // 22
+  TAG('v', 'h', 'e', 'a'),  // 23
+  TAG('v', 'm', 't', 'x'),  // 24
+  TAG('B', 'A', 'S', 'E'),  // 25
+  TAG('G', 'D', 'E', 'F'),  // 26
+  TAG('G', 'P', 'O', 'S'),  // 27
+  TAG('G', 'S', 'U', 'B'),  // 28
+  TAG('E', 'B', 'S', 'C'),  // 29
+  TAG('J', 'S', 'T', 'F'),  // 30
+  TAG('M', 'A', 'T', 'H'),  // 31
+  TAG('C', 'B', 'D', 'T'),  // 32
+  TAG('C', 'B', 'L', 'C'),  // 33
+  TAG('C', 'O', 'L', 'R'),  // 34
+  TAG('C', 'P', 'A', 'L'),  // 35
+  TAG('S', 'V', 'G', ' '),  // 36
+  TAG('s', 'b', 'i', 'x'),  // 37
+  TAG('a', 'c', 'n', 't'),  // 38
+  TAG('a', 'v', 'a', 'r'),  // 39
+  TAG('b', 'd', 'a', 't'),  // 40
+  TAG('b', 'l', 'o', 'c'),  // 41
+  TAG('b', 's', 'l', 'n'),  // 42
+  TAG('c', 'v', 'a', 'r'),  // 43
+  TAG('f', 'd', 's', 'c'),  // 44
+  TAG('f', 'e', 'a', 't'),  // 45
+  TAG('f', 'm', 't', 'x'),  // 46
+  TAG('f', 'v', 'a', 'r'),  // 47
+  TAG('g', 'v', 'a', 'r'),  // 48
+  TAG('h', 's', 't', 'y'),  // 49
+  TAG('j', 'u', 's', 't'),  // 50
+  TAG('l', 'c', 'a', 'r'),  // 51
+  TAG('m', 'o', 'r', 't'),  // 52
+  TAG('m', 'o', 'r', 'x'),  // 53
+  TAG('o', 'p', 'b', 'd'),  // 54
+  TAG('p', 'r', 'o', 'p'),  // 55
+  TAG('t', 'r', 'a', 'k'),  // 56
+  TAG('Z', 'a', 'p', 'f'),  // 57
+  TAG('S', 'i', 'l', 'f'),  // 58
+  TAG('G', 'l', 'a', 't'),  // 59
+  TAG('G', 'l', 'o', 'c'),  // 60
+  TAG('F', 'e', 'a', 't'),  // 61
+  TAG('S', 'i', 'l', 'l'),  // 62
+};
+
+struct Point {
+  int16_t x;
+  int16_t y;
+  bool on_curve;
+};
+
+struct Table {
+  uint32_t tag;
+  uint32_t flags;
+
+  uint32_t transform_length;
+
+  uint32_t dst_offset;
+  uint32_t dst_length;
+
+  Table()
+      : tag(0),
+        flags(0),
+        transform_length(0),
+        dst_offset(0),
+        dst_length(0) {}
+};
+
+// Based on section 6.1.1 of MicroType Express draft spec
+bool Read255UShort(ots::Buffer* buf, uint16_t* value) {
+  static const uint8_t kWordCode = 253;
+  static const uint8_t kOneMoreByteCode2 = 254;
+  static const uint8_t kOneMoreByteCode1 = 255;
+  static const uint8_t kLowestUCode = 253;
+  uint8_t code = 0;
+  if (!buf->ReadU8(&code)) {
+    return OTS_FAILURE();
+  }
+  if (code == kWordCode) {
+    uint16_t result = 0;
+    if (!buf->ReadU16(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result;
+    return true;
+  } else if (code == kOneMoreByteCode1) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode;
+    return true;
+  } else if (code == kOneMoreByteCode2) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode * 2;
+    return true;
+  } else {
+    *value = code;
+    return true;
+  }
+}
+
+bool ReadBase128(ots::Buffer* buf, uint32_t* value) {
+  uint32_t result = 0;
+  for (size_t i = 0; i < 5; ++i) {
+    uint8_t code = 0;
+    if (!buf->ReadU8(&code)) {
+      return OTS_FAILURE();
+    }
+    // If any of the top seven bits are set then we're about to overflow.
+    if (result & 0xfe000000U) {
+      return OTS_FAILURE();
+    }
+    result = (result << 7) | (code & 0x7f);
+    if ((code & 0x80) == 0) {
+      *value = result;
+      return true;
+    }
+  }
+  // Make sure not to exceed the size bound
+  return OTS_FAILURE();
+}
+
+// Caller must ensure that buffer overrun won't happen.
+// TODO(ksakamaoto): Consider creating 'writer' version of the Buffer class
+// and use it across the code.
+size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
+  dst[offset] = x >> 24;
+  dst[offset + 1] = (x >> 16) & 0xff;
+  dst[offset + 2] = (x >> 8) & 0xff;
+  dst[offset + 3] = x & 0xff;
+  return offset + 4;
+}
+
+size_t StoreU16(uint8_t* dst, size_t offset, uint16_t x) {
+  dst[offset] = x >> 8;
+  dst[offset + 1] = x & 0xff;
+  return offset + 2;
+}
+
+int WithSign(int flag, int baseval) {
+  assert(0 <= baseval && baseval < 65536);
+  return (flag & 1) ? baseval : -baseval;
+}
+
+bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
+    unsigned int n_points, std::vector<Point>* result,
+    size_t* in_bytes_consumed) {
+  int x = 0;
+  int y = 0;
+
+  // Early return if |in| buffer is too small. Each point consumes 1-4 bytes.
+  if (n_points > in_size) {
+    return OTS_FAILURE();
+  }
+  unsigned int triplet_index = 0;
+
+  for (unsigned int i = 0; i < n_points; ++i) {
+    uint8_t flag = flags_in[i];
+    bool on_curve = !(flag >> 7);
+    flag &= 0x7f;
+    unsigned int n_data_bytes;
+    if (flag < 84) {
+      n_data_bytes = 1;
+    } else if (flag < 120) {
+      n_data_bytes = 2;
+    } else if (flag < 124) {
+      n_data_bytes = 3;
+    } else {
+      n_data_bytes = 4;
+    }
+    if (triplet_index + n_data_bytes > in_size ||
+        triplet_index + n_data_bytes < triplet_index) {
+      return OTS_FAILURE();
+    }
+    int dx, dy;
+    if (flag < 10) {
+      dx = 0;
+      dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]);
+    } else if (flag < 20) {
+      dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]);
+      dy = 0;
+    } else if (flag < 84) {
+      int b0 = flag - 20;
+      int b1 = in[triplet_index];
+      dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
+      dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
+    } else if (flag < 120) {
+      int b0 = flag - 84;
+      dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]);
+      dy = WithSign(flag >> 1,
+                    1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]);
+    } else if (flag < 124) {
+      int b2 = in[triplet_index + 1];
+      dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4));
+      dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]);
+    } else {
+      dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]);
+      dy = WithSign(flag >> 1,
+          (in[triplet_index + 2] << 8) + in[triplet_index + 3]);
+    }
+    triplet_index += n_data_bytes;
+    // Possible overflow but coordinate values are not security sensitive
+    x += dx;
+    y += dy;
+    result->push_back(Point());
+    Point& back = result->back();
+    back.x = static_cast<int16_t>(x);
+    back.y = static_cast<int16_t>(y);
+    back.on_curve = on_curve;
+  }
+  *in_bytes_consumed = triplet_index;
+  return true;
+}
+
+// This function stores just the point data. On entry, dst points to the
+// beginning of a simple glyph. Returns true on success.
+bool StorePoints(const std::vector<Point>& points,
+    unsigned int n_contours, unsigned int instruction_length,
+    uint8_t* dst, size_t dst_size, size_t* glyph_size) {
+  // I believe that n_contours < 65536, in which case this is safe. However, a
+  // comment and/or an assert would be good.
+  unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
+    instruction_length;
+  uint8_t last_flag = 0xff;
+  uint8_t repeat_count = 0;
+  int last_x = 0;
+  int last_y = 0;
+  unsigned int x_bytes = 0;
+  unsigned int y_bytes = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    const Point& point = points.at(i);
+    uint8_t flag = point.on_curve ? kGlyfOnCurve : 0;
+    int dx = point.x - last_x;
+    int dy = point.y - last_y;
+    if (dx == 0) {
+      flag |= kGlyfThisXIsSame;
+    } else if (dx > -256 && dx < 256) {
+      flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0);
+      x_bytes += 1;
+    } else {
+      x_bytes += 2;
+    }
+    if (dy == 0) {
+      flag |= kGlyfThisYIsSame;
+    } else if (dy > -256 && dy < 256) {
+      flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0);
+      y_bytes += 1;
+    } else {
+      y_bytes += 2;
+    }
+
+    if (flag == last_flag && repeat_count != 255) {
+      dst[flag_offset - 1] |= kGlyfRepeat;
+      repeat_count++;
+    } else {
+      if (repeat_count != 0) {
+        if (flag_offset >= dst_size) {
+          return OTS_FAILURE();
+        }
+        dst[flag_offset++] = repeat_count;
+      }
+      if (flag_offset >= dst_size) {
+        return OTS_FAILURE();
+      }
+      dst[flag_offset++] = flag;
+      repeat_count = 0;
+    }
+    last_x = point.x;
+    last_y = point.y;
+    last_flag = flag;
+  }
+
+  if (repeat_count != 0) {
+    if (flag_offset >= dst_size) {
+      return OTS_FAILURE();
+    }
+    dst[flag_offset++] = repeat_count;
+  }
+  unsigned int xy_bytes = x_bytes + y_bytes;
+  if (xy_bytes < x_bytes ||
+      flag_offset + xy_bytes < flag_offset ||
+      flag_offset + xy_bytes > dst_size) {
+    return OTS_FAILURE();
+  }
+
+  int x_offset = flag_offset;
+  int y_offset = flag_offset + x_bytes;
+  last_x = 0;
+  last_y = 0;
+  for (size_t i = 0; i < points.size(); ++i) {
+    int dx = points.at(i).x - last_x;
+    if (dx == 0) {
+      // pass
+    } else if (dx > -256 && dx < 256) {
+      dst[x_offset++] = static_cast<uint8_t>(std::abs(dx));
+    } else {
+      // will always fit for valid input, but overflow is harmless
+      x_offset = StoreU16(dst, x_offset, static_cast<uint16_t>(dx));
+    }
+    last_x += dx;
+    int dy = points.at(i).y - last_y;
+    if (dy == 0) {
+      // pass
+    } else if (dy > -256 && dy < 256) {
+      dst[y_offset++] = static_cast<uint8_t>(std::abs(dy));
+    } else {
+      y_offset = StoreU16(dst, y_offset, static_cast<uint16_t>(dy));
+    }
+    last_y += dy;
+  }
+  *glyph_size = y_offset;
+  return true;
+}
+
+// Compute the bounding box of the coordinates, and store into a glyf buffer.
+// A precondition is that there are at least 10 bytes available.
+void ComputeBbox(const std::vector<Point>& points, uint8_t* dst) {
+  int16_t x_min = 0;
+  int16_t y_min = 0;
+  int16_t x_max = 0;
+  int16_t y_max = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    int16_t x = points.at(i).x;
+    int16_t y = points.at(i).y;
+    if (i == 0 || x < x_min) x_min = x;
+    if (i == 0 || x > x_max) x_max = x;
+    if (i == 0 || y < y_min) y_min = y;
+    if (i == 0 || y > y_max) y_max = y;
+  }
+  size_t offset = 2;
+  offset = StoreU16(dst, offset, x_min);
+  offset = StoreU16(dst, offset, y_min);
+  offset = StoreU16(dst, offset, x_max);
+  offset = StoreU16(dst, offset, y_max);
+}
+
+// Process entire bbox stream. This is done as a separate pass to allow for
+// composite bbox computations (an optional more aggressive transform).
+bool ProcessBboxStream(ots::Buffer* bbox_stream, unsigned int n_glyphs,
+    const std::vector<uint32_t>& loca_values, uint8_t* glyf_buf,
+    size_t glyf_buf_length) {
+  const uint8_t* buf = bbox_stream->buffer();
+  if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) {
+    return OTS_FAILURE();
+  }
+  // Safe because n_glyphs is bounded
+  unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2;
+  if (!bbox_stream->Skip(bitmap_length)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned int i = 0; i < n_glyphs; ++i) {
+    if (buf[i >> 3] & (0x80 >> (i & 7))) {
+      uint32_t loca_offset = loca_values.at(i);
+      if (loca_values.at(i + 1) - loca_offset < kEndPtsOfContoursOffset) {
+        return OTS_FAILURE();
+      }
+      if (glyf_buf_length < 2 + 10 ||
+          loca_offset > glyf_buf_length - 2 - 10) {
+        return OTS_FAILURE();
+      }
+      if (!bbox_stream->Read(glyf_buf + loca_offset + 2, 8)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+bool ProcessComposite(ots::Buffer* composite_stream, uint8_t* dst,
+    size_t dst_size, size_t* glyph_size, bool* have_instructions) {
+  size_t start_offset = composite_stream->offset();
+  bool we_have_instructions = false;
+
+  uint16_t flags = FLAG_MORE_COMPONENTS;
+  while (flags & FLAG_MORE_COMPONENTS) {
+    if (!composite_stream->ReadU16(&flags)) {
+      return OTS_FAILURE();
+    }
+    we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0;
+    size_t arg_size = 2;  // glyph index
+    if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) {
+      arg_size += 4;
+    } else {
+      arg_size += 2;
+    }
+    if (flags & FLAG_WE_HAVE_A_SCALE) {
+      arg_size += 2;
+    } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+      arg_size += 4;
+    } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) {
+      arg_size += 8;
+    }
+    if (!composite_stream->Skip(arg_size)) {
+      return OTS_FAILURE();
+    }
+  }
+  size_t composite_glyph_size = composite_stream->offset() - start_offset;
+  if (composite_glyph_size + kCompositeGlyphBegin > dst_size) {
+    return OTS_FAILURE();
+  }
+  StoreU16(dst, 0, 0xffff);  // nContours = -1 for composite glyph
+  std::memcpy(dst + kCompositeGlyphBegin,
+      composite_stream->buffer() + start_offset,
+      composite_glyph_size);
+  *glyph_size = kCompositeGlyphBegin + composite_glyph_size;
+  *have_instructions = we_have_instructions;
+  return true;
+}
+
+// Build TrueType loca table
+bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
+    uint8_t* dst, size_t dst_size) {
+  const uint64_t loca_size = loca_values.size();
+  const uint64_t offset_size = index_format ? 4 : 2;
+  if ((loca_size << 2) >> 2 != loca_size) {
+    return OTS_FAILURE();
+  }
+  // No integer overflow here (loca_size <= 2^16).
+  if (offset_size * loca_size > dst_size) {
+    return OTS_FAILURE();
+  }
+  size_t offset = 0;
+  for (size_t i = 0; i < loca_values.size(); ++i) {
+    uint32_t value = loca_values.at(i);
+    if (index_format) {
+      offset = StoreU32(dst, offset, value);
+    } else {
+      offset = StoreU16(dst, offset, static_cast<uint16_t>(value >> 1));
+    }
+  }
+  return true;
+}
+
+// Reconstruct entire glyf table based on transformed original
+bool ReconstructGlyf(const uint8_t* data, size_t data_size,
+    uint8_t* dst, size_t dst_size,
+    uint8_t* loca_buf, size_t loca_size) {
+  static const int kNumSubStreams = 7;
+  ots::Buffer file(data, data_size);
+  uint32_t version;
+  std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
+
+  if (!file.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  uint16_t num_glyphs;
+  uint16_t index_format;
+  if (!file.ReadU16(&num_glyphs) ||
+      !file.ReadU16(&index_format)) {
+    return OTS_FAILURE();
+  }
+  unsigned int offset = (2 + kNumSubStreams) * 4;
+  if (offset > data_size) {
+    return OTS_FAILURE();
+  }
+  // Invariant from here on: data_size >= offset
+  for (int i = 0; i < kNumSubStreams; ++i) {
+    uint32_t substream_size;
+    if (!file.ReadU32(&substream_size)) {
+      return OTS_FAILURE();
+    }
+    if (substream_size > data_size - offset) {
+      return OTS_FAILURE();
+    }
+    substreams.at(i) = std::make_pair(data + offset, substream_size);
+    offset += substream_size;
+  }
+  ots::Buffer n_contour_stream(substreams.at(0).first, substreams.at(0).second);
+  ots::Buffer n_points_stream(substreams.at(1).first, substreams.at(1).second);
+  ots::Buffer flag_stream(substreams.at(2).first, substreams.at(2).second);
+  ots::Buffer glyph_stream(substreams.at(3).first, substreams.at(3).second);
+  ots::Buffer composite_stream(substreams.at(4).first, substreams.at(4).second);
+  ots::Buffer bbox_stream(substreams.at(5).first, substreams.at(5).second);
+  ots::Buffer instruction_stream(substreams.at(6).first,
+                                 substreams.at(6).second);
+
+  std::vector<uint32_t> loca_values;
+  loca_values.reserve(num_glyphs + 1);
+  std::vector<uint16_t> n_points_vec;
+  std::vector<Point> points;
+  uint32_t loca_offset = 0;
+  for (unsigned int i = 0; i < num_glyphs; ++i) {
+    size_t glyph_size = 0;
+    uint16_t n_contours = 0;
+    if (!n_contour_stream.ReadU16(&n_contours)) {
+      return OTS_FAILURE();
+    }
+    uint8_t* glyf_dst = dst + loca_offset;
+    size_t glyf_dst_size = dst_size - loca_offset;
+    if (n_contours == 0xffff) {
+      // composite glyph
+      bool have_instructions = false;
+      uint16_t instruction_size = 0;
+      if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size,
+            &glyph_size, &have_instructions)) {
+        return OTS_FAILURE();
+      }
+      if (have_instructions) {
+        if (!Read255UShort(&glyph_stream, &instruction_size)) {
+          return OTS_FAILURE();
+        }
+        // No integer overflow here (instruction_size < 2^16).
+        if (instruction_size + 2U > glyf_dst_size - glyph_size) {
+          return OTS_FAILURE();
+        }
+        StoreU16(glyf_dst, glyph_size, instruction_size);
+        if (!instruction_stream.Read(glyf_dst + glyph_size + 2,
+              instruction_size)) {
+          return OTS_FAILURE();
+        }
+        glyph_size += instruction_size + 2;
+      }
+    } else if (n_contours > 0) {
+      // simple glyph
+      n_points_vec.clear();
+      points.clear();
+      uint32_t total_n_points = 0;
+      uint16_t n_points_contour;
+      for (uint32_t j = 0; j < n_contours; ++j) {
+        if (!Read255UShort(&n_points_stream, &n_points_contour)) {
+          return OTS_FAILURE();
+        }
+        n_points_vec.push_back(n_points_contour);
+        if (total_n_points + n_points_contour < total_n_points) {
+          return OTS_FAILURE();
+        }
+        total_n_points += n_points_contour;
+      }
+      uint32_t flag_size = total_n_points;
+      if (flag_size > flag_stream.length() - flag_stream.offset()) {
+        return OTS_FAILURE();
+      }
+      const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset();
+      const uint8_t* triplet_buf = glyph_stream.buffer() +
+        glyph_stream.offset();
+      size_t triplet_size = glyph_stream.length() - glyph_stream.offset();
+      size_t triplet_bytes_consumed = 0;
+      if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points,
+            &points, &triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      const uint32_t header_and_endpts_contours_size =
+          kEndPtsOfContoursOffset + 2 * n_contours;
+      if (glyf_dst_size < header_and_endpts_contours_size) {
+        return OTS_FAILURE();
+      }
+      StoreU16(glyf_dst, 0, n_contours);
+      ComputeBbox(points, glyf_dst);
+      size_t endpts_offset = kEndPtsOfContoursOffset;
+      int end_point = -1;
+      for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) {
+        end_point += n_points_vec.at(contour_ix);
+        if (end_point >= 65536) {
+          return OTS_FAILURE();
+        }
+        endpts_offset = StoreU16(glyf_dst, endpts_offset, static_cast<uint16_t>(end_point));
+      }
+      if (!flag_stream.Skip(flag_size)) {
+        return OTS_FAILURE();
+      }
+      if (!glyph_stream.Skip(triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      uint16_t instruction_size;
+      if (!Read255UShort(&glyph_stream, &instruction_size)) {
+        return OTS_FAILURE();
+      }
+      // No integer overflow here (instruction_size < 2^16).
+      if (glyf_dst_size - header_and_endpts_contours_size <
+          instruction_size + 2U) {
+        return OTS_FAILURE();
+      }
+      uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size;
+      StoreU16(instruction_dst, 0, instruction_size);
+      if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) {
+        return OTS_FAILURE();
+      }
+      if (!StorePoints(points, n_contours, instruction_size,
+            glyf_dst, glyf_dst_size, &glyph_size)) {
+        return OTS_FAILURE();
+      }
+    } else {
+      glyph_size = 0;
+    }
+    loca_values.push_back(loca_offset);
+    if (glyph_size + 3 < glyph_size) {
+      return OTS_FAILURE();
+    }
+    glyph_size = ots::Round2(glyph_size);
+    if (glyph_size > dst_size - loca_offset) {
+      // This shouldn't happen, but this test defensively maintains the
+      // invariant that loca_offset <= dst_size.
+      return OTS_FAILURE();
+    }
+    loca_offset += glyph_size;
+  }
+  loca_values.push_back(loca_offset);
+  assert(loca_values.size() == static_cast<size_t>(num_glyphs + 1));
+  if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values,
+          dst, dst_size)) {
+    return OTS_FAILURE();
+  }
+  return StoreLoca(loca_values, index_format, loca_buf, loca_size);
+}
+
+// This is linear search, but could be changed to binary because we
+// do have a guarantee that the tables are sorted by tag. But the total
+// cpu time is expected to be very small in any case.
+const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
+  size_t n_tables = tables.size();
+  for (size_t i = 0; i < n_tables; ++i) {
+    if (tables.at(i).tag == tag) {
+      return &tables.at(i);
+    }
+  }
+  return NULL;
+}
+
+bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
+    const uint8_t* transformed_buf, size_t transformed_size,
+    uint8_t* dst, size_t dst_length) {
+  if (tag == TAG('g', 'l', 'y', 'f')) {
+    const Table* glyf_table = FindTable(tables, tag);
+    const Table* loca_table = FindTable(tables, TAG('l', 'o', 'c', 'a'));
+    if (glyf_table == NULL || loca_table == NULL) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(glyf_table->dst_offset) + glyf_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(loca_table->dst_offset) + loca_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    return ReconstructGlyf(transformed_buf, transformed_size,
+        dst + glyf_table->dst_offset, glyf_table->dst_length,
+        dst + loca_table->dst_offset, loca_table->dst_length);
+  } else if (tag == TAG('l', 'o', 'c', 'a')) {
+    // processing was already done by glyf table, but validate
+    if (!FindTable(tables, TAG('g', 'l', 'y', 'f'))) {
+      return OTS_FAILURE();
+    }
+  } else {
+    // transform for the tag is not known
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
+  uint32_t checksum = 0;
+  for (size_t i = 0; i < size; i += 4) {
+    // We assume the addition is mod 2^32, which is valid because unsigned
+    checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
+      (buf[i + 2] << 8) | buf[i + 3];
+  }
+  return checksum;
+}
+
+bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
+  const Table* head_table = FindTable(tables, TAG('h', 'e', 'a', 'd'));
+  if (head_table == NULL ||
+      head_table->dst_length < kCheckSumAdjustmentOffset + 4) {
+    return OTS_FAILURE();
+  }
+  size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset;
+  if (adjustment_offset < head_table->dst_offset) {
+    return OTS_FAILURE();
+  }
+  StoreU32(dst, adjustment_offset, 0);
+  size_t n_tables = tables.size();
+  uint32_t file_checksum = 0;
+  for (size_t i = 0; i < n_tables; ++i) {
+    const Table* table = &tables.at(i);
+    size_t table_length = table->dst_length;
+    uint8_t* table_data = dst + table->dst_offset;
+    uint32_t checksum = ComputeChecksum(table_data, table_length);
+    StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
+    file_checksum += checksum;  // The addition is mod 2^32
+  }
+  file_checksum += ComputeChecksum(dst,
+      kSfntHeaderSize + kSfntEntrySize * n_tables);
+  uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
+  StoreU32(dst, adjustment_offset, checksum_adjustment);
+  return true;
+}
+
+bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
+    const uint8_t* src_buf, size_t src_size) {
+  size_t uncompressed_size = dst_size;
+  int ok = BrotliDecompressBuffer(src_size, src_buf,
+                                  &uncompressed_size, dst_buf);
+  if (!ok || uncompressed_size != dst_size) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool ReadTableDirectory(ots::OpenTypeFile* file,
+    ots::Buffer* buffer, std::vector<Table>* tables,
+    size_t num_tables) {
+  for (size_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables->at(i);
+    uint8_t flag_byte;
+    if (!buffer->ReadU8(&flag_byte)) {
+      return OTS_FAILURE_MSG("Failed to read the flags of table directory entry %d", i);
+    }
+    uint32_t tag;
+    if ((flag_byte & 0x3f) == 0x3f) {
+      if (!buffer->ReadU32(&tag)) {
+        return OTS_FAILURE_MSG("Failed to read the tag of table directory entry %d", i);
+      }
+    } else {
+      tag = kKnownTags[flag_byte & 0x3f];
+    }
+    // Bits 6 and 7 are reserved and must be 0.
+    if ((flag_byte & 0xc0) != 0) {
+      return OTS_FAILURE_MSG("Bits 6 and 7 are not 0 for table directory entry %d", i);
+    }
+    uint32_t flags = 0;
+    // Always transform the glyf and loca tables
+    if (tag == TAG('g', 'l', 'y', 'f') ||
+        tag == TAG('l', 'o', 'c', 'a')) {
+      flags |= kWoff2FlagsTransform;
+    }
+    uint32_t dst_length;
+    if (!ReadBase128(buffer, &dst_length)) {
+      return OTS_FAILURE_MSG("Failed to read \"origLength\" for table %4.4s", (char*)&tag);
+    }
+    uint32_t transform_length = dst_length;
+    if ((flags & kWoff2FlagsTransform) != 0) {
+      if (!ReadBase128(buffer, &transform_length)) {
+        return OTS_FAILURE_MSG("Failed to read \"transformLength\" for table %4.4s", (char*)&tag);
+      }
+    }
+    // Disallow huge numbers (> 1GB) for sanity.
+    if (transform_length > 1024 * 1024 * 1024 ||
+        dst_length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("\"origLength\" or \"transformLength\" > 1GB");
+    }
+    table->tag = tag;
+    table->flags = flags;
+    table->transform_length = transform_length;
+    table->dst_length = dst_length;
+  }
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
+  ots::Buffer file(data, length);
+  uint32_t total_length;
+
+  if (!file.Skip(16) ||
+      !file.ReadU32(&total_length)) {
+    return 0;
+  }
+  return total_length;
+}
+
+bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
+                       uint8_t* result, size_t result_length,
+                       const uint8_t* data, size_t length) {
+  static const uint32_t kWoff2Signature = 0x774f4632;  // "wOF2"
+  ots::Buffer buffer(data, length);
+
+  uint32_t signature;
+  uint32_t flavor = 0;
+  if (!buffer.ReadU32(&signature) || signature != kWoff2Signature ||
+      !buffer.ReadU32(&flavor)) {
+    return OTS_FAILURE_MSG("Failed to read \"signature\" or \"flavor\", or not WOFF2 signature");
+  }
+
+  if (!IsValidVersionTag(ntohl(flavor))) {
+    return OTS_FAILURE_MSG("Invalid \"flavor\"");
+  }
+
+  uint32_t reported_length;
+  if (!buffer.ReadU32(&reported_length) || length != reported_length) {
+    return OTS_FAILURE_MSG("Failed to read \"length\" or it does not match the actual file size");
+  }
+  uint16_t num_tables;
+  if (!buffer.ReadU16(&num_tables) || !num_tables) {
+    return OTS_FAILURE_MSG("Failed to read \"numTables\"");
+  }
+  // We don't care about these fields of the header:
+  //   uint16_t reserved
+  //   uint32_t total_sfnt_size
+  if (!buffer.Skip(6)) {
+    return OTS_FAILURE_MSG("Failed to read \"reserve\" or \"totalSfntSize\"");
+  }
+  uint32_t compressed_length;
+  if (!buffer.ReadU32(&compressed_length)) {
+    return OTS_FAILURE_MSG("Failed to read \"totalCompressedSize\"");
+  }
+  if (compressed_length > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // We don't care about these fields of the header:
+  //   uint16_t major_version, minor_version
+  //   uint32_t meta_offset, meta_length, meta_orig_length
+  //   uint32_t priv_offset, priv_length
+  if (!buffer.Skip(24)) {
+    return OTS_FAILURE();
+  }
+  std::vector<Table> tables(num_tables);
+  if (!ReadTableDirectory(file, &buffer, &tables, num_tables)) {
+    return OTS_FAILURE_MSG("Failed to read table directory");
+  }
+  uint64_t compressed_offset = buffer.offset();
+  if (compressed_offset > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE();
+  }
+  uint64_t dst_offset = kSfntHeaderSize +
+      kSfntEntrySize * static_cast<uint64_t>(num_tables);
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables.at(i);
+    table->dst_offset = static_cast<uint32_t>(dst_offset);
+    dst_offset += table->dst_length;
+    if (dst_offset > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+    dst_offset = ots::Round4(dst_offset);
+  }
+  if (ots::Round4(compressed_offset + compressed_length) > length || dst_offset > result_length) {
+    return OTS_FAILURE();
+  }
+
+  const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
+  if (sfnt_header_and_table_directory_size > result_length) {
+    return OTS_FAILURE();
+  }
+
+  // Start building the font
+  size_t offset = 0;
+  offset = StoreU32(result, offset, flavor);
+  offset = StoreU16(result, offset, num_tables);
+  uint8_t max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+  offset = StoreU16(result, offset, output_search_range);
+  offset = StoreU16(result, offset, max_pow2);
+  offset = StoreU16(result, offset, (num_tables << 4) - output_search_range);
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    offset = StoreU32(result, offset, table->tag);
+    offset = StoreU32(result, offset, 0);  // checksum, to fill in later
+    offset = StoreU32(result, offset, table->dst_offset);
+    offset = StoreU32(result, offset, table->dst_length);
+  }
+  std::vector<uint8_t> uncompressed_buf;
+  const uint8_t* transform_buf = NULL;
+  uint64_t total_size = 0;
+
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    total_size += tables.at(i).transform_length;
+    if (total_size > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+  }
+  // Enforce same 30M limit on uncompressed tables as OTS
+  if (total_size > 30 * 1024 * 1024) {
+    return OTS_FAILURE();
+  }
+  const size_t total_size_size_t = static_cast<size_t>(total_size);
+  uncompressed_buf.resize(total_size_size_t);
+  const uint8_t* src_buf = data + compressed_offset;
+  if (!Woff2Uncompress(&uncompressed_buf[0], total_size_size_t,
+      src_buf, compressed_length)) {
+    return OTS_FAILURE();
+  }
+  transform_buf = &uncompressed_buf[0];
+
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    uint32_t flags = table->flags;
+    size_t transform_length = table->transform_length;
+
+    if ((flags & kWoff2FlagsTransform) == 0) {
+      if (transform_length != table->dst_length) {
+        return OTS_FAILURE();
+      }
+      if (static_cast<uint64_t>(table->dst_offset) + transform_length >
+          result_length) {
+        return OTS_FAILURE();
+      }
+      std::memcpy(result + table->dst_offset, transform_buf,
+          transform_length);
+    } else {
+      if (!ReconstructTransformed(tables, table->tag,
+            transform_buf, transform_length, result, result_length)) {
+        return OTS_FAILURE();
+      }
+    }
+
+    transform_buf += transform_length;
+    if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return FixChecksums(tables, result);
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/woff2.h b/third_party/ots/src/woff2.h
new file mode 100644
index 0000000..1db259a
--- /dev/null
+++ b/third_party/ots/src/woff2.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_WOFF2_H_
+#define OTS_WOFF2_H_
+
+namespace ots {
+
+// Compute the size of the final uncompressed font, or 0 on error.
+size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length);
+
+// Decompresses the font into the target buffer. The result_length should
+// be the same as determined by ComputeFinalSize(). Returns true on successful
+// decompression.
+bool ConvertWOFF2ToTTF(OpenTypeFile *file, uint8_t *result, size_t result_length,
+                       const uint8_t *data, size_t length);
+}
+
+#endif  // OTS_WOFF2_H_
diff --git a/third_party/ots/test/BLACKLIST.txt b/third_party/ots/test/BLACKLIST.txt
new file mode 100644
index 0000000..f3cd207
--- /dev/null
+++ b/third_party/ots/test/BLACKLIST.txt
@@ -0,0 +1,124 @@
+# Required table(s) are missing (e.g. OS/2 table).
+AppleGothic.ttf
+AppleMyungjo.ttf
+ArialHB.ttf
+ArialHBBold.ttf
+Corsiva.ttf
+CorsivaBold.ttf
+InaiMathi.ttf
+NISC18030.ttf
+NewPeninimMT.ttf
+NewPeninimMTBold.ttf
+NewPeninimMTBoldInclined.ttf
+NewPeninimMTInclined.ttf
+Raanana.ttf
+RaananaBold.ttf
+
+# The length field of a table is weird.
+homa.ttf
+nazli.ttf
+titr.ttf
+ume-tgc4.ttf
+ume-tgs4.ttf
+ume-tgc5.ttf
+ume-tgs5.ttf
+ume-tms3.ttf
+
+# Table(s) are not 4-byte aligned.
+UnBatang.ttf
+UnBom.ttf
+UnDotum.ttf
+UnGraphic.ttf
+UnGungseo.ttf
+UnJamoBatang.ttf
+UnJamoDotum.ttf
+UnJamoNovel.ttf
+UnJamoSora.ttf
+UnPenheulim.ttf
+UnPen.ttf
+UnPilgiBold.ttf
+UnPilgi.ttf
+UnShinmun.ttf
+UnTaza.ttf
+UnYetgul.ttf
+
+# Tables are not sorted by table tags.
+f500.ttf
+
+# non-ASCII characters are used in a table tag
+SyrCOMAdiabene.otf
+SyrCOMAntioch.otf
+SyrCOMBatnanBold.otf
+SyrCOMBatnan.otf
+SyrCOMCtesiphon.otf
+SyrCOMJerusalemBold.otf
+SyrCOMJerusalemItalic.otf
+SyrCOMJerusalem.otf
+SyrCOMJerusalemOutline.otf
+SyrCOMKharput.otf
+SyrCOMMalankara.otf
+SyrCOMMardinBold.otf
+SyrCOMMardin.otf
+SyrCOMMidyat.otf
+SyrCOMNisibin.otf
+SyrCOMNisibinOutline.otf
+SyrCOMQenNeshrin.otf
+SyrCOMTalada.otf
+SyrCOMTurAbdin.otf
+SyrCOMUrhoyBold.otf
+SyrCOMUrhoy.otf
+
+# Malformed SFNT table; unexpected entry selector
+misakimn.ttf
+misaki.ttf
+
+# Malformed CMAP table; Subtables are not sorted by platform ID
+ani.ttf
+Caliban.ttf
+
+# Malformed CMAP table; Entries in a 3-0-4 or 3-1-4 subtable are not sorted.
+LucidaSansOblique.ttf
+LucidaTypewriterOblique.ttf
+bkai00mp.ttf
+bsmi00lp.ttf
+modelwor.ttf
+
+# Malformed CMAP table; "search range" in a 3-0-4 or 3-1-4 subtable are invalid.
+cmmi10.ttf
+cmsy10.ttf
+msam10.ttf
+
+# Malformed CMAP table; The 3-10-12 table is too short.
+BPG_Chveulebrivi.ttf
+BPG_Chveulebrivi_bold.ttf
+
+# Unsupported CMAP table; ots doesn't support non-Unicode fonts.
+Apple Symbols.ttf
+儷宋 Pro.ttf
+儷黑 Pro.ttf
+华文仿宋.ttf
+华文宋体.ttf
+华文楷体.ttf
+华文细黑.ttf
+华文黑体.ttf
+
+# Unsupported CMAP table; The Unicode BMP table is missing, while the UCS-4 table is available.
+DroidSansJapanese.ttf
+DroidSansFallback.ttf
+
+# Malformed GLYF table; The content of flags array and the lengths of xCoordinates, yCoordinates are inconsistent.
+DecoTypeNaskh.ttf
+
+# Malformed HMTX table; The table is too short.
+mona.ttf
+
+# CMAP glyph id is out of range.
+Samyak-Oriya.ttf
+
+# Unsupported CFF table; "supplemental encoding" is not supported at the moment. This should be fixed in the future.
+Walbf___.otf
+
+# GDEF MarkAttachClassDef offset is invalid.
+ManchuFont.ttf
+arianamu.ttf
+summersby.ttf
diff --git a/third_party/ots/test/README b/third_party/ots/test/README
new file mode 100644
index 0000000..f634e06
--- /dev/null
+++ b/third_party/ots/test/README
@@ -0,0 +1,243 @@
+------------------------------------------------------------------------------
+ot-sanitise - TTF/OTF font validator/transcoder
+
+Description:
+  ot-sanitise is a program which validates and/or transcodes a truetype or
+  opentype font file using the OTS library:
+
+      transcoded_font = ValidateAndTranscode(original_font);
+      if (validation_error)
+        PrintErrorAndExit;
+      OutputToStdout(transcoded_font);
+
+Usage:
+  $ ./ot-sanitise ttf_or_otf_file [transcoded_file]
+
+Example:
+  $ ./ot-sanitise sample.otf transcoded_sample.otf
+  $ ./ot-sanitise malformed.ttf
+  WARNING at ots/src/ots.cc:158: bad range shift
+  ERROR at ots/src/ots.cc:199 (bool<unnamed>::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t))
+  Failed to sanitise file!
+  $
+
+------------------------------------------------------------------------------
+idempotent - TTF/OTF font transcoder (for OTS debugging)
+
+Description:
+  idempotent is a program which validates and transcodes a truetype or opentype
+  font file using OTS. This tool transcodes the original font twice and then
+  verifies that the two transcoded fonts are identical:
+
+      t1 = ValidateAndTranscode(original_font);
+      if (validation_error)
+        PrintErrorAndExit;
+      t2 = ValidateAndTranscode(t1);
+      if (validation_error)
+        PrintErrorAndExit;
+      if (t1 != t2)
+        PrintErrorAndExit;
+
+  This tool is basically for OTS developers.
+
+Usage:
+  $ ./idempotent ttf_or_otf_file
+
+Example:
+  $ ./idempotent sample.otf
+  $ ./idempotent malformed.ttf
+  WARNING at ots/src/ots.cc:158: bad range shift
+  ERROR at ots/src/ots.cc:199 (bool<unnamed>::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t))
+  Failed to sanitise file!
+  $
+
+------------------------------------------------------------------------------
+validator_checker - font validation checker
+
+Description:
+  validator_checker is a program which is intended to validate malformed fonts.
+  If the program detects that the font is invalid, it prints "OK" and returns
+  with 0 (success). If it coulndn't detect any errors, the program then opens
+  the transcoded font and renders some characters using FreeType2:
+
+      transcoded_font = ValidateAndTranscode(malicious_font);
+      if (validation_error)
+        Print("OK");
+      OpenAndRenderSomeCharacters(transcoded_font);  # may cause SIGSEGV
+      Print("OK");
+
+  If SEGV doesn't raise inside FreeType2 library, the program prints "OK" and
+  returns with 0 as well. You should run this tool under the catchsegv or
+  valgrind command so that you can easily verify that all transformed fonts
+  don't crash the library (see the example below).
+
+Usage:
+  $ catchsegv ./validator_checker malicous_ttf_or_otf_file
+
+Example:
+  $ for f in malformed/*.ttf ; do catchsegv ./validator-checker "$f" ; done
+  OK: the malicious font was filtered: malformed/1.ttf
+  OK: the malicious font was filtered: malformed/2.ttf
+  OK: FreeType2 didn't crash: malformed/3.ttf
+  OK: the malicious font was filtered: malformed/4.ttf
+  $
+
+------------------------------------------------------------------------------
+perf - performance checker
+
+Description:
+  perf is a program which validates and transcodes a truetype or opentype font
+  file N times using OTS, then prints the elapsed time:
+
+      for (N times)
+        ValidateAndTranscode(original_font);
+      Print(elapsed_time_in_us / N);
+
+Usage:
+  $ ./perf ttf_or_otf_file
+
+Example:
+  $ ./perf sample.ttf 
+  903 [us] sample.ttf (139332 bytes, 154 [byte/us])
+  $ ./perf sample-bold.otf
+  291 [us] sample-bold.otf (150652 bytes, 517 [byte/us])
+
+------------------------------------------------------------------------------
+side-by-side - font quality checker
+
+Description:
+  side-by-side is a program which renders some characters (ASCII, Latin-1, CJK)
+  using both original font and transcoded font and checks that the two rendering
+  results are exactly equal.
+
+  The following Unicode characters are used during the test:
+    0x0020 - 0x007E  // Basic Latin
+    0x00A1 - 0x017F  // Latin-1
+    0x1100 - 0x11FF  // Hangul
+    0x3040 - 0x309F  // Japanese HIRAGANA letters
+    0x3130 - 0x318F  // Hangul
+    0x4E00 - 0x4F00  // CJK Kanji/Hanja
+    0xAC00 - 0xAD00  // Hangul
+
+  This tool uses FreeType2 library.
+  Note: This tool doesn't check kerning (GPOS/kern) nor font substitution
+  (GSUB). These should be tested in Layout tests if necessary.
+
+Usage:
+  $ ./side-by-side ttf_or_otf_file
+
+Example:
+  $ ./side-by-side linux/kochi-gothic.ttf  # no problem
+  $ ./side-by-side free/kredit1.ttf        # this is known issue of OTS.
+  bitmap metrics doesn't match! (14, 57), (37, 45)
+  EXPECTED:
+                
+    +#######*.  
+   +##########+ 
+  .###+.#.   .#.
+  *#*   #     #*
+  ##.   #     ##
+  ##    #     ##
+  ##    #     ##
+  ##    #.    ##
+  ##.   #.   .##
+  ##.   #.   .##
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   *#*
+  *#+   ++   *#+
+  +#*   +*   *#+
+  +#*   +*   *#+
+  +#*   +*   *#+
+  +#*   +*   ##.
+  +#*   +*   ##.
+  .##   .#   ## 
+  .##   .#   ## 
+  .##   .#   ## 
+   ##    #   ## 
+   ##    #   ## 
+   ##    #  .## 
+   ##    #  .## 
+   ##   .#+ +#* 
+   ##  +######* 
+   ##.+#######* 
+   *##########* 
+   +##########+ 
+    #########*  
+    .########   
+      +####+    
+                
+                
+                
+                
+                
+                
+    .*######*   
+   +##*.*#####  
+  .##+.#+    +# 
+  *#* ##      #+
+  ##*###      ##
+  ######      ##
+  ##+.##+    +##
+  ##  ##########
+  ##  +#########
+  ##   +########
+  *#. .########*
+  .#* #########.
+   +##########+ 
+    +*######*   
+  
+  ACTUAL:
+
+    .*##*+                             
+   +##+.##*.                           
+  .#* .##.+#*                          
+  *#  ###   *#+                        
+  #*######+  .*#+                      
+  #########*.  +#*.                    
+  ###########*   +#*                   
+  *############+   *#+                 
+  +##############.  .##.               
+   *##############*   +#*              
+    +###############+   *#+            
+      *###############+  .*#+          
+       .###############*.  +#*.        
+         +###############*   +#*       
+           *###############+   *#+     
+            .*###############+  .*#+   
+              +###############*.  +#*  
+                +###############*   ** 
+                  *###############+  #+
+                   .###############* ##
+                     +############+  ##
+                       +########*   .##
+                        .######.   +###
+                       +#####+   .*#..#
+                     +#####*    *###..#
+                    *#####.   +#######*
+                  +#####+   .*########.
+                +#####*    +#########* 
+               *#####.   +##########+  
+             +#####+    *#########*.   
+           .#####*    +##########+     
+          *#####.   +##########*       
+        +#####+    *#########*.        
+      .#####*    +##########+          
+     *#####+   +##########*            
+   .#*++#+    *#########*.             
+  .#+  ##   +##########+               
+  ****###+.##########*                 
+  ##################.                  
+  ###+  *#########+                    
+  ##   +########*                      
+  *#+ *########.                       
+   ##.#######+                         
+   +#######*                           
+     *###*.                            
+  
+  
+  Glyph mismatch! (file: free/kredit1.ttf, U+0021, 100pt)!
+  $
+------------------------------------------------------------------------------
diff --git a/third_party/ots/test/cff_type2_charstring_test.cc b/third_party/ots/test/cff_type2_charstring_test.cc
new file mode 100644
index 0000000..21139aa
--- /dev/null
+++ b/third_party/ots/test/cff_type2_charstring_test.cc
@@ -0,0 +1,1584 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cff_type2_charstring.h"
+
+#include <gtest/gtest.h>
+
+#include <climits>
+#include <vector>
+
+#include "cff.h"
+
+// Returns a biased number for callsubr and callgsubr operators.
+#define GET_SUBR_NUMBER(n) ((n) - 107)
+#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
+
+namespace {
+
+// A constant which is used in AddSubr function below.
+const int kOpPrefix = INT_MAX;
+
+// Encodes an operator |op| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds.
+bool EncodeOperator(int op, std::vector<uint8_t> *out_bytes) {
+  if (op < 0) {
+    return false;
+  }
+  if (op <= 11) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 12) {
+    return false;
+  }
+  if (op <= 27) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 28) {
+    return false;
+  }
+  if (op <= 31) {
+    out_bytes->push_back(op);
+    return true;
+  }
+
+  const uint8_t upper = (op & 0xff00u) >> 8;
+  const uint8_t lower = op & 0xffu;
+  if (upper != 12) {
+    return false;
+  }
+  out_bytes->push_back(upper);
+  out_bytes->push_back(lower);
+  return true;
+}
+
+// Encodes a number |num| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds. The function does not support 16.16
+// Fixed number.
+bool EncodeNumber(int num, std::vector<uint8_t> *out_bytes) {
+  if (num >= -107 && num <= 107) {
+    out_bytes->push_back(num + 139);
+    return true;
+  }
+  if (num >= 108 && num <= 1131) {
+    const uint8_t v = ((num - 108) / 256) + 247;
+    const uint8_t w = (num - 108) % 256;
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  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) {
+    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;
+}
+
+// Adds a subroutine |subr| to |out_buffer| and |out_subr|. The contents of the
+// subroutine is copied to |out_buffer|, and then the position of the subroutine
+// in |out_buffer| is written to |out_subr|. Returns true on success.
+bool AddSubr(const int *subr, size_t subr_len,
+             std::vector<uint8_t>* out_buffer, ots::CFFIndex *out_subr) {
+  size_t pre_offset = out_buffer->size();
+  for (size_t i = 0; i < subr_len; ++i) {
+    if (subr[i] != kOpPrefix) {
+      if (!EncodeNumber(subr[i], out_buffer)) {
+        return false;
+      }
+    } else {
+      if (i + 1 == subr_len) {
+        return false;
+      }
+      ++i;
+      if (!EncodeOperator(subr[i], out_buffer)) {
+        return false;
+      }
+    }
+  }
+
+  ++(out_subr->count);
+  out_subr->off_size = 1;
+  if (out_subr->offsets.empty()) {
+    out_subr->offsets.push_back(pre_offset);
+  }
+  out_subr->offsets.push_back(out_buffer->size());
+  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 global_subrs_index;
+  ots::CFFIndex local_subrs_index;
+
+  if (char_string) {
+    if (!AddSubr(char_string, char_string_len,
+                 &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)) {
+      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::OpenTypeFile* file = new ots::OpenTypeFile();
+  file->context = new ots::OTSContext();
+  return ots::ValidateType2CharStringIndex(file,
+                                           char_strings_index,
+                                           global_subrs_index,
+                                           fd_select,
+                                           local_subrs_per_font,
+                                           &local_subrs_index,
+                                           &ots_buffer);
+}
+
+// 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);
+}
+
+}  // namespace
+
+TEST(ValidateTest, TestRMoveTo) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRMoveTo,  // invalid number of args
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      1, 2, kOpPrefix, ots::kHMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kVMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHLineTo,
+      1, 2, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVLineTo,
+      1, 2, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRRCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kRRCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kRRCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kHVCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kHVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRCurveLine) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+      kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRCurveLine,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineCurve) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRLineCurve,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kVHCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kVHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEndChar) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestHStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHintMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kHintMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kHintMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kHintMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kHintMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCntrMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kCntrMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kCntrMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kCntrMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kCntrMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAbs) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kAbs,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kAbs,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAdd) {
+  {
+    const int char_string[] = {
+      0, 1, kOpPrefix, ots::kAdd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAdd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSub) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kSub,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kSub,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDiv) {
+  // TODO(yusukes): Test div-by-zero.
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kDiv,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kDiv,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNeg) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kNeg,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNeg,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRandom) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kRandom,  // OTS rejects the operator.
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestMul) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kMul,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kMul,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSqrt) {
+  // TODO(yusukes): Test negative numbers.
+  {
+    const int char_string[] = {
+      4, kOpPrefix, ots::kSqrt,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kSqrt,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDrop) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDrop,
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDrop,  // invalid
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestExch) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kExch,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kExch,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIndex) {
+  {
+    const int char_string[] = {
+      1, 2, 3, -1, kOpPrefix, ots::kIndex,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRoll) {
+  {
+    const int char_string[] = {
+      1, 2, 2, 1, kOpPrefix, ots::kRoll,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDup) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDup,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestPut) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kPut,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestGet) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kGet,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAnd) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kAnd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAnd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestOr) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kOr,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kOr,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNot) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kNot,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNot,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEq) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kEq,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kEq,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIfElse) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kIfElse,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, kOpPrefix, ots::kIfElse,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubrWithComputedValues) {
+  {
+    // OTS does not allow to call(g)subr with a subroutine number which is
+    // not a immediate value for safety.
+    const int char_string[] = {
+      0, 0, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestInfiniteLoop) {
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  // mutual recursion which doesn't stop.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+}
+
+TEST(ValidateTest, TestStackOverflow) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9,  // overflow
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDeprecatedOperators) {
+  {
+    const int char_string[] = {
+      kOpPrefix, 16,  // 'blend'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 8,  // 'store'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 13,  // 'load'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestUnterminatedCharString) {
+  // No endchar operator.
+  {
+    const int char_string[] = {
+      123,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456, kOpPrefix, ots::kReturn,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
diff --git a/third_party/ots/test/file-stream.h b/third_party/ots/test/file-stream.h
new file mode 100644
index 0000000..44dd4a1
--- /dev/null
+++ b/third_party/ots/test/file-stream.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_FILE_STREAM_H_
+#define OTS_FILE_STREAM_H_
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+// An OTSStream implementation for testing.
+class FILEStream : public OTSStream {
+ public:
+  explicit FILEStream(FILE *stream)
+      : file_(stream), position_(0) {
+  }
+
+  ~FILEStream() {
+    if (file_)
+      fclose(file_);
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    if (!file_ || ::fwrite(data, length, 1, file_) == 1) {
+      position_ += length;
+      return true;
+    }
+    return false;
+  }
+
+  bool Seek(off_t position) {
+#if defined(_WIN32)
+    if (!file_ || !::_fseeki64(file_, position, SEEK_SET)) {
+      position_ = position;
+      return true;
+    }
+#else
+    if (!file_ || !::fseeko(file_, position, SEEK_SET)) {
+      position_ = position;
+      return true;
+    }
+#endif  // defined(_WIN32)
+    return false;
+  }
+
+  off_t Tell() const {
+    return position_;
+  }
+
+ private:
+  FILE * const file_;
+  off_t position_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_FILE_STREAM_H_
diff --git a/third_party/ots/test/idempotent.cc b/third_party/ots/test/idempotent.cc
new file mode 100644
index 0000000..ec50ab4
--- /dev/null
+++ b/third_party/ots/test/idempotent.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(_WIN32)
+#ifdef __linux__
+// Linux
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#else
+// Mac OS X
+#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
+#endif  // __linux__
+#include <unistd.h>
+#else
+// Windows
+#include <io.h>
+#include <Windows.h>
+#endif  // !defiend(_WIN32)
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
+  return 1;
+}
+
+bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size);
+bool DumpResults(const uint8_t *result1, const size_t len1,
+                 const uint8_t *result2, const size_t len2);
+
+#if defined(_WIN32)
+#define ADDITIONAL_OPEN_FLAGS O_BINARY
+#else
+#define ADDITIONAL_OPEN_FLAGS 0
+#endif
+
+bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size) {
+  const int fd = open(file_name, O_RDONLY | ADDITIONAL_OPEN_FLAGS);
+  if (fd < 0) {
+    return false;
+  }
+
+  struct stat st;
+  fstat(fd, &st);
+
+  *file_size = st.st_size;
+  *data = new uint8_t[st.st_size];
+  if (read(fd, *data, st.st_size) != st.st_size) {
+    close(fd);
+    return false;
+  }
+  close(fd);
+  return true;
+}
+
+bool DumpResults(const uint8_t *result1, const size_t len1,
+                 const uint8_t *result2, const size_t len2) {
+  int fd1 = open("out1.ttf",
+                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
+  int fd2 = open("out2.ttf",
+                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
+  if (fd1 < 0 || fd2 < 0) {
+    perror("opening output file");
+    return false;
+  }
+  if ((write(fd1, result1, len1) < 0) ||
+      (write(fd2, result2, len2) < 0)) {
+    perror("writing output file");
+    close(fd1);
+    close(fd2);
+    return false;
+  }
+  close(fd1);
+  close(fd2);
+  return true;
+}
+
+// Platform specific implementations.
+bool VerifyTranscodedFont(uint8_t *result, const size_t len);
+
+#if defined(__linux__)
+// Linux
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  FT_Library library;
+  FT_Error error = ::FT_Init_FreeType(&library);
+  if (error) {
+    return false;
+  }
+  FT_Face dummy;
+  error = ::FT_New_Memory_Face(library, result, len, 0, &dummy);
+  if (error) {
+    return false;
+  }
+  ::FT_Done_Face(dummy);
+  return true;
+}
+
+#elif defined(__APPLE_CC__)
+// Mac
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  CFDataRef data = CFDataCreate(0, result, len);
+  if (!data) {
+    return false;
+  }
+
+  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
+  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
+  CGDataProviderRelease(dataProvider);
+  CFRelease(data);
+  if (!cgFontRef) {
+    return false;
+  }
+
+  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
+  CGFontRelease(cgFontRef);
+  if (!numGlyphs) {
+    return false;
+  }
+  return true;
+}
+
+#elif defined(_WIN32)
+// Windows
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  DWORD num_fonts = 0;
+  HANDLE handle = AddFontMemResourceEx(result, len, 0, &num_fonts);
+  if (!handle) {
+    return false;
+  }
+  RemoveFontMemResourceEx(handle);
+  return true;
+}
+
+#else
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  std::fprintf(stderr, "Can't verify the transcoded font on this platform.\n");
+  return false;
+}
+
+#endif
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) return Usage(argv[0]);
+
+  size_t file_size = 0;
+  uint8_t *data = 0;
+  if (!ReadFile(argv[1], &data, &file_size)) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+
+  // A transcoded font is usually smaller than an original font.
+  // However, it can be slightly bigger than the original one due to
+  // name table replacement and/or padding for glyf table.
+  //
+  // However, a WOFF font gets decompressed and so can be *much* larger than
+  // the original.
+  uint8_t *result = new uint8_t[file_size * 8];
+  ots::MemoryStream output(result, file_size * 8);
+
+  ots::OTSContext context;
+
+  bool r = context.Process(&output, data, file_size);
+  if (!r) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+    return 1;
+  }
+  const size_t result_len = output.Tell();
+  delete[] data;
+
+  uint8_t *result2 = new uint8_t[result_len];
+  ots::MemoryStream output2(result2, result_len);
+  r = context.Process(&output2, result, result_len);
+  if (!r) {
+    std::fprintf(stderr, "Failed to sanitise previous output!\n");
+    return 1;
+  }
+  const size_t result2_len = output2.Tell();
+
+  bool dump_results = false;
+  if (result2_len != result_len) {
+    std::fprintf(stderr, "Outputs differ in length\n");
+    dump_results = true;
+  } else if (std::memcmp(result2, result, result_len)) {
+    std::fprintf(stderr, "Outputs differ in content\n");
+    dump_results = true;
+  }
+
+  if (dump_results) {
+    std::fprintf(stderr, "Dumping results to out1.tff and out2.tff\n");
+    if (!DumpResults(result, result_len, result2, result2_len)) {
+      std::fprintf(stderr, "Failed to dump output files.\n");
+      return 1;
+    }
+  }
+
+  // Verify that the transcoded font can be opened by the font renderer for
+  // Linux (FreeType2), Mac OS X, or Windows.
+  if (!VerifyTranscodedFont(result, result_len)) {
+    std::fprintf(stderr, "Failed to verify the transcoded font\n");
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/third_party/ots/test/layout_common_table_test.cc b/third_party/ots/test/layout_common_table_test.cc
new file mode 100644
index 0000000..5e9a03b
--- /dev/null
+++ b/third_party/ots/test/layout_common_table_test.cc
@@ -0,0 +1,761 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "layout.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+const uint32_t kFakeTag = 0x00000000;
+const size_t kScriptRecordSize = 6;
+const size_t kLangSysRecordSize = 6;
+
+bool BuildFakeScriptListTable(ots::OTSStream *out, const uint16_t script_count,
+                              const uint16_t langsys_count,
+                              const uint16_t feature_count) {
+  if (!out->WriteU16(script_count)) {
+    return false;
+  }
+  const off_t script_record_end = out->Tell() +
+      kScriptRecordSize * script_count;
+  const size_t script_table_size = 4 + kLangSysRecordSize * langsys_count;
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!out->WriteU32(kFakeTag) ||
+        !out->WriteU16(script_record_end + i * script_table_size)) {
+      return false;
+    }
+  }
+
+  // Offsets to LangSys tables are measured from the beginning of each
+  // script table.
+  const off_t langsys_record_end = 4 + kLangSysRecordSize * langsys_count;
+  const size_t langsys_table_size = 6 + 2 * feature_count;
+  // Write Fake Script tables.
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(langsys_count)) {
+      return false;
+    }
+    for (unsigned j = 0; j < langsys_count; ++j) {
+      if (!out->WriteU32(kFakeTag) ||
+          !out->WriteU16(langsys_record_end + j * langsys_table_size)) {
+        return false;
+      }
+    }
+  }
+
+  // Write Fake LangSys tables.
+  for (unsigned i = 0; i < langsys_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(0xFFFF) ||
+        !out->WriteU16(feature_count)) {
+      return false;
+    }
+    for (unsigned j = 0; j < feature_count; ++j) {
+      if (!out->WriteU16(j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+const size_t kFeatureRecordSize = 6;
+
+bool BuildFakeFeatureListTable(ots::OTSStream *out,
+                               const uint16_t feature_count,
+                               const uint16_t lookup_count) {
+  if (!out->WriteU16(feature_count)) {
+    return false;
+  }
+  const off_t feature_record_end = out->Tell() +
+      kFeatureRecordSize * feature_count;
+  const size_t feature_table_size = 4 + 2 * lookup_count;
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!out->WriteU32(kFakeTag) ||
+        !out->WriteU16(feature_record_end + i * feature_table_size)) {
+      return false;
+    }
+  }
+
+  // Write FeatureTable
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(lookup_count)) {
+      return false;
+    }
+    for (uint16_t j = 0; j < lookup_count; ++j) {
+      if (!out->WriteU16(j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool BuildFakeLookupListTable(ots::OTSStream *out, const uint16_t lookup_count,
+                              const uint16_t subtable_count) {
+  if (!out->WriteU16(lookup_count)) {
+    return false;
+  }
+  const off_t base_offset_lookup = out->Tell();
+  if (!out->Pad(2 * lookup_count)) {
+    return false;
+  }
+
+  std::vector<off_t> offsets_lookup(lookup_count, 0);
+  for (uint16_t i = 0; i < lookup_count; ++i) {
+    offsets_lookup[i] = out->Tell();
+    if (!out->WriteU16(i + 1) ||
+        !out->WriteU16(0) ||
+        !out->WriteU16(subtable_count) ||
+        !out->Pad(2 * subtable_count) ||
+        !out->WriteU16(0)) {
+      return false;
+    }
+  }
+
+  const off_t offset_lookup_table_end = out->Tell();
+  // Allocate 256 bytes for each subtable.
+  if (!out->Pad(256 * lookup_count * subtable_count)) {
+    return false;
+  }
+
+  if (!out->Seek(base_offset_lookup)) {
+    return false;
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!out->WriteU16(offsets_lookup[i])) {
+      return false;
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!out->Seek(offsets_lookup[i] + 6)) {
+      return false;
+    }
+    for (unsigned j = 0; j < subtable_count; ++j) {
+      if (!out->WriteU16(offset_lookup_table_end +
+                         256*i*subtable_count + 256*j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool BuildFakeCoverageFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+  if (!out->WriteU16(1) || !out->WriteU16(glyph_count)) {
+    return false;
+  }
+  for (uint16_t glyph_id = 1; glyph_id <= glyph_count; ++glyph_id) {
+    if (!out->WriteU16(glyph_id)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool BuildFakeCoverageFormat2(ots::OTSStream *out, const uint16_t range_count) {
+  if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+    return false;
+  }
+  uint16_t glyph_id = 1;
+  uint16_t start_coverage_index = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    // Write consecutive ranges in which each range consists of two glyph id.
+    if (!out->WriteU16(glyph_id) ||
+        !out->WriteU16(glyph_id + 1) ||
+        !out->WriteU16(start_coverage_index)) {
+      return false;
+    }
+    glyph_id += 2;
+    start_coverage_index += 2;
+  }
+  return true;
+}
+
+bool BuildFakeClassDefFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+  if (!out->WriteU16(1) ||
+      !out->WriteU16(1) ||
+      !out->WriteU16(glyph_count)) {
+    return false;
+  }
+  for (uint16_t class_value = 1; class_value <= glyph_count; ++class_value) {
+    if (!out->WriteU16(class_value)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool BuildFakeClassDefFormat2(ots::OTSStream *out, const uint16_t range_count) {
+  if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+    return false;
+  }
+  uint16_t glyph_id = 1;
+  for (uint16_t class_value = 1; class_value <= range_count; ++class_value) {
+    // Write consecutive ranges in which each range consists of one glyph id.
+    if (!out->WriteU16(glyph_id) ||
+        !out->WriteU16(glyph_id + 1) ||
+        !out->WriteU16(class_value)) {
+      return false;
+    }
+    glyph_id += 2;
+  }
+  return true;
+}
+
+bool BuildFakeDeviceTable(ots::OTSStream *out, const uint16_t start_size,
+                          const uint16_t end_size, const uint16_t format) {
+  if (!out->WriteU16(start_size) ||
+      !out->WriteU16(end_size) ||
+      !out->WriteU16(format)) {
+    return false;
+  }
+
+  const unsigned num_values = std::abs(end_size - start_size) + 1;
+  const unsigned num_bits = (1 << format) * num_values;
+  const unsigned num_units = (num_bits - 1) / 16 + 1;
+  if (!out->Pad(num_units * 2)) {
+    return false;
+  }
+  return true;
+}
+
+class TestStream : public ots::MemoryStream {
+ public:
+  TestStream()
+      : ots::MemoryStream(data_, sizeof(data_)), size_(0) {
+    std::memset(reinterpret_cast<char*>(data_), 0, sizeof(data_));
+  }
+
+  uint8_t* data() { return data_; }
+  size_t size() const { return size_; }
+
+  virtual bool WriteRaw(const void *d, size_t length) {
+    if (Tell() + length > size_) {
+      size_ = Tell() + length;
+    }
+    return ots::MemoryStream::WriteRaw(d, length);
+  }
+
+ private:
+  size_t size_;
+  uint8_t data_[4096];
+};
+
+class TableTest : public ::testing::Test {
+ protected:
+
+  virtual void SetUp() {
+    file = new ots::OpenTypeFile();
+    file->context = new ots::OTSContext();
+  }
+
+  TestStream out;
+  ots::OpenTypeFile *file;
+};
+
+class ScriptListTableTest : public TableTest { };
+class DeviceTableTest : public TableTest { };
+class CoverageTableTest : public TableTest { };
+class CoverageFormat1Test : public TableTest { };
+class CoverageFormat2Test : public TableTest { };
+class ClassDefTableTest : public TableTest { };
+class ClassDefFormat1Test : public TableTest { };
+class ClassDefFormat2Test : public TableTest { };
+class LookupSubtableParserTest : public TableTest { };
+
+class FeatureListTableTest : public TableTest {
+ protected:
+
+  virtual void SetUp() {
+    num_features = 0;
+  }
+
+  uint16_t num_features;
+};
+
+bool fakeTypeParserReturnsTrue(const ots::OpenTypeFile*, const uint8_t *,
+                               const size_t) {
+  return true;
+}
+
+bool fakeTypeParserReturnsFalse(const ots::OpenTypeFile*, const uint8_t *,
+                                const size_t) {
+  return false;
+}
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnTrue[] = {
+  {1, fakeTypeParserReturnsTrue},
+  {2, fakeTypeParserReturnsTrue},
+  {3, fakeTypeParserReturnsTrue},
+  {4, fakeTypeParserReturnsTrue},
+  {5, fakeTypeParserReturnsTrue}
+};
+
+// Fake lookup subtable parser which always returns true.
+const ots::LookupSubtableParser FakeLookupParserReturnsTrue = {
+  5, 5, TypeParsersReturnTrue,
+};
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnFalse[] = {
+  {1, fakeTypeParserReturnsFalse}
+};
+
+// Fake lookup subtable parser which always returns false.
+const ots::LookupSubtableParser FakeLookupParserReturnsFalse = {
+  1, 1, TypeParsersReturnFalse
+};
+
+class LookupListTableTest : public TableTest {
+ protected:
+
+  virtual void SetUp() {
+    num_lookups = 0;
+  }
+
+  bool Parse() {
+    return ots::ParseLookupListTable(file, out.data(), out.size(),
+                                     &FakeLookupParserReturnsTrue,
+                                     &num_lookups);
+  }
+
+  uint16_t num_lookups;
+};
+
+}  // namespace
+
+TEST_F(ScriptListTableTest, TestSuccess) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  EXPECT_TRUE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadScriptCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large script count.
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetUnderflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to ScriptRecord[0].
+  out.Seek(6);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetOverflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to ScriptRecord[0].
+  out.Seek(6);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadLangSysCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large langsys count.
+  out.Seek(10);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetUnderflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to LangSysRecord[0].
+  out.Seek(16);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetOverflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to LangSysRecord[0].
+  out.Seek(16);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadReqFeatureIndex) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature index to ReqFeatureIndex of LangSysTable[0].
+  out.Seek(20);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature count to LangSysTable[0].
+  out.Seek(22);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureIndex) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature index to ReatureIndex[0] of LangSysTable[0].
+  out.Seek(24);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(FeatureListTableTest, TestSuccess) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  EXPECT_TRUE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                         &num_features));
+  EXPECT_EQ(num_features, 1);
+}
+
+TEST_F(FeatureListTableTest, TestSuccess2) {
+  BuildFakeFeatureListTable(&out, 5, 1);
+  EXPECT_TRUE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                         &num_features));
+  EXPECT_EQ(num_features, 5);
+}
+
+TEST_F(FeatureListTableTest, TestBadFeatureCount) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set too large feature count.
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureUnderflow) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set bad offset to FeatureRecord[0].
+  out.Seek(6);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureOverflow) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set bad offset to FeatureRecord[0].
+  out.Seek(6);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestBadLookupCount) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set too large lookup count to FeatureTable[0].
+  out.Seek(10);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(LookupListTableTest, TestSuccess) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  EXPECT_TRUE(Parse());
+  EXPECT_EQ(num_lookups, 1);
+}
+
+TEST_F(LookupListTableTest, TestSuccess2) {
+  BuildFakeLookupListTable(&out, 5, 1);
+  EXPECT_TRUE(Parse());
+  EXPECT_EQ(num_lookups, 5);
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableUnderflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to Lookup[0].
+  out.Seek(2);
+  out.WriteU16(0);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableOverflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to Lookup[0].
+  out.Seek(2);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableUnderflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to SubTable[0] of LookupTable[0].
+  out.Seek(10);
+  out.WriteU16(0);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableOverflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to SubTable[0] of LookupTable[0].
+  out.Seek(10);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupCount) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large lookup count of LookupTable[0].
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupType) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large lookup type of LookupTable[0].
+  out.Seek(4);
+  out.WriteU16(6);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupFlag) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set IgnoreBaseGlyphs(0x0002) to the lookup flag of LookupTable[0].
+  out.Seek(6);
+  out.WriteU16(0x0002);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadSubtableCount) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large sutable count of LookupTable[0].
+  out.Seek(8);
+  out.WriteU16(2);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat1) {
+  BuildFakeCoverageFormat1(&out, 1);
+  EXPECT_TRUE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat2) {
+  BuildFakeCoverageFormat2(&out, 1);
+  EXPECT_TRUE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestBadFormat) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set bad format.
+  out.Seek(0);
+  out.WriteU16(3);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphCount) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set too large glyph count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphId) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set too large glyph id.
+  out.Seek(4);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRangeCount) {
+  BuildFakeCoverageFormat2(&out, 1);
+  // Set too large range count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRange) {
+  BuildFakeCoverageFormat2(&out, 1);
+  // Set reverse order glyph id to start/end fields.
+  out.Seek(4);
+  out.WriteU16(2);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap) {
+  BuildFakeCoverageFormat2(&out, 2);
+  // Set overlapping glyph id to an end field.
+  out.Seek(12);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 2));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap2) {
+  BuildFakeCoverageFormat2(&out, 2);
+  // Set overlapping range.
+  out.Seek(10);
+  out.WriteU16(1);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 2));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat1) {
+  BuildFakeClassDefFormat1(&out, 1);
+  EXPECT_TRUE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat2) {
+  BuildFakeClassDefFormat2(&out, 1);
+  EXPECT_TRUE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestBadFormat) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set bad format.
+  out.Seek(0);
+  out.WriteU16(3);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadStartGlyph) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large start glyph id.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadGlyphCount) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large glyph count.
+  out.Seek(4);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadClassValue) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large class value.
+  out.Seek(6);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestBadRangeCount) {
+  BuildFakeClassDefFormat2(&out, 1);
+  // Set too large range count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap) {
+  BuildFakeClassDefFormat2(&out, 2);
+  // Set overlapping glyph id to an end field.
+  out.Seek(12);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap2) {
+  BuildFakeClassDefFormat2(&out, 2);
+  // Set overlapping range.
+  out.Seek(10);
+  out.WriteU16(1);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success) {
+  BuildFakeDeviceTable(&out, 1, 8, 1);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success2) {
+  BuildFakeDeviceTable(&out, 1, 9, 1);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 1);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 1);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success) {
+  BuildFakeDeviceTable(&out, 1, 1, 2);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success2) {
+  BuildFakeDeviceTable(&out, 1, 8, 2);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 2);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 2);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success) {
+  BuildFakeDeviceTable(&out, 1, 1, 3);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success2) {
+  BuildFakeDeviceTable(&out, 1, 8, 3);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 3);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 3);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(LookupSubtableParserTest, TestSuccess) {
+  {
+    EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 1));
+  }
+  {
+    EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 5));
+  }
+}
+
+TEST_F(LookupSubtableParserTest, TestFail) {
+  {
+    // Pass bad lookup type which less than the smallest type.
+    EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 0));
+  }
+  {
+    // Pass bad lookup type which greater than the maximum type.
+    EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 6));
+  }
+  {
+    // Check the type parser failure.
+    EXPECT_FALSE(FakeLookupParserReturnsFalse.Parse(file, 0, 0, 1));
+  }
+}
diff --git a/third_party/ots/test/ot-sanitise.cc b/third_party/ots/test/ot-sanitise.cc
new file mode 100644
index 0000000..2d4526a
--- /dev/null
+++ b/third_party/ots/test/ot-sanitise.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A very simple driver program while sanitises the file given as argv[1] and
+// writes the sanitised version to stdout.
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(_WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif  // defined(_WIN32)
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+#include "file-stream.h"
+#include "opentype-sanitiser.h"
+
+#if defined(_WIN32)
+#define ADDITIONAL_OPEN_FLAGS O_BINARY
+#else
+#define ADDITIONAL_OPEN_FLAGS 0
+#endif
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s ttf_file [dest_ttf_file]\n", argv0);
+  return 1;
+}
+
+class Context: public ots::OTSContext {
+ public:
+  virtual void Message(int level, const char *format, ...) {
+    va_list va;
+
+    if (level == 0)
+      std::fprintf(stderr, "ERROR: ");
+    else
+      std::fprintf(stderr, "WARNING: ");
+    va_start(va, format);
+    std::vfprintf(stderr, format, va);
+    std::fprintf(stderr, "\n");
+    va_end(va);
+  }
+
+  virtual ots::TableAction GetTableAction(uint32_t tag) {
+#define TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+    switch (tag) {
+      case TAG('S','i','l','f'):
+      case TAG('S','i','l','l'):
+      case TAG('G','l','o','c'):
+      case TAG('G','l','a','t'):
+      case TAG('F','e','a','t'):
+        return ots::TABLE_ACTION_PASSTHRU;
+      default:
+        return ots::TABLE_ACTION_DEFAULT;
+    }
+#undef TAG
+  }
+};
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc < 2 || argc > 3) return Usage(argv[0]);
+
+  const int fd = ::open(argv[1], O_RDONLY | ADDITIONAL_OPEN_FLAGS);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+
+  uint8_t *data = new uint8_t[st.st_size];
+  if (::read(fd, data, st.st_size) != st.st_size) {
+    ::perror("read");
+    return 1;
+  }
+  ::close(fd);
+
+  Context context;
+
+  FILE* out = NULL;
+  if (argc == 3)
+    out = fopen(argv[2], "wb");
+
+  ots::FILEStream output(out);
+  const bool result = context.Process(&output, data, st.st_size);
+
+  if (!result) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+  }
+  return !result;
+}
diff --git a/third_party/ots/test/perf.cc b/third_party/ots/test/perf.cc
new file mode 100644
index 0000000..de7ee41
--- /dev/null
+++ b/third_party/ots/test/perf.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
+  return 1;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) return Usage(argv[0]);
+
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+
+  uint8_t *data = new uint8_t[st.st_size];
+  if (::read(fd, data, st.st_size) != st.st_size) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+
+  // A transcoded font is usually smaller than an original font.
+  // However, it can be slightly bigger than the original one due to
+  // name table replacement and/or padding for glyf table.
+  static const size_t kPadLen = 20 * 1024;
+  uint8_t *result = new uint8_t[st.st_size + kPadLen];
+
+  int num_repeat = 250;
+  if (st.st_size < 1024 * 1024) {
+    num_repeat = 2500;
+  }
+  if (st.st_size < 1024 * 100) {
+    num_repeat = 5000;
+  }
+
+  struct timeval start, end, elapsed;
+  ::gettimeofday(&start, 0);
+  for (int i = 0; i < num_repeat; ++i) {
+    ots::MemoryStream output(result, st.st_size + kPadLen);
+    ots::OTSContext context;
+    bool r = context.Process(&output, data, st.st_size);
+    if (!r) {
+      std::fprintf(stderr, "Failed to sanitise file!\n");
+      return 1;
+    }
+  }
+  ::gettimeofday(&end, 0);
+  timersub(&end, &start, &elapsed);
+
+  long long unsigned us
+      = ((elapsed.tv_sec * 1000 * 1000) + elapsed.tv_usec) / num_repeat;
+  std::fprintf(stderr, "%llu [us] %s (%llu bytes, %llu [byte/us])\n",
+               us, argv[1], static_cast<long long>(st.st_size),
+               (us ? st.st_size / us : 0));
+
+  return 0;
+}
diff --git a/third_party/ots/test/side-by-side.cc b/third_party/ots/test/side-by-side.cc
new file mode 100644
index 0000000..9034a7c
--- /dev/null
+++ b/third_party/ots/test/side-by-side.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+void DumpBitmap(const FT_Bitmap *bitmap) {
+  for (int i = 0; i < bitmap->rows * bitmap->width; ++i) {
+    if (bitmap->buffer[i] > 192) {
+      std::fprintf(stderr, "#");
+    } else if (bitmap->buffer[i] > 128) {
+      std::fprintf(stderr, "*");
+    } else if (bitmap->buffer[i] > 64) {
+      std::fprintf(stderr, "+");
+    } else if (bitmap->buffer[i] > 32) {
+      std::fprintf(stderr, ".");
+    } else {
+      std::fprintf(stderr, " ");
+    }
+
+    if ((i + 1) % bitmap->width == 0) {
+      std::fprintf(stderr, "\n");
+    }
+  }
+}
+
+int CompareBitmaps(const FT_Bitmap *orig, const FT_Bitmap *trans) {
+  int ret = 0;
+
+  if (orig->width == trans->width &&
+      orig->rows == trans->rows) {
+    for (int i = 0; i < orig->rows * orig->width; ++i) {
+      if (orig->buffer[i] != trans->buffer[i]) {
+        std::fprintf(stderr, "bitmap data doesn't match!\n");
+        ret = 1;
+        break;
+      }
+    }
+  } else {
+    std::fprintf(stderr, "bitmap metrics doesn't match! (%d, %d), (%d, %d)\n",
+                 orig->width, orig->rows, trans->width, trans->rows);
+    ret = 1;
+  }
+
+  if (ret) {
+    std::fprintf(stderr, "EXPECTED:\n");
+    DumpBitmap(orig);
+    std::fprintf(stderr, "\nACTUAL:\n");
+    DumpBitmap(trans);
+    std::fprintf(stderr, "\n\n");
+  }
+
+  delete[] orig->buffer;
+  delete[] trans->buffer;
+  return ret;
+}
+
+int GetBitmap(FT_Library library, FT_Outline *outline, FT_Bitmap *bitmap) {
+  FT_BBox bbox;
+  FT_Outline_Get_CBox(outline, &bbox);
+
+  bbox.xMin &= ~63;
+  bbox.yMin &= ~63;
+  bbox.xMax = (bbox.xMax + 63) & ~63;
+  bbox.yMax = (bbox.yMax + 63) & ~63;
+  FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin);
+
+  const int w = (bbox.xMax - bbox.xMin) >> 6;
+  const int h = (bbox.yMax - bbox.yMin) >> 6;
+
+  if (w == 0 || h == 0) {
+    return -1;  // white space
+  }
+  if (w < 0 || h < 0) {
+    std::fprintf(stderr, "bad width/height\n");
+    return 1;  // error
+  }
+
+  uint8_t *buf = new uint8_t[w * h];
+  std::memset(buf, 0x0, w * h);
+
+  bitmap->width = w;
+  bitmap->rows = h;
+  bitmap->pitch = w;
+  bitmap->buffer = buf;
+  bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
+  bitmap->num_grays = 256;
+  if (FT_Outline_Get_Bitmap(library, outline, bitmap)) {
+    std::fprintf(stderr, "can't get outline\n");
+    delete[] buf;
+    return 1;  // error.
+  }
+
+  return 0;
+}
+
+int LoadChar(FT_Face face, bool use_bitmap, int pt, FT_ULong c) {
+  static const int kDpi = 72;
+
+  FT_Matrix matrix;
+  matrix.xx = matrix.yy = 1 << 16;
+  matrix.xy = matrix.yx = 0 << 16;
+
+  FT_Int32 flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
+  if (!use_bitmap) {
+    // Since the transcoder drops embedded bitmaps from the transcoded one,
+    // we have to use FT_LOAD_NO_BITMAP flag for the original face.
+    flags |= FT_LOAD_NO_BITMAP;
+  }
+
+  FT_Error error = FT_Set_Char_Size(face, pt * (1 << 6), 0, kDpi, 0);
+  if (error) {
+    std::fprintf(stderr, "Failed to set the char size!\n");
+    return 1;
+  }
+
+  FT_Set_Transform(face, &matrix, 0);
+
+  error = FT_Load_Char(face, c, flags);
+  if (error) return -1;  // no such glyf in the font.
+
+  if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
+    std::fprintf(stderr, "bad format\n");
+    return 1;
+  }
+
+  return 0;
+}
+
+int LoadCharThenCompare(FT_Library library,
+                        FT_Face orig_face, FT_Face trans_face,
+                        int pt, FT_ULong c) {
+  FT_Bitmap orig_bitmap, trans_bitmap;
+
+  // Load original bitmap.
+  int ret = LoadChar(orig_face, false, pt, c);
+  if (ret) return ret;  // 1: error, -1: no such glyph
+
+  FT_Outline *outline = &orig_face->glyph->outline;
+  ret = GetBitmap(library, outline, &orig_bitmap);
+  if (ret) return ret;  // white space?
+
+  // Load transformed bitmap.
+  ret = LoadChar(trans_face, true, pt, c);
+  if (ret == -1) {
+    std::fprintf(stderr, "the glyph is not found on the transcoded font\n");
+  }
+  if (ret) return 1;  // -1 should be treated as error.
+  outline = &trans_face->glyph->outline;
+  ret = GetBitmap(library, outline, &trans_bitmap);
+  if (ret) return ret;  // white space?
+
+  return CompareBitmaps(&orig_bitmap, &trans_bitmap);
+}
+
+int SideBySide(FT_Library library, const char *file_name,
+               uint8_t *orig_font, size_t orig_len,
+               uint8_t *trans_font, size_t trans_len) {
+  FT_Face orig_face;
+  FT_Error error
+      = FT_New_Memory_Face(library, orig_font, orig_len, 0, &orig_face);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the original font: %s!\n", file_name);
+    return 1;
+  }
+
+  FT_Face trans_face;
+  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the transcoded font: %s!\n",
+                 file_name);
+    return 1;
+  }
+
+  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
+  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);
+
+  static const int kUnicodeRanges[] = {
+    0x0020, 0x007E,  // Basic Latin (ASCII)
+    0x00A1, 0x017F,  // Latin-1
+    0x1100, 0x11FF,  // Hangul
+    0x3040, 0x309F,  // Japanese HIRAGANA letters
+    0x3130, 0x318F,  // Hangul
+    0x4E00, 0x4F00,  // CJK Kanji/Hanja
+    0xAC00, 0xAD00,  // Hangul
+  };
+  static const size_t kUnicodeRangesLen
+      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);
+
+  for (size_t i = 0; i < kPtsLen; ++i) {
+    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
+      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
+        int ret = LoadCharThenCompare(library, orig_face, trans_face,
+                                      kPts[i],
+                                      kUnicodeRanges[j] + k);
+        if (ret > 0) {
+          std::fprintf(stderr, "Glyph mismatch! (file: %s, U+%04x, %dpt)!\n",
+                       file_name, kUnicodeRanges[j] + k, kPts[i]);
+          return 1;
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) {
+    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
+    return 1;
+  }
+
+  // load the font to memory.
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+  const off_t orig_len = st.st_size;
+
+  uint8_t *orig_font = new uint8_t[orig_len];
+  if (::read(fd, orig_font, orig_len) != orig_len) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+  ::close(fd);
+
+  // check if FreeType2 can open the original font.
+  FT_Library library;
+  FT_Error error = FT_Init_FreeType(&library);
+  if (error) {
+    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
+    return 1;
+  }
+  FT_Face dummy;
+  error = FT_New_Memory_Face(library, orig_font, orig_len, 0, &dummy);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the original font with FT2! %s\n",
+                 argv[1]);
+    return 1;
+  }
+
+  // transcode the original font.
+  static const size_t kPadLen = 20 * 1024;
+  uint8_t *trans_font = new uint8_t[orig_len + kPadLen];
+  ots::MemoryStream output(trans_font, orig_len + kPadLen);
+  ots::OTSContext context;
+
+  bool result = context.Process(&output, orig_font, orig_len);
+  if (!result) {
+    std::fprintf(stderr, "Failed to sanitise file! %s\n", argv[1]);
+    return 1;
+  }
+  const size_t trans_len = output.Tell();
+
+  // perform side-by-side tests.
+  return SideBySide(library, argv[1],
+                    orig_font, orig_len,
+                    trans_font, trans_len);
+}
diff --git a/third_party/ots/test/table_dependencies_test.cc b/third_party/ots/test/table_dependencies_test.cc
new file mode 100644
index 0000000..bbaaa30
--- /dev/null
+++ b/third_party/ots/test/table_dependencies_test.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+
+#include "gsub.h"
+#include "ots.h"
+#include "ots-memory-stream.h"
+#include "vhea.h"
+#include "vmtx.h"
+
+#define SET_TABLE(name, capname) \
+  do { file.name = new ots::OpenType##capname; } while (0)
+#define SET_LAYOUT_TABLE(name, capname)                    \
+  do {                                                     \
+    if (!file.name) {                                      \
+      SET_TABLE(name, capname);                            \
+    }                                                      \
+    file.name->data = reinterpret_cast<const uint8_t*>(1); \
+    file.name->length = 1;                                 \
+  } while (0)
+#define DROP_TABLE(name) \
+  do { delete file.name; file.name = NULL; } while (0)
+#define DROP_LAYOUT_TABLE(name) \
+  do { file.name->data = NULL; file.name->length = 0; } while (0)
+
+namespace {
+
+class TableDependenciesTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    SET_LAYOUT_TABLE(gsub, GSUB);
+    SET_TABLE(vhea, VHEA);
+    SET_TABLE(vmtx, VMTX);
+  }
+
+  virtual void TearDown() {
+    DROP_TABLE(gsub);
+    DROP_TABLE(vhea);
+    DROP_TABLE(vmtx);
+  }
+  ots::OpenTypeFile file;
+};
+}  // namespace
+
+TEST_F(TableDependenciesTest, TestVhea) {
+  EXPECT_TRUE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtx) {
+  EXPECT_TRUE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaVmtx) {
+  DROP_TABLE(vmtx);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxVhea) {
+  DROP_TABLE(vhea);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaGsub) {
+  DROP_LAYOUT_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+  DROP_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxGsub) {
+  DROP_LAYOUT_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+  DROP_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+
diff --git a/third_party/ots/test/test_malicious_fonts.sh b/third_party/ots/test/test_malicious_fonts.sh
new file mode 100755
index 0000000..7a35f26
--- /dev/null
+++ b/third_party/ots/test/test_malicious_fonts.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Usage: ./test_malicious_fonts.sh [ttf_or_otf_file_name]
+
+BASE_DIR=~/malicious/
+CHECKER=./validator-checker
+
+if [ ! -x "$CHECKER" ] ; then
+  echo "$CHECKER is not found."
+  exit 1
+fi
+
+if [ $# -eq 0 ] ; then
+  # No font file is specified. Apply this script to all TT/OT files under the
+  # BASE_DIR.
+  if [ ! -d $BASE_DIR ] ; then
+    echo "$BASE_DIR does not exist."
+    exit 1
+  fi
+
+  # Recursively call this script.
+  find $BASE_DIR -type f -name '*tf' -exec "$0" {} \;
+  echo
+  exit 0
+fi
+
+if [ $# -gt 1 ] ; then
+  echo "Usage: $0 [ttf_or_otf_file_name]"
+  exit 1
+fi
+
+# Confirm that the malicious font file does not crash OTS nor OS font renderer. 
+base=`basename "$1"`
+"$CHECKER" "$1" > /dev/null 2>&1 || (echo ; echo "\nFAIL: $1 (Run $CHECKER $1 for more information.)")
+echo -n "."
diff --git a/third_party/ots/test/test_unmalicious_fonts.sh b/third_party/ots/test/test_unmalicious_fonts.sh
new file mode 100755
index 0000000..ae3a5bb
--- /dev/null
+++ b/third_party/ots/test/test_unmalicious_fonts.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Usage: ./test_unmalicious_fonts.sh [ttf_or_otf_file_name]
+
+BLACKLIST=./BLACKLIST.txt
+CHECKER=./idempotent
+
+if [ ! -r "$BLACKLIST" ] ; then
+  echo "$BLACKLIST is not found."
+  exit 1
+fi
+
+if [ ! -x "$CHECKER" ] ; then
+  echo "$CHECKER is not found."
+  exit 1
+fi
+
+if [ $# -eq 0 ] ; then
+  # No font file is specified. Apply this script to all TT/OT files under the
+  # BASE_DIR below.
+
+  # On Ubuntu Linux (>= 8.04), You can install ~1800 TrueType/OpenType fonts
+  # to /usr/share/fonts/truetype by:
+  #   % sudo apt-get install ttf-.*[^0]$
+  BASE_DIR=/usr/share/fonts/truetype/
+  if [ ! -d $BASE_DIR ] ; then
+    # Mac OS X
+    BASE_DIR="/Library/Fonts/ /System/Library/Fonts/"
+  fi
+  # TODO(yusukes): Support Cygwin.
+
+  # Recursively call this script.
+  find $BASE_DIR -type f -name '*tf' -exec "$0" {} \;
+  echo
+  exit 0
+fi
+
+if [ $# -gt 1 ] ; then
+  echo "Usage: $0 [ttf_or_otf_file_name]"
+  exit 1
+fi
+
+# Check the font file using idempotent iff the font is not blacklisted.
+base=`basename "$1"`
+egrep -i -e "^$base" "$BLACKLIST" > /dev/null 2>&1 || "$CHECKER" "$1" > /dev/null 2>&1 || (echo ; echo "FAIL: $1 (Run $CHECKER $1 for more information.)")
+echo -n "."
diff --git a/third_party/ots/test/validator-checker.cc b/third_party/ots/test/validator-checker.cc
new file mode 100644
index 0000000..6cb5bca
--- /dev/null
+++ b/third_party/ots/test/validator-checker.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(_MSC_VER)
+#ifdef __linux__
+// Linux
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#else
+// Mac OS X
+#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
+#endif  // __linux__
+#else
+// Windows
+// TODO(yusukes): Support Windows.
+#endif  // _MSC_VER
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+#if !defined(_MSC_VER)
+#ifdef __linux__
+// Linux
+void LoadChar(FT_Face face, int pt, FT_ULong c) {
+  FT_Matrix matrix;
+  matrix.xx = matrix.yy = 1 << 16;
+  matrix.xy = matrix.yx = 0 << 16;
+
+  FT_Set_Char_Size(face, pt * (1 << 6), 0, 72, 0);
+  FT_Set_Transform(face, &matrix, 0);
+  FT_Load_Char(face, c, FT_LOAD_RENDER);
+}
+
+int OpenAndLoadChars(
+    const char *file_name, uint8_t *trans_font, size_t trans_len) {
+  FT_Library library;
+  FT_Error error = FT_Init_FreeType(&library);
+  if (error) {
+    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
+    return 1;
+  }
+
+  FT_Face trans_face;
+  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
+  if (error) {
+    std::fprintf(stderr,
+                 "OK: FreeType2 couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
+  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);
+
+  static const int kUnicodeRanges[] = {
+    0x0020, 0x007E,  // Basic Latin (ASCII)
+    0x00A1, 0x017F,  // Latin-1
+    0x1100, 0x11FF,  // Hangul
+    0x3040, 0x309F,  // Japanese HIRAGANA letters
+    0x3130, 0x318F,  // Hangul
+    0x4E00, 0x4F00,  // CJK Kanji/Hanja
+    0xAC00, 0xAD00,  // Hangul
+  };
+  static const size_t kUnicodeRangesLen
+      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);
+
+  for (size_t i = 0; i < kPtsLen; ++i) {
+    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
+      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
+        LoadChar(trans_face, kPts[i], kUnicodeRanges[j] + k);
+      }
+    }
+  }
+
+  std::fprintf(stderr, "OK: FreeType2 didn't crash: %s\n", file_name);
+  return 0;
+}
+#else
+// Mac OS X
+int OpenAndLoadChars(
+    const char *file_name, uint8_t *trans_font, size_t trans_len) {
+  CFDataRef data = CFDataCreate(0, trans_font, trans_len);
+  if (!data) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
+  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
+  CGDataProviderRelease(dataProvider);
+  CFRelease(data);
+  if (!cgFontRef) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
+  CGFontRelease(cgFontRef);
+  if (!numGlyphs) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+  std::fprintf(stderr, "OK: font renderer didn't crash: %s\n", file_name);
+  // TODO(yusukes): would be better to perform LoadChar() like Linux.
+  return 0;
+}
+#endif  // __linux__
+#else
+// Windows
+// TODO(yusukes): Support Windows.
+#endif  // _MSC_VER
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) {
+    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
+    return 1;
+  }
+
+  // load the font to memory.
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+  const off_t orig_len = st.st_size;
+
+  uint8_t *orig_font = new uint8_t[orig_len];
+  if (::read(fd, orig_font, orig_len) != orig_len) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+  ::close(fd);
+
+  // transcode the malicious font.
+  static const size_t kBigPadLen = 1024 * 1024;  // 1MB
+  uint8_t *trans_font = new uint8_t[orig_len + kBigPadLen];
+  ots::MemoryStream output(trans_font, orig_len + kBigPadLen);
+  ots::OTSContext context;
+
+  bool result = context.Process(&output, orig_font, orig_len);
+  if (!result) {
+    std::fprintf(stderr, "OK: the malicious font was filtered: %s\n", argv[1]);
+    return 0;
+  }
+  const size_t trans_len = output.Tell();
+
+  return OpenAndLoadChars(argv[1], trans_font, trans_len);
+}
diff --git a/third_party/ots/third_party/brotli.gyp b/third_party/ots/third_party/brotli.gyp
new file mode 100644
index 0000000..9903110
--- /dev/null
+++ b/third_party/ots/third_party/brotli.gyp
@@ -0,0 +1,32 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'brotli',
+      'type': 'static_library',
+      'include_dirs': [
+        'brotli/dec',
+      ],
+      'sources': [
+        'brotli/dec/bit_reader.c',
+        'brotli/dec/bit_reader.h',
+        'brotli/dec/context.h',
+        'brotli/dec/decode.c',
+        'brotli/dec/decode.h',
+        'brotli/dec/dictionary.h',
+        'brotli/dec/huffman.c',
+        'brotli/dec/huffman.h',
+        'brotli/dec/prefix.h',
+        'brotli/dec/safe_malloc.c',
+        'brotli/dec/safe_malloc.h',
+        'brotli/dec/streams.c',
+        'brotli/dec/streams.h',
+        'brotli/dec/transform.h',
+        'brotli/dec/types.h',
+      ],
+    },
+  ],
+}
diff --git a/third_party/ots/tools/ttf-checksum.py b/third_party/ots/tools/ttf-checksum.py
new file mode 100644
index 0000000..802e3cc
--- /dev/null
+++ b/third_party/ots/tools/ttf-checksum.py
@@ -0,0 +1,44 @@
+import struct
+import sys
+
+def readU32(contents, offset):
+  wordBytes = contents[offset:offset + 4]
+  return struct.unpack('>I', wordBytes)[0]
+
+def readU16(contents, offset):
+  wordBytes = contents[offset:offset + 2]
+  return struct.unpack('>H', wordBytes)[0]
+
+def checkChecksum(infile):
+  contents = infile.read()
+  if len(contents) % 4:
+    print 'File length is not a multiple of 4'
+
+  sum = 0
+  for offset in range(0, len(contents), 4):
+    sum += readU32(contents, offset)
+    while sum >= 2**32:
+      sum -= 2**32
+  print 'Sum of whole file: %x' % sum
+
+  numTables = readU16(contents, 4)
+
+  for offset in range(12, 12 + numTables * 16, 16):
+    tag = contents[offset:offset + 4]
+    chksum = readU32(contents, offset + 4)
+    toffset = readU32(contents, offset + 8)
+    tlength = readU32(contents, offset + 12)
+
+    sum = 0
+    for offset2 in range(toffset, toffset + tlength, 4):
+      sum += readU32(contents, offset2)
+      while sum >= 2**32:
+        sum -= 2**32
+    if sum != chksum:
+      print 'Bad chksum: %s' % tag
+
+if __name__ == '__main__':
+  if len(sys.argv) != 2:
+    print 'Usage: %s <ttf filename>' % sys.argv[0]
+  else:
+    checkChecksum(file(sys.argv[1], 'r'))
diff --git a/third_party/protobuf/BUILD.gn b/third_party/protobuf/BUILD.gn
index 1367d8a..6825f1e 100644
--- a/third_party/protobuf/BUILD.gn
+++ b/third_party/protobuf/BUILD.gn
@@ -17,11 +17,6 @@
     "GOOGLE_PROTOBUF_NO_RTTI",
     "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): http://crbug.com/167187 size_t -> int.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 if (component_mode == "shared_library") {
@@ -98,7 +93,11 @@
   if (is_win) {
     configs -= [ "//build/config/win:lean_and_mean" ]
   }
-  public_configs = [ ":protobuf_config" ]
+  public_configs = [
+    ":protobuf_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   cflags = protobuf_lite_cflags
 
@@ -177,7 +176,11 @@
   if (is_win) {
     configs -= [ "//build/config/win:lean_and_mean" ]
   }
-  public_configs = [ ":protobuf_config" ]
+  public_configs = [
+    ":protobuf_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   cflags = protobuf_lite_cflags
 }
diff --git a/third_party/qcms/BUILD.gn b/third_party/qcms/BUILD.gn
index b059319..ee62b8e 100644
--- a/third_party/qcms/BUILD.gn
+++ b/third_party/qcms/BUILD.gn
@@ -25,10 +25,10 @@
   configs += [ "//build/config/compiler:no_chromium_code" ]
   public_configs = [ ":qcms_config" ]
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     defines = [ "SSE2_ENABLE" ]
     sources += [ "src/transform-sse2.c" ]
-    if (!(is_win && cpu_arch == "x64")) {
+    if (!(is_win && current_cpu == "x64")) {
       # QCMS assumes this target isn't compiled since MSVC x64 doesn't support
       # the MMX intrinsics present in the SSE1 code.
       sources += [ "src/transform-sse1.c" ]
diff --git a/third_party/re2/BUILD.gn b/third_party/re2/BUILD.gn
index d022e4f..2bc130a 100644
--- a/third_party/re2/BUILD.gn
+++ b/third_party/re2/BUILD.gn
@@ -67,10 +67,7 @@
 
   if (is_win) {
     include_dirs = [ "mswin" ]
-    cflags = [
-      "/wd4267",  # Conversion from size_t.
-      "/wd4722",  # Destructor never terminates.
-    ]
+    cflags = [ "/wd4722" ]  # Destructor never terminates.
   } else {
     sources -= [ "mswin/stdint.h" ]
   }
diff --git a/third_party/smhasher/BUILD.gn b/third_party/smhasher/BUILD.gn
index e1bbb5e..fd9088c 100644
--- a/third_party/smhasher/BUILD.gn
+++ b/third_party/smhasher/BUILD.gn
@@ -27,9 +27,4 @@
   ]
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
-
-  if (is_win) {
-    # TODO(jschuh): http://code.google.com/p/smhasher/issues/detail?id=19
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/third_party/yasm/BUILD.gn b/third_party/yasm/BUILD.gn
index cd9b453..a09a495 100644
--- a/third_party/yasm/BUILD.gn
+++ b/third_party/yasm/BUILD.gn
@@ -30,18 +30,14 @@
 if (current_toolchain == host_toolchain) {
   # Various files referenced by multiple targets.
   yasm_gen_include_dir = "$target_gen_dir/include"
-  yasm_os = os
-  if (is_chromeos) {
-    yasm_os = "linux"
-  }
-  config_makefile = "source/config/$yasm_os/Makefile"
+  config_makefile = "source/config/$host_os/Makefile"
   version_file = "version.mac"
 
   import("//build/compiled_action.gni")
 
   config("yasm_config") {
     include_dirs = [
-      "source/config/$yasm_os",
+      "source/config/$host_os",
       "source/patched-yasm",
     ]
     defines = [ "HAVE_CONFIG_H" ]
@@ -148,8 +144,6 @@
     # re2c is missing CLOSEVOP from one switch.
     if (is_posix) {
       cflags = [ "-Wno-switch" ]
-    } else if (is_win) {
-      cflags = [ "/wd4267" ]  # size_t to int conversion.
     }
   }
 
@@ -248,9 +242,7 @@
     # directory, but the gen_x86_insn.py script does not make this easy.
     include_dirs = [ yasm_gen_include_dir ]
 
-    if (is_win) {
-      cflags = [ "/wd4267" ]  # size_t to int conversion.
-    } else {
+    if (!is_win) {
       cflags = [
         "-ansi",
         "-pedantic",
diff --git a/third_party/yasm/yasm_assemble.gni b/third_party/yasm/yasm_assemble.gni
index 56d7e11..1a84d51 100644
--- a/third_party/yasm/yasm_assemble.gni
+++ b/third_party/yasm/yasm_assemble.gni
@@ -42,13 +42,13 @@
 #   }
 
 if (is_mac || is_ios) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-fmacho32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-fmacho64",
       "-m",
@@ -56,13 +56,13 @@
     ]
   }
 } else if (is_posix) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-felf32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-DPIC",
       "-felf64",
@@ -71,14 +71,14 @@
     ]
   }
 } else if (is_win) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-DPREFIX",
       "-fwin32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-fwin64",
       "-m",
@@ -99,7 +99,7 @@
 
   # Only depend on YASM on x86 systems. Force compilation of .asm files for
   # ARM to fail.
-  assert(cpu_arch == "x86" || cpu_arch == "x64")
+  assert(current_cpu == "x86" || current_cpu == "x64")
 
   action_name = "${target_name}_action"
   source_set_name = target_name
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index e49b5e9..1cea9ac 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -7,7 +7,7 @@
 }
 
 static_library("zlib_x86_simd") {
-  if (!is_ios && (cpu_arch == "x86" || cpu_arch == "x64")) {
+  if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
     sources = [
       "crc_folding.c",
       "fill_window_sse.c",
@@ -65,7 +65,7 @@
     "zutil.h",
   ]
 
-  if (!is_ios && (cpu_arch == "x86" || cpu_arch == "x64")) {
+  if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
     sources += [ "x86.c" ]
   }