initial commit.



git-svn-id: http://ots.googlecode.com/svn/trunk@2 a4e77c2c-9104-11de-800e-5b313e0d2bf3
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..232307e
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,5 @@
+How to build (on Linux):
+
+ $ cd ots/test/
+ $ sudo apt-get install scons g++ libfreetype6-dev
+ $ scons
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a7531cf
--- /dev/null
+++ b/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/README b/README
new file mode 100644
index 0000000..b74d519
--- /dev/null
+++ b/README
@@ -0,0 +1,20 @@
+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.
+
+This is just an initial release. Do not use it in anger yet.
+
+[1] http://www.w3.org/TR/CSS2/fonts.html#font-descriptions
diff --git a/include/opentype-sanitiser.h b/include/opentype-sanitiser.h
new file mode 100644
index 0000000..dc593ba
--- /dev/null
+++ b/include/opentype-sanitiser.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OPENTYPE_SANITISER_H_
+#define OPENTYPE_SANITISER_H_
+
+#if defined(_MSC_VER)
+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;
+#include <Winsock2.h>  // for htons/ntohs
+#else
+#include <arpa/inet.h>
+#include <stdint.h>
+#endif
+
+#include <algorithm>  // for std::min
+#include <cassert>
+#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) {
+      chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(chksum_buffer_));
+      chksum_buffer_offset_ = 0;
+    }
+
+    while (length >= 4) {
+      chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(
+          reinterpret_cast<const uint8_t*>(data) + offset));
+      length -= 4;
+      offset += 4;
+    }
+
+    if (length) {
+      if (chksum_buffer_offset_ != 0) return false;  // not reached
+      if (length > 4) return false;  // not reached
+      std::memcpy(chksum_buffer_,
+             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 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 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_;
+};
+
+// -----------------------------------------------------------------------------
+// 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|
+// -----------------------------------------------------------------------------
+bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+// Force to disable debug output even when the library is compiled with
+// -DOTS_DEBUG.
+void DisableDebugOutput();
+
+}  // namespace ots
+
+#endif  // OPENTYPE_SANITISER_H_
diff --git a/include/ots-memory-stream.h b/include/ots-memory-stream.h
new file mode 100644
index 0000000..42b21f1
--- /dev/null
+++ b/include/ots-memory-stream.h
@@ -0,0 +1,50 @@
+// 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) {
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    if ((off_ + length > length_) ||
+        (length > std::numeric_limits<size_t>::max() - off_)) {
+      return false;
+    }
+    std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
+    off_ += length;
+    return true;
+  }
+
+  bool Seek(off_t position) {
+    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 * const ptr_;
+  size_t length_;
+  off_t off_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MEMORY_STREAM_H_
diff --git a/src/cff.cc b/src/cff.cc
new file mode 100644
index 0000000..28676b5
--- /dev/null
+++ b/src/cff.cc
@@ -0,0 +1,895 @@
+// 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 "cff.h"
+
+#include <cstring>
+#include <utility>  // std::pair
+#include <vector>
+
+// CFF - PostScript font program (Compact Font Format) table
+// http://www.microsoft.com/opentype/otspec/cff.htm
+// http://www.microsoft.com/opentype/otspec/5176.CFF.pdf
+
+namespace {
+
+struct CFFIndex {
+  CFFIndex()
+      : count(0), off_size(0) {}
+  uint16_t count;
+  uint8_t off_size;
+  std::vector<uint32_t> offsets;
+};
+
+enum DICT_OPERAND_TYPE {
+  DICT_OPERAND_INTEGER,
+  DICT_OPERAND_REAL,
+  DICT_OPERATOR,
+};
+
+// 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, CFFIndex *index) {
+  index->off_size = 0;
+  index->offsets.clear();
+
+  if (!table->ReadU16(&(index->count))) {
+    return OTS_FAILURE();
+  }
+  if (index->count == 0) {
+    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;
+    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) {
+    if (index->offsets[i] <= index->offsets[i - 1]) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseNameData(ots::Buffer *table, const CFFIndex &index) {
+  uint8_t name[256];
+  for (unsigned i = 1; i < index.offsets.size(); ++i) {
+    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();
+      }
+    }
+  }
+
+  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(0, DICT_OPERAND_REAL));
+        return true;
+      }
+      return OTS_FAILURE();
+    }
+    if ((nibble & 0x0f) == 0x0f) {
+      operands->push_back(std::make_pair(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((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(
+          (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(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) {
+  ots::Buffer table(data + offset, dict_length);
+
+  std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+  while (table.offset() < dict_length) {
+    if (!ParseDictDataReadNext(&table, &operands)) {
+      return OTS_FAILURE();
+    }
+    if (operands.empty()) {
+      return OTS_FAILURE();
+    }
+    if (operands.size() > 48) {
+      // An operator may be preceded by up to a maximum of 48 operands.
+      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 table(data, table_length);
+        table.set_offset(operands.back().first + offset);
+        CFFIndex local_subrs_index;
+        if (!ParseIndex(&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 CFFIndex &index, size_t sid_max, bool toplevel) {
+  for (unsigned i = 1; i < index.offsets.size(); ++i) {
+    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;
+
+    bool have_ros = false;
+    size_t 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) + 6:   // CharstringType
+        case (12U << 8) + 8:   // StrokeWidth
+        case (12U << 8) + 20:  // SyntheticBase
+        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();
+          }
+          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 table(data, table_length);
+          table.set_offset(operands.back().first);
+          uint8_t format = 0;
+          if (!table.ReadU8(&format)) {
+            return OTS_FAILURE();
+          }
+          if (format & 0x80) {
+            // supplemental encoding is not supported at the moment.
+            return OTS_FAILURE();
+          }
+          // TODO(yusukes): support & parse supplemental encoding tables.
+          break;
+        }
+
+        case 17: {  // CharStrings
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+          // parse "14. CharStrings INDEX"
+          ots::Buffer table(data, table_length);
+          table.set_offset(operands.back().first);
+          CFFIndex charstring_index;
+          if (!ParseIndex(&table, &charstring_index)) {
+            return OTS_FAILURE();
+          }
+          if (charstring_index.count < 2) {
+            return OTS_FAILURE();
+          }
+          if (glyphs) {
+            return OTS_FAILURE();  // multiple charstring tables?
+          }
+          glyphs = charstring_index.count;
+          break;
+        }
+
+        case (12U << 8) + 36: {  // FDArray
+          if (!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 table(data, table_length);
+          table.set_offset(operands.back().first);
+          CFFIndex sub_dict_index;
+          if (!ParseIndex(&table, &sub_dict_index)) {
+            return OTS_FAILURE();
+          }
+          if (!ParseDictData(data, table_length,
+                             sub_dict_index, sid_max, false /* toplevel */)) {
+            return OTS_FAILURE();
+          }
+          break;
+        }
+
+        case (12U << 8) + 37: {  // FDSelect
+          if (!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 table(data, table_length);
+          table.set_offset(operands.back().first);
+          uint8_t format = 0;
+          if (!table.ReadU8(&format)) {
+            return OTS_FAILURE();
+          }
+          if (format == 0) {
+            if (!table.Skip(glyphs)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check fd value?
+          } else if (format == 3) {
+            uint16_t n_ranges;
+            if (!table.ReadU16(&n_ranges)) {
+              return OTS_FAILURE();
+            }
+            if (n_ranges == 0) {
+              return OTS_FAILURE();
+            }
+
+            uint16_t last_gid = 0;
+            for (unsigned j = 0; j < n_ranges; ++j) {
+              uint16_t first;  // GID
+              if (!table.ReadU16(&first)) {
+                return OTS_FAILURE();
+              }
+              uint8_t fd = 0;
+              if (!table.ReadU8(&fd)) {
+                return OTS_FAILURE();
+              }
+              if ((j == 0) && (first != 0)) {
+                return OTS_FAILURE();
+              }
+              if ((j != 0) && (last_gid >= first)) {
+                return OTS_FAILURE();  // not increasing order.
+              }
+              last_gid = first;
+              // TODO(yusukes): check fd & GID values?
+            }
+            uint16_t sentinel;
+            if (!table.ReadU16(&sentinel)) {
+              return OTS_FAILURE();
+            }
+            if (last_gid >= sentinel) {
+              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) {
+            // does not overflow since table_length < 1GB
+            return OTS_FAILURE();
+          }
+          // parse "15. Private DICT Data"
+          if (!ParsePrivateDictData(data, table_length,
+                                    private_offset, private_length)) {
+            return OTS_FAILURE();
+          }
+          break;
+        }
+
+        // ROS
+        case (12U << 8) + 30:
+          if (!toplevel) {
+            return OTS_FAILURE();
+          }
+          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();
+    }
+
+    // parse "13. Charsets"
+    if (charset_offset) {
+      ots::Buffer table(data, table_length);
+      table.set_offset(charset_offset);
+      uint8_t format = 0;
+      if (!table.ReadU8(&format)) {
+        return OTS_FAILURE();
+      }
+      switch (format) {
+        case 0:
+          for (unsigned j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
+            uint16_t sid;
+            if (!table.ReadU16(&sid)) {
+              return OTS_FAILURE();
+            }
+            if (!have_ros && (sid > sid_max)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check CIDs when have_ros is true.
+          }
+          break;
+
+        case 1:
+        case 2: {
+          uint32_t total = 1;  // .notdef is omitted.
+          while (total < glyphs) {
+            uint16_t sid;
+            if (!table.ReadU16(&sid)) {
+              return OTS_FAILURE();
+            }
+            if (!have_ros && (sid > sid_max)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check CIDs when have_ros is true.
+
+            if (format == 1) {
+              uint8_t left = 0;
+              if (!table.ReadU8(&left)) {
+                return OTS_FAILURE();
+              }
+              total += (left + 1);
+            } else {
+              uint16_t left;
+              if (!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;
+
+  // 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 ((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)) {
+    return OTS_FAILURE();
+  }
+
+  // parse "8. Top DICT INDEX"
+  table.set_offset(name_index.offsets[name_index.count]);
+  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.offsets[top_dict_index.count]);
+  CFFIndex string_index;
+  if (!ParseIndex(&table, &string_index)) {
+    return OTS_FAILURE();
+  }
+  if (string_index.count >= 65000 - kNStdString) {
+    return OTS_FAILURE();
+  }
+
+  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,
+                     sid_max, true /* toplevel */)) {
+    return OTS_FAILURE();
+  }
+
+  // parse "16. Global Subrs INDEX"
+  table.set_offset(string_index.offsets[string_index.count]);
+  CFFIndex global_subrs_index;
+  if (!ParseIndex(&table, &global_subrs_index)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ots_cff_should_serialise(OpenTypeFile *file) {
+  return file->cff;
+}
+
+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) {
+  delete file->cff;
+}
+
+}  // namespace ots
diff --git a/src/cff.h b/src/cff.h
new file mode 100644
index 0000000..6d16ae8
--- /dev/null
+++ b/src/cff.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_CFF_H_
+#define OTS_CFF_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCFF {
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_CFF_H_
diff --git a/src/cmap.cc b/src/cmap.cc
new file mode 100644
index 0000000..a65c551
--- /dev/null
+++ b/src/cmap.cc
@@ -0,0 +1,757 @@
+// 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 "maxp.h"
+
+// cmap - Character To Glyph Index Mapping Table
+// http://www.microsoft.com/opentype/otspec/cmap.htm
+
+namespace {
+
+struct CMAPSubtableHeader {
+  uint16_t platform;
+  uint16_t encoding;
+  uint32_t offset;
+  uint16_t format;
+  uint32_t length;
+};
+
+struct Subtable314Range {
+  uint16_t start_range;
+  uint16_t end_range;
+  int16_t id_delta;
+  uint16_t id_range_offset;
+  uint32_t id_range_offset_offset;
+};
+
+// The maximum number of groups in format 12 or 13 subtables. Set so that we'll
+// allocate, at most, 8MB of memory when parsing these.
+const unsigned kMaxCMAPGroups = 699050;
+
+// Glyph array size for the Mac Roman (format 0) table.
+const size_t kFormat0ArraySize = 256;
+
+bool Parse3x4(ots::OpenTypeFile *file, int encoding,
+              const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
+  // whole thing and recompacting it, we valid it and include it verbatim
+  // in the ouput.
+
+  if (!subtable.Skip(4)) {
+    return OTS_FAILURE();
+  }
+  uint16_t language;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE();
+  }
+  if (language) {
+    return OTS_FAILURE();
+  }
+
+  uint16_t segcountx2, search_range, entry_selector, range_shift;
+  if (!subtable.ReadU16(&segcountx2) ||
+      !subtable.ReadU16(&search_range) ||
+      !subtable.ReadU16(&entry_selector) ||
+      !subtable.ReadU16(&range_shift)) {
+    return OTS_FAILURE();
+  }
+
+  if (segcountx2 & 1 || search_range & 1) {
+    return OTS_FAILURE();
+  }
+  const uint16_t segcount = segcountx2 >> 1;
+  // There must be at least one segment according the spec.
+  if (segcount < 1) {
+    return OTS_FAILURE();
+  }
+
+  // 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();
+  }
+
+  if (entry_selector != log2segcount) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t expected_range_shift = segcountx2 - search_range;
+  if (range_shift != expected_range_shift) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<Subtable314Range> ranges(segcount);
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].end_range)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  uint16_t padding;
+  if (!subtable.ReadU16(&padding)) {
+    return OTS_FAILURE();
+  }
+  if (padding) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].start_range)) {
+      return OTS_FAILURE();
+    }
+  }
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadS16(&ranges[i].id_delta)) {
+      return OTS_FAILURE();
+    }
+  }
+  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();
+    }
+
+    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();
+      }
+    }
+  }
+
+  // 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();
+    }
+    if (ranges[i].start_range <= ranges[i - 1].end_range) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // The last range must end at 0xffff
+  if (ranges[segcount - 1].end_range != 0xffff) {
+    return OTS_FAILURE();
+  }
+
+  // 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 = 1; i < segcount; ++i) {
+    for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
+      const uint16_t code_point = 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();
+        }
+      } 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();
+        }
+        uint16_t glyph;
+        memcpy(&glyph, data + glyph_id_offset, 2);
+        glyph = ntohs(glyph);
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE();
+        }
+      }
+    }
+  }
+
+  // We accept the table.
+  // TODO(yusukes): transcode the subtable.
+  if (encoding == 0) {
+    file->cmap->subtable_304_data = data;
+    file->cmap->subtable_304_length = length;
+  } else if (encoding == 1) {
+    file->cmap->subtable_314_data = data;
+    file->cmap->subtable_314_length = length;
+  } else {
+    return OTS_FAILURE();
+  }
+
+  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();
+  }
+  uint32_t language;
+  if (!subtable.ReadU32(&language)) {
+    return OTS_FAILURE();
+  }
+  if (language) {
+    return OTS_FAILURE();
+  }
+
+  uint32_t num_groups;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE();
+  }
+  // There are 12 bytes of data per group. In order to keep some sanity, we'll
+  // only allow ourselves to allocate 8MB of memory here. That means that
+  // we'll allow, at most, 8 * 1024 * 1024 / 12 groups. Note that this is
+  // still far in excess of the number of Unicode code-points currently
+  // allocated.
+  if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_31012;
+  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();
+    }
+
+    if (groups[i].start_range > 0x10FFFF ||
+        groups[i].end_range > 0x10FFFF ||
+        groups[i].start_glyph_id > 0xFFFF) {
+      return OTS_FAILURE();
+    }
+
+    // We assert that the glyph value is within range. Because the range
+    // limits, above, we don't need to worry about overflow.
+    if (groups[i].end_range < groups[i].start_range) {
+      return OTS_FAILURE();
+    }
+    if ((groups[i].end_range - groups[i].start_range) +
+        groups[i].start_glyph_id > num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // 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();
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE();
+    }
+  }
+
+  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();
+  }
+  uint16_t language;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE();
+  }
+  if (language) {
+    return OTS_FAILURE();
+  }
+
+  uint32_t num_groups;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE();
+  }
+
+  // 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();
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_31013;
+  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();
+    }
+
+    // We conservatively limit all of the values to 2^30 which is vastly larger
+    // than the number of Unicode code-points defined and might protect some
+    // parsers from overflows
+    if (groups[i].start_range > 0x40000000 ||
+        groups[i].end_range > 0x40000000 ||
+        groups[i].start_glyph_id > 0x40000000) {
+      return OTS_FAILURE();
+    }
+
+    if (groups[i].start_glyph_id >= num_glyphs) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // 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();
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE();
+    }
+  }
+
+  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();
+  }
+  uint16_t language;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE();
+  }
+  if (language) {
+    // simsun.ttf has non-zero language id.
+    OTS_WARNING("language id should be zero: %u", language);
+  }
+
+  file->cmap->subtable_100.reserve(kFormat0ArraySize);
+  for (size_t i = 0; i < kFormat0ArraySize; ++i) {
+    uint8_t glyph_id = 0;
+    if (!subtable.ReadU8(&glyph_id)) {
+      return OTS_FAILURE();
+    }
+    file->cmap->subtable_100.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, num_tables;
+  if (!table.ReadU16(&version) ||
+      !table.ReadU16(&num_tables)) {
+    return OTS_FAILURE();
+  }
+
+  if (version != 0) {
+    return OTS_FAILURE();
+  }
+  if (!num_tables) {
+    return OTS_FAILURE();
+  }
+
+  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();
+    }
+
+    subtable_headers.push_back(subt);
+  }
+
+  const size_t data_offset = table.offset();
+
+  // make sure that all the offsets are valid.
+  uint32_t last_id = 0;
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
+      return OTS_FAILURE();
+    }
+    if (subtable_headers[i].offset < data_offset ||
+        subtable_headers[i].offset >= length) {
+      return OTS_FAILURE();
+    }
+
+    // check if the table is sorted first by platform ID, then by encoding ID.
+    uint32_t current_id
+        = (subtable_headers[i].platform << 16) + subtable_headers[i].encoding;
+    if ((i != 0) && (last_id >= current_id)) {
+      return OTS_FAILURE();
+    }
+    last_id = current_id;
+  }
+
+  // the format of the table is the first couple of bytes in the table. The
+  // length of the table is in a format specific format afterwards.
+  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();
+    }
+
+    if ((subtable_headers[i].format == 0) ||
+        (subtable_headers[i].format == 4)) {
+      uint16_t len;
+      if (!table.ReadU16(&len)) {
+        return OTS_FAILURE();
+      }
+      subtable_headers[i].length = len;
+    } else if (subtable_headers[i].format == 12 ||
+               subtable_headers[i].format == 13) {
+      if (!table.Skip(2)) {
+        return OTS_FAILURE();
+      }
+      if (!table.ReadU32(&subtable_headers[i].length)) {
+        return OTS_FAILURE();
+      }
+    } else {
+      subtable_headers[i].length = 0;
+    }
+  }
+
+  // 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();
+    }
+    // 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();
+    }
+  }
+
+  // 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();
+  }
+  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             3            4       (Unicode BMP)
+  //   0             3            12      (Unicode UCS-4)
+  //   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)
+
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].platform == 0) {
+      // Unicode platform
+
+      if ((subtable_headers[i].encoding == 0) &&
+          (subtable_headers[i].format == 4)) {
+        // parse and output the 0-0-4 table as 3-1-4 table. Sometimes the 0-0-4
+        // table actually points to MS symbol data and thus should be parsed as
+        // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
+        // recovered in ots_cmap_serialise().
+        if (!Parse3x4(file, 1, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE();
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 4)) {
+        // parse and output the 0-3-4 table as 3-1-4 table.
+        file->cmap->subtable_314_data = 0;
+        file->cmap->subtable_314_length = 0;
+        if (!Parse3x4(file, 1, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE();
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 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();
+        }
+      }
+    } 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
+
+      if (subtable_headers[i].encoding == 0) {
+        if (subtable_headers[i].format == 4) {
+          if (!Parse3x4(file, 0, data + subtable_headers[i].offset,
+                        subtable_headers[i].length, num_glyphs)) {
+            return OTS_FAILURE();
+          }
+        }
+      } else if (subtable_headers[i].encoding == 1) {
+        if (subtable_headers[i].format == 4) {
+          // clear 0-0-4 or 0-3-4 table.
+          file->cmap->subtable_314_data = 0;
+          file->cmap->subtable_314_length = 0;
+          if (!Parse3x4(file, 1, data + subtable_headers[i].offset,
+                        subtable_headers[i].length, num_glyphs)) {
+            return OTS_FAILURE();
+          }
+        }
+      } else if (subtable_headers[i].encoding == 10) {
+        if (subtable_headers[i].format == 12) {
+          // clear 0-3-12 table.
+          file->cmap->subtable_31012.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) {
+          if (!Parse31013(file, data + subtable_headers[i].offset,
+                          subtable_headers[i].length, num_glyphs)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ots_cmap_should_serialise(OpenTypeFile *file) {
+  return file->cmap;
+}
+
+bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
+  const bool have_100 = file->cmap->subtable_100.size();
+  const bool have_304 = file->cmap->subtable_304_data;
+  // 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_314_data;
+  const bool have_31012 = file->cmap->subtable_31012.size();
+  const bool have_31013 = file->cmap->subtable_31013.size();
+  const unsigned num_subtables = static_cast<unsigned>(have_100) +
+                                 static_cast<unsigned>(have_304) +
+                                 static_cast<unsigned>(have_314) +
+                                 static_cast<unsigned>(have_31012) +
+                                 static_cast<unsigned>(have_31013);
+  const off_t table_start = out->Tell();
+
+  // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
+  // (e.g., old fonts for Mac). We don't support them.
+  if (!have_304 && !have_314) {
+    return OTS_FAILURE();
+  }
+
+  if (!out->WriteU16(0) ||
+      !out->WriteU16(num_subtables)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t record_offset = out->Tell();
+  if (!out->Pad(num_subtables * 8)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t offset_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_100[0]), kFormat0ArraySize)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_304 = out->Tell();
+  if (have_304) {
+    if (!out->Write(file->cmap->subtable_304_data,
+                    file->cmap->subtable_304_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_314 = out->Tell();
+  if (have_314) {
+    if (!out->Write(file->cmap->subtable_314_data,
+                    file->cmap->subtable_314_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_31012 = out->Tell();
+  if (have_31012) {
+    std::vector<OpenTypeCMAPSubtableRange> &groups = file->cmap->subtable_31012;
+    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_31013;
+    const unsigned num_groups = groups.size();
+    if (!out->WriteU16(13) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(num_groups * 12 + 14) ||
+        !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_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
diff --git a/src/cmap.h b/src/cmap.h
new file mode 100644
index 0000000..bf98c3d
--- /dev/null
+++ b/src/cmap.h
@@ -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.
+
+#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 OpenTypeCMAP {
+  OpenTypeCMAP()
+      : subtable_304_data(NULL),
+        subtable_304_length(0),
+        subtable_314_data(NULL),
+        subtable_314_length(0) {
+  }
+
+  const uint8_t *subtable_304_data;
+  size_t subtable_304_length;
+  const uint8_t *subtable_314_data;
+  size_t subtable_314_length;
+  std::vector<OpenTypeCMAPSubtableRange> subtable_31012;
+  std::vector<OpenTypeCMAPSubtableRange> subtable_31013;
+  std::vector<uint8_t> subtable_100;
+};
+
+}  // namespace ots
+
+#endif
diff --git a/src/cvt.cc b/src/cvt.cc
new file mode 100644
index 0000000..3403a87
--- /dev/null
+++ b/src/cvt.cc
@@ -0,0 +1,56 @@
+// 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/opentype/otspec/cvt.htm
+
+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();  // almost all cvt tables are less than 4k bytes.
+  }
+
+  if (length % 2 != 0) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE();
+  }
+
+  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 g_transcode_hints && file->cvt;
+}
+
+bool ots_cvt_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeCVT *cvt = file->cvt;
+
+  if (!out->Write(cvt->data, cvt->length)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_cvt_free(OpenTypeFile *file) {
+  delete file->cvt;
+}
+
+}  // namespace ots
diff --git a/src/cvt.h b/src/cvt.h
new file mode 100644
index 0000000..3c25f06
--- /dev/null
+++ b/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/src/fpgm.cc b/src/fpgm.cc
new file mode 100644
index 0000000..cf0e53c
--- /dev/null
+++ b/src/fpgm.cc
@@ -0,0 +1,50 @@
+// 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/opentype/otspec/fpgm.htm
+
+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();  // almost all fpgm tables are less than 5k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE();
+  }
+
+  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 g_transcode_hints && file->fpgm;
+}
+
+bool ots_fpgm_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeFPGM *fpgm = file->fpgm;
+
+  if (!out->Write(fpgm->data, fpgm->length)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_fpgm_free(OpenTypeFile *file) {
+  delete file->fpgm;
+}
+
+}  // namespace ots
diff --git a/src/fpgm.h b/src/fpgm.h
new file mode 100644
index 0000000..8fabac3
--- /dev/null
+++ b/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/src/gasp.cc b/src/gasp.cc
new file mode 100644
index 0000000..2adcc94
--- /dev/null
+++ b/src/gasp.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 "gasp.h"
+
+// gasp - Grid-fitting And Scan-conversion Procedure
+// http://www.microsoft.com/opentype/otspec/gasp.htm
+
+#define DROP_THIS_TABLE \
+  do { delete file->gasp; file->gasp = 0; } while (0)
+
+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;
+  if (!table.ReadU16(&gasp->version) ||
+      !table.ReadU16(&num_ranges)) {
+    return OTS_FAILURE();
+  }
+
+  if (gasp->version > 1) {
+    // Lots of Linux fonts have bad version numbers...
+    OTS_WARNING("bad version: %u", gasp->version);
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  if (num_ranges == 0) {
+    OTS_WARNING("num_ranges is zero");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  gasp->gasp_ranges.reserve(num_ranges);
+  for (unsigned i = 0; i < num_ranges; ++i) {
+    uint16_t max_ppem;
+    uint16_t behavior;
+    if (!table.ReadU16(&max_ppem) ||
+        !table.ReadU16(&behavior)) {
+      return OTS_FAILURE();
+    }
+    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.
+      OTS_WARNING("ranges are not sorted");
+      DROP_THIS_TABLE;
+      return true;
+    }
+    if ((i == num_ranges - 1u) &&  // never underflow.
+        (max_ppem != 0xffffu)) {
+      OTS_WARNING("The last record should be 0xFFFF as a sentinel value "
+                  "for rangeMaxPPEM");
+      DROP_THIS_TABLE;
+      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;
+}
+
+bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeGASP *gasp = file->gasp;
+
+  if (!out->WriteU16(gasp->version) ||
+      !out->WriteU16(gasp->gasp_ranges.size())) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < gasp->gasp_ranges.size(); ++i) {
+    if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
+        !out->WriteU16(gasp->gasp_ranges[i].second)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+void ots_gasp_free(OpenTypeFile *file) {
+  delete file->gasp;
+}
+
+}  // namespace ots
diff --git a/src/gasp.h b/src/gasp.h
new file mode 100644
index 0000000..9e7efba
--- /dev/null
+++ b/src/gasp.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_GASP_H_
+#define OTS_GASP_H_
+
+#include <utility>  // std::pair
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGASP {
+  uint16_t version;
+  std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;  // max PPEM, etc.
+};
+
+}  // namespace ots
+
+#endif  // OTS_GASP_H_
diff --git a/src/glyf.cc b/src/glyf.cc
new file mode 100644
index 0000000..bae147c
--- /dev/null
+++ b/src/glyf.cc
@@ -0,0 +1,265 @@
+// 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/opentype/otspec/glyf.htm
+
+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();
+  }
+
+  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();
+  }
+
+  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();
+    }
+    // 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();
+    }
+    if (gly_offset + gly_length > length) {
+      return OTS_FAILURE();
+    }
+
+    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();
+    }
+
+    if (num_contours < -1) {
+      // -2, -3, -4, ... are reserved for future use.
+      return OTS_FAILURE();
+    }
+
+    // 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();
+    }
+
+    unsigned new_size = 0;
+    if (num_contours >= 0) {
+      // this is a simple glyph and might contain bytecode
+
+      // read the end-points array
+      uint16_t num_flags = 0;
+      for (int j = 0; j < num_contours; ++j) {
+        uint16_t tmp_index;
+        if (!table.ReadU16(&tmp_index)) {
+          return OTS_FAILURE();
+        }
+        if (tmp_index == 0xffffu) {
+          return OTS_FAILURE();
+        }
+        // check if the indices are monotonically increasing
+        if (j && (tmp_index + 1 <= num_flags)) {
+          return OTS_FAILURE();
+        }
+        num_flags = tmp_index + 1;
+      }
+
+      uint16_t bytecode_length;
+      if (!table.ReadU16(&bytecode_length)) {
+        return OTS_FAILURE();
+      }
+      if ((file->maxp->version_1) &&
+          (file->maxp->max_glyf_insns < bytecode_length)) {
+        return OTS_FAILURE();
+      }
+
+      const unsigned gly_header_length = 10 + num_contours * 2 + 2;
+      if (gly_length < (gly_header_length + bytecode_length)) {
+        return OTS_FAILURE();
+      }
+
+      if (g_transcode_hints) {
+        glyf->iov.push_back(std::make_pair(
+            data + gly_offset, gly_header_length + bytecode_length));
+      } else {
+        // enqueue two vectors: the glyph data up to the bytecode length, then
+        // a pointer to a static uint16_t 0 to overwrite the length.
+        glyf->iov.push_back(std::make_pair(
+            data + gly_offset, gly_header_length - 2));
+        glyf->iov.push_back(std::make_pair((const uint8_t*) "\x00\x00", 2));
+      }
+
+      if (!table.Skip(bytecode_length)) {
+        return OTS_FAILURE();
+      }
+
+      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) {
+        uint8_t flag = 0;
+        if (!table.ReadU8(&flag)) {
+          return OTS_FAILURE();
+        }
+
+        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();
+          }
+          uint8_t repeat = 0;
+          if (!table.ReadU8(&repeat)) {
+            return OTS_FAILURE();
+          }
+          if (repeat == 0) {
+            return OTS_FAILURE();
+          }
+          delta += (delta * repeat);
+
+          flags_count_logical += repeat;
+          if (flags_count_logical >= num_flags) {
+            return OTS_FAILURE();
+          }
+          ++flags_count_physical;
+        }
+
+        if ((flag & (1u << 6)) || (flag & (1u << 7))) {  // reserved flags
+          return OTS_FAILURE();
+        }
+
+        xy_coordinates_length += delta;
+        if (gly_length < xy_coordinates_length) {
+          return OTS_FAILURE();
+        }
+      }
+
+      if (gly_length < (gly_header_length + bytecode_length +
+                        flags_count_physical + xy_coordinates_length)) {
+        return OTS_FAILURE();
+      }
+
+      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();
+      }
+
+      glyf->iov.push_back(std::make_pair(
+          data + gly_offset + gly_header_length + bytecode_length,
+          flags_count_physical + xy_coordinates_length));
+
+      new_size
+          = gly_header_length + flags_count_physical + xy_coordinates_length;
+      if (g_transcode_hints) {
+        new_size += bytecode_length;
+      }
+    } else {
+      // it's a composite glyph without any bytecode. Enqueue the whole thing
+      glyf->iov.push_back(std::make_pair(data + gly_offset, 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"), 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;
+}
+
+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();
+    }
+  }
+
+  return true;
+}
+
+void ots_glyf_free(OpenTypeFile *file) {
+  delete file->glyf;
+}
+
+}  // namespace ots
diff --git a/src/glyf.h b/src/glyf.h
new file mode 100644
index 0000000..d2eda77
--- /dev/null
+++ b/src/glyf.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_GLYF_H_
+#define OTS_GLYF_H_
+
+#include <utility>  // std::pair
+#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/src/hdmx.cc b/src/hdmx.cc
new file mode 100644
index 0000000..690bcce
--- /dev/null
+++ b/src/hdmx.cc
@@ -0,0 +1,132 @@
+// 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/opentype/otspec/hdmx.htm
+
+#define DROP_THIS_TABLE \
+  do { 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();
+  }
+
+  if ((file->head->flags & 0x14) == 0) {
+    OTS_WARNING("the table should not be present when bit 2 and 4 of the "
+                "head->flags are not set");
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  int16_t num_recs;
+  if (!table.ReadU16(&hdmx->version) ||
+      !table.ReadS16(&num_recs) ||
+      !table.ReadS32(&hdmx->size_device_record)) {
+    return OTS_FAILURE();
+  }
+  if (hdmx->version != 0) {
+    OTS_WARNING("bad version: %u", hdmx->version);
+    DROP_THIS_TABLE;
+    return true;
+  }
+  if (num_recs <= 0) {
+    OTS_WARNING("bad num_recs: %d", num_recs);
+    DROP_THIS_TABLE;
+    return true;
+  }
+  if (hdmx->size_device_record < (file->maxp->num_glyphs + 2)) {
+    OTS_WARNING("bad hdmx->size_device_record: %d", hdmx->size_device_record);
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  hdmx->pad_len = hdmx->size_device_record - (file->maxp->num_glyphs + 2);
+  if (hdmx->pad_len > 3) {
+    return OTS_FAILURE();
+  }
+
+  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();
+    }
+    if ((i != 0) &&
+        (rec.pixel_size <= last_pixel_size)) {
+      OTS_WARNING("records are not sorted");
+      DROP_THIS_TABLE;
+      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();
+      }
+      rec.widths.push_back(width);
+    }
+
+    if ((hdmx->pad_len > 0) &&
+        !table.Skip(hdmx->pad_len)) {
+      return OTS_FAILURE();
+    }
+
+    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;
+
+  if (!out->WriteU16(hdmx->version) ||
+      !out->WriteS16(hdmx->records.size()) ||
+      !out->WriteS32(hdmx->size_device_record)) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < hdmx->records.size(); ++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();
+    }
+    if ((hdmx->pad_len > 0) &&
+        !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+void ots_hdmx_free(OpenTypeFile *file) {
+  delete file->hdmx;
+}
+
+}  // namespace ots
diff --git a/src/hdmx.h b/src/hdmx.h
new file mode 100644
index 0000000..9ec2124
--- /dev/null
+++ b/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/src/head.cc b/src/head.cc
new file mode 100644
index 0000000..f9b456d
--- /dev/null
+++ b/src/head.cc
@@ -0,0 +1,146 @@
+// 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/opentype/otspec/head.htm
+
+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;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU32(&file->head->revision)) {
+    return OTS_FAILURE();
+  }
+
+  if (version >> 16 != 1) {
+    return OTS_FAILURE();
+  }
+
+  // Skip the checksum adjustment
+  if (!table.Skip(4)) {
+    return OTS_FAILURE();
+  }
+
+  uint32_t magic;
+  if (!table.ReadTag(&magic) ||
+      std::memcmp(&magic, "\x5F\x0F\x3C\xF5", 4)) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.ReadU16(&file->head->flags)) {
+    return OTS_FAILURE();
+  }
+
+  // We allow bits 0..4, 11..13
+  file->head->flags &= 0x381f;
+
+  if (!table.ReadU16(&file->head->ppem)) {
+    return OTS_FAILURE();
+  }
+
+  // ppem must be in range
+  if (file->head->ppem < 16 ||
+      file->head->ppem > 16384) {
+    return OTS_FAILURE();
+  }
+
+  // ppem must be a power of two
+  if ((file->head->ppem - 1) & file->head->ppem) {
+    // 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 (!table.ReadR64(&file->head->created) ||
+      !table.ReadR64(&file->head->modified)) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.ReadS16(&file->head->xmin) ||
+      !table.ReadS16(&file->head->ymin) ||
+      !table.ReadS16(&file->head->xmax) ||
+      !table.ReadS16(&file->head->ymax)) {
+    return OTS_FAILURE();
+  }
+
+  if (file->head->xmin > file->head->xmax) {
+    return OTS_FAILURE();
+  }
+  if (file->head->ymin > file->head->ymax) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.ReadU16(&file->head->mac_style)) {
+    return OTS_FAILURE();
+  }
+
+  // We allow bits 0..6
+  file->head->mac_style &= 0x7f;
+
+  if (!table.ReadU16(&file->head->min_ppem)) {
+    return OTS_FAILURE();
+  }
+
+  // We don't care about the font direction hint
+  if (!table.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.ReadS16(&file->head->index_to_loc_format)) {
+    return OTS_FAILURE();
+  }
+  if (file->head->index_to_loc_format < 0 ||
+      file->head->index_to_loc_format > 1) {
+    return OTS_FAILURE();
+  }
+
+  int16_t glyph_data_format;
+  if (!table.ReadS16(&glyph_data_format) ||
+      glyph_data_format) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ots_head_should_serialise(OpenTypeFile *file) {
+  return file->head;
+}
+
+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();
+  }
+
+  return true;
+}
+
+void ots_head_free(OpenTypeFile *file) {
+  delete file->head;
+}
+
+}  // namespace
diff --git a/src/head.h b/src/head.h
new file mode 100644
index 0000000..5967c4b
--- /dev/null
+++ b/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/src/hhea.cc b/src/hhea.cc
new file mode 100644
index 0000000..68704ef
--- /dev/null
+++ b/src/hhea.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 "hhea.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// hhea - Horizontal Header
+// http://www.microsoft.com/opentype/otspec/hhea.htm
+
+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;
+
+  uint32_t version;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  if (version >> 16 != 1) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.ReadS16(&hhea->ascent) ||
+      !table.ReadS16(&hhea->descent) ||
+      !table.ReadS16(&hhea->linegap) ||
+      !table.ReadU16(&hhea->adv_width_max) ||
+      !table.ReadS16(&hhea->min_lsb) ||
+      !table.ReadS16(&hhea->min_rsb) ||
+      !table.ReadS16(&hhea->x_max_extent) ||
+      !table.ReadS16(&hhea->caret_slope_rise) ||
+      !table.ReadS16(&hhea->caret_slope_run) ||
+      !table.ReadS16(&hhea->caret_offset)) {
+    return OTS_FAILURE();
+  }
+
+  if (hhea->ascent < 0) {
+    OTS_WARNING("bad ascent: %d", hhea->ascent);
+    hhea->ascent = 0;
+  }
+  if (hhea->linegap < 0) {
+    OTS_WARNING("bad linegap: %d", hhea->linegap);
+    hhea->linegap = 0;
+  }
+
+  if (!file->head) {
+    return OTS_FAILURE();
+  }
+
+  // if the font is non-slanted, caret_offset should be zero.
+  if (!(file->head->mac_style & 2) &&
+      (hhea->caret_offset != 0)) {
+    OTS_WARNING("bad caret offset: %d", hhea->caret_offset);
+    hhea->caret_offset = 0;
+  }
+
+  // skip the reserved bytes
+  if (!table.Skip(8)) {
+    return OTS_FAILURE();
+  }
+
+  int16_t data_format;
+  if (!table.ReadS16(&data_format)) {
+    return OTS_FAILURE();
+  }
+  if (data_format) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.ReadU16(&hhea->num_hmetrics)) {
+    return OTS_FAILURE();
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+
+  if (hhea->num_hmetrics > file->maxp->num_glyphs) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ots_hhea_should_serialise(OpenTypeFile *file) {
+  return file->hhea;
+}
+
+bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeHHEA *hhea = file->hhea;
+
+  if (!out->WriteU32(0x00010000) ||
+      !out->WriteS16(hhea->ascent) ||
+      !out->WriteS16(hhea->descent) ||
+      !out->WriteS16(hhea->linegap) ||
+      !out->WriteU16(hhea->adv_width_max) ||
+      !out->WriteS16(hhea->min_lsb) ||
+      !out->WriteS16(hhea->min_rsb) ||
+      !out->WriteS16(hhea->x_max_extent) ||
+      !out->WriteS16(hhea->caret_slope_rise) ||
+      !out->WriteS16(hhea->caret_slope_run) ||
+      !out->WriteS16(hhea->caret_offset) ||
+      !out->WriteR64(0) ||  // reserved
+      !out->WriteS16(0) ||  // metric data format
+      !out->WriteU16(hhea->num_hmetrics)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_hhea_free(OpenTypeFile *file) {
+  delete file->hhea;
+}
+
+}  // namespace ots
diff --git a/src/hhea.h b/src/hhea.h
new file mode 100644
index 0000000..99aafde
--- /dev/null
+++ b/src/hhea.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_HHEA_H_
+#define OTS_HHEA_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHHEA {
+  int16_t ascent;
+  int16_t descent;
+  int16_t linegap;
+  uint16_t adv_width_max;
+  int16_t min_lsb;
+  int16_t min_rsb;
+  int16_t x_max_extent;
+  int16_t caret_slope_rise;
+  int16_t caret_slope_run;
+  int16_t caret_offset;
+  uint16_t num_hmetrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HHEA_H_
diff --git a/src/hmtx.cc b/src/hmtx.cc
new file mode 100644
index 0000000..3b6ece0
--- /dev/null
+++ b/src/hmtx.cc
@@ -0,0 +1,107 @@
+// 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/opentype/otspec/hmtx.htm
+
+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();
+  }
+
+  // |num_hmetrics| is a uint16_t, so it's bounded < 65536. This limits that
+  // amount of memory that we'll allocate for this to a sane amount.
+  const unsigned num_hmetrics = file->hhea->num_hmetrics;
+
+  if (num_hmetrics > file->maxp->num_glyphs) {
+    return OTS_FAILURE();
+  }
+  if (!num_hmetrics) {
+    return OTS_FAILURE();
+  }
+  const unsigned num_lsbs = file->maxp->num_glyphs - num_hmetrics;
+
+  hmtx->metrics.reserve(num_hmetrics);
+  for (unsigned i = 0; i < num_hmetrics; ++i) {
+    uint16_t adv;
+    int16_t lsb;
+    if (!table.ReadU16(&adv) || !table.ReadS16(&lsb)) {
+      return OTS_FAILURE();
+    }
+
+    // Since so many fonts don't have proper value on |adv| and |lsb|,
+    // we should not call ots_failure() here. For example, about 20% of fonts
+    // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
+    if (adv > file->hhea->adv_width_max) {
+      OTS_WARNING("bad adv: %u > %u", adv, file->hhea->adv_width_max);
+      adv = file->hhea->adv_width_max;
+    }
+    if (lsb < file->hhea->min_lsb) {
+      OTS_WARNING("bad lsb: %d < %d", lsb, file->hhea->min_lsb);
+      lsb = file->hhea->min_lsb;
+    }
+
+    hmtx->metrics.push_back(std::make_pair(adv, lsb));
+  }
+
+  hmtx->lsbs.reserve(num_lsbs);
+  for (unsigned i = 0; i < num_lsbs; ++i) {
+    int16_t lsb;
+    if (!table.ReadS16(&lsb)) {
+      // Some Japanese font (e.g., mona.ttf) have bad hmtx table.
+      return OTS_FAILURE();
+    }
+
+    if (lsb < file->hhea->min_lsb) {
+      // The same as above. Three fonts in http://www.fontsquirrel.com/fontface
+      // (e.g., Notice2Std.otf) have weird lsb values.
+      OTS_WARNING("bad lsb: %d < %d", lsb, file->hhea->min_lsb);
+      lsb = file->hhea->min_lsb;
+    }
+
+    hmtx->lsbs.push_back(lsb);
+  }
+
+  return true;
+}
+
+bool ots_hmtx_should_serialise(OpenTypeFile *file) {
+  return file->hmtx;
+}
+
+bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeHMTX *hmtx = file->hmtx;
+
+  for (unsigned i = 0; i < hmtx->metrics.size(); ++i) {
+    if (!out->WriteU16(hmtx->metrics[i].first) ||
+        !out->WriteS16(hmtx->metrics[i].second)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  for (unsigned i = 0; i < hmtx->lsbs.size(); ++i) {
+    if (!out->WriteS16(hmtx->lsbs[i])) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+void ots_hmtx_free(OpenTypeFile *file) {
+  delete file->hmtx;
+}
+
+}  // namespace ots
diff --git a/src/hmtx.h b/src/hmtx.h
new file mode 100644
index 0000000..79a9cb6
--- /dev/null
+++ b/src/hmtx.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_HMTX_H_
+#define OTS_HMTX_H_
+
+#include <utility>  // std::pair
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHMTX {
+  std::vector<std::pair<uint16_t, int16_t> > metrics;
+  std::vector<int16_t> lsbs;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HMTX_H_
diff --git a/src/loca.cc b/src/loca.cc
new file mode 100644
index 0000000..49588e3
--- /dev/null
+++ b/src/loca.cc
@@ -0,0 +1,98 @@
+// 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/opentype/otspec/loca.htm
+
+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();
+  }
+
+  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;
+      if (!table.ReadU16(&offset)) {
+        return OTS_FAILURE();
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE();
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset * 2;
+    }
+  } else {
+    for (unsigned i = 0; i <= num_glyphs; ++i) {
+      uint32_t offset;
+      if (!table.ReadU32(&offset)) {
+        return OTS_FAILURE();
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE();
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset;
+    }
+  }
+
+  return true;
+}
+
+bool ots_loca_should_serialise(OpenTypeFile *file) {
+  return file->loca;
+}
+
+bool ots_loca_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLOCA *loca = file->loca;
+  const OpenTypeHEAD *head = file->head;
+
+  if (!head) {
+    return OTS_FAILURE();
+  }
+
+  if (head->index_to_loc_format == 0) {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      if (!out->WriteU16(loca->offsets[i] >> 1)) {
+        return OTS_FAILURE();
+      }
+    }
+  } else {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      if (!out->WriteU32(loca->offsets[i])) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_loca_free(OpenTypeFile *file) {
+  delete file->loca;
+}
+
+}  // namespace ots
diff --git a/src/loca.h b/src/loca.h
new file mode 100644
index 0000000..255ef06
--- /dev/null
+++ b/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/src/ltsh.cc b/src/ltsh.cc
new file mode 100644
index 0000000..57118c1
--- /dev/null
+++ b/src/ltsh.cc
@@ -0,0 +1,82 @@
+// 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 DROP_THIS_TABLE \
+  do { 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();
+  }
+
+  OpenTypeLTSH *ltsh = new OpenTypeLTSH;
+  file->ltsh = ltsh;
+
+  uint16_t num_glyphs;
+  if (!table.ReadU16(&ltsh->version) ||
+      !table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  if (ltsh->version != 0) {
+    OTS_WARNING("bad version: %u", ltsh->version);
+    DROP_THIS_TABLE;
+    return true;
+  }
+
+  if (num_glyphs != file->maxp->num_glyphs) {
+    OTS_WARNING("bad num_glyphs: %u", num_glyphs);
+    DROP_THIS_TABLE;
+    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();
+    }
+    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;
+}
+
+bool ots_ltsh_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLTSH *ltsh = file->ltsh;
+
+  if (!out->WriteU16(ltsh->version) ||
+      !out->WriteU16(ltsh->ypels.size())) {
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 0; i < ltsh->ypels.size(); ++i) {
+    if (!out->Write(&(ltsh->ypels[i]), 1)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+void ots_ltsh_free(OpenTypeFile *file) {
+  delete file->ltsh;
+}
+
+}  // namespace ots
diff --git a/src/ltsh.h b/src/ltsh.h
new file mode 100644
index 0000000..23d97d7
--- /dev/null
+++ b/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/src/maxp.cc b/src/maxp.cc
new file mode 100644
index 0000000..b984fe9
--- /dev/null
+++ b/src/maxp.cc
@@ -0,0 +1,128 @@
+// 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/opentype/otspec/maxp.htm
+
+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;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+
+  if (version >> 16 > 1) {
+    return OTS_FAILURE();
+  }
+
+  if (!table.ReadU16(&maxp->num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  if (!maxp->num_glyphs) {
+    return OTS_FAILURE();
+  }
+
+  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_glyf_insns) ||
+        !table.ReadU16(&maxp->max_c_components) ||
+        !table.ReadU16(&maxp->max_c_recursion)) {
+      return OTS_FAILURE();
+    }
+
+    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();
+    }
+  } else {
+    maxp->version_1 = false;
+  }
+
+  return true;
+}
+
+bool ots_maxp_should_serialise(OpenTypeFile *file) {
+  return file->maxp;
+}
+
+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();
+  }
+
+  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();
+  }
+
+  if (g_transcode_hints) {
+    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_glyf_insns)) {
+      return OTS_FAILURE();
+    }
+  } else {
+    if (!out->WriteU16(1) ||  // max zones
+        !out->WriteU16(0) ||  // max twilight points
+        !out->WriteU16(0) ||  // max storage
+        !out->WriteU16(0) ||  // max function defs
+        !out->WriteU16(0) ||  // max instruction defs
+        !out->WriteU16(0) ||  // max stack elements
+        !out->WriteU16(0)) {  // max instruction byte count
+      return OTS_FAILURE();
+    }
+  }
+
+  if (!out->WriteU16(maxp->max_c_components) ||
+      !out->WriteU16(maxp->max_c_recursion)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_maxp_free(OpenTypeFile *file) {
+  delete file->maxp;
+}
+
+}  // namespace ots
diff --git a/src/maxp.h b/src/maxp.h
new file mode 100644
index 0000000..5809f75
--- /dev/null
+++ b/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_glyf_insns;
+
+  uint16_t max_c_components;
+  uint16_t max_c_recursion;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MAXP_H_
diff --git a/src/name.cc b/src/name.cc
new file mode 100644
index 0000000..2ab7ebc
--- /dev/null
+++ b/src/name.cc
@@ -0,0 +1,86 @@
+// 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 <cstring>
+
+#include "ots.h"
+
+// name - Naming Table
+// http://www.microsoft.com/opentype/otspec/name.htm
+
+namespace ots {
+
+bool ots_name_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  return true;
+}
+
+bool ots_name_should_serialise(OpenTypeFile *) {
+  return true;
+}
+
+bool ots_name_serialise(OTSStream *out, OpenTypeFile *) {
+  // NAME is a required table, but we don't want anything to do with it. Thus,
+  // we don't bother parsing it and we just serialise an empty name table.
+
+  static const char * const kStrings[] = {
+      "Derived font data",  // 0: copyright
+      "OTS derived font",  // 1: the name the user sees
+      "Unspecified",  // 2: face weight
+      "UniqueID",  // 3: unique id
+      "OTS derivied font",  // 4: human readable name
+      "Version 0.0",  // 5: version
+      "False",  // 6: postscript name
+      NULL,  // 7: trademark data
+      "OTS",  // 8: foundary
+      "OTS",  // 9: designer
+  };
+  static const size_t kStringsLen = sizeof(kStrings) / sizeof(kStrings[0]);
+
+  unsigned num_strings = 0;
+  for (unsigned i = 0; i < kStringsLen; ++i) {
+    if (kStrings[i]) num_strings++;
+  }
+
+  if (!out->WriteU16(0) ||  // version
+      !out->WriteU16(num_strings) ||  // count
+      !out->WriteU16(6 + num_strings * 12)) {  // string data offset
+    return OTS_FAILURE();
+  }
+
+  unsigned current_offset = 0;
+  for (unsigned i = 0; i < kStringsLen; ++i) {
+    if (!kStrings[i]) continue;
+
+    const size_t len = std::strlen(kStrings[i]) * 2;
+    if (!out->WriteU16(3) ||  // Windows
+        !out->WriteU16(1) ||  // Roman
+        !out->WriteU16(0x0409) ||  // US English
+        !out->WriteU16(i) ||
+        !out->WriteU16(len) ||
+        !out->WriteU16(current_offset)) {
+      return OTS_FAILURE();
+    }
+
+    current_offset += len;
+  }
+
+  for (unsigned i = 0; i < kStringsLen; ++i) {
+    if (!kStrings[i]) continue;
+
+    const size_t len = std::strlen(kStrings[i]);
+    for (size_t j = 0; j < len; ++j) {
+      uint16_t v = kStrings[i][j];
+      if (!out->WriteU16(v)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_name_free(OpenTypeFile *) {
+}
+
+}  // namespace
diff --git a/src/os2.cc b/src/os2.cc
new file mode 100644
index 0000000..983482e
--- /dev/null
+++ b/src/os2.cc
@@ -0,0 +1,290 @@
+// 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/opentype/otspec/os2.htm
+
+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();
+  }
+
+  if (os2->version > 4) {
+    return OTS_FAILURE();
+  }
+
+  // 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();
+    }
+  }
+
+  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();
+  }
+
+  // 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();
+  }
+  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();
+  }
+
+  // mask reserved bits. use only 0..9 bits.
+  os2->selection &= 0x3ff;
+
+  if (os2->first_char_index > os2->last_char_index) {
+    return OTS_FAILURE();
+  }
+  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();
+  }
+
+  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();
+  }
+
+  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;
+}
+
+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();
+  }
+
+  for (unsigned i = 0; i < 10; ++i) {
+    if (!out->Write(&os2->panose[i], 1)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  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();
+  }
+
+  if (os2->version < 1) {
+    return true;
+  }
+
+  if (!out->WriteU32(os2->code_page_range_1) ||
+      !out->WriteU32(os2->code_page_range_2)) {
+    return OTS_FAILURE();
+  }
+
+  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();
+  }
+
+  return true;
+}
+
+void ots_os2_free(OpenTypeFile *file) {
+  delete file->os2;
+}
+
+}  // namespace ots
diff --git a/src/os2.h b/src/os2.h
new file mode 100644
index 0000000..9e0fc34
--- /dev/null
+++ b/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/src/ots.cc b/src/ots.cc
new file mode 100644
index 0000000..024af63
--- /dev/null
+++ b/src/ots.cc
@@ -0,0 +1,505 @@
+// 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 <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <map>
+#include <vector>
+
+// The OpenType Font File
+// http://www.microsoft.com/opentype/otspec/otff.htm
+
+#define F(name, capname) \
+  namespace ots { \
+  bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
+  bool ots_##name##_should_serialise(OpenTypeFile *f); \
+  bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
+  void ots_##name##_free(OpenTypeFile *f); \
+  }
+  // TODO(yusukes): change these function names to follow the coding rule.
+FOR_EACH_TABLE_TYPE
+#undef F
+
+namespace {
+
+bool g_debug_output = true;
+
+struct OpenTypeTable {
+  uint32_t tag;
+  uint32_t chksum;
+  uint32_t offset;
+  uint32_t length;
+};
+
+// Round a value up to the nearest multiple of 4. Note that this can overflow
+// and return zero.
+template<typename T> T Round4(T value) {
+  return (value + 3) & ~3;
+}
+
+uint32_t Tag(const char *tag_str) {
+  uint32_t ret;
+  std::memcpy(&ret, tag_str, 4);
+  return ret;
+}
+
+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;
+}
+
+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 BypassTable {
+  uint32_t tag;
+  size_t offset;  // offset into input data
+  size_t length;
+};
+
+const struct {
+  uint32_t 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;
+  bool bypass;
+} table_parsers[] = {
+  { Tag("maxp"), ots::ots_maxp_parse, ots::ots_maxp_serialise,
+    ots::ots_maxp_should_serialise, ots::ots_maxp_free, 1, 0 },
+  { Tag("cmap"), ots::ots_cmap_parse, ots::ots_cmap_serialise,
+    ots::ots_cmap_should_serialise, ots::ots_cmap_free, 1, 0 },
+  { Tag("head"), ots::ots_head_parse, ots::ots_head_serialise,
+    ots::ots_head_should_serialise, ots::ots_head_free, 1, 0 },
+  { Tag("hhea"), ots::ots_hhea_parse, ots::ots_hhea_serialise,
+    ots::ots_hhea_should_serialise, ots::ots_hhea_free, 1, 0 },
+  { Tag("hmtx"), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
+    ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, 1, 0 },
+  { Tag("name"), ots::ots_name_parse, ots::ots_name_serialise,
+    ots::ots_name_should_serialise, ots::ots_name_free, 1, 0 },
+  { Tag("OS/2"), ots::ots_os2_parse, ots::ots_os2_serialise,
+    ots::ots_os2_should_serialise, ots::ots_os2_free, 1, 0 },
+  { Tag("post"), ots::ots_post_parse, ots::ots_post_serialise,
+    ots::ots_post_should_serialise, ots::ots_post_free, 1, 0 },
+  { Tag("loca"), ots::ots_loca_parse, ots::ots_loca_serialise,
+    ots::ots_loca_should_serialise, ots::ots_loca_free, 0, 0 },
+  { Tag("glyf"), ots::ots_glyf_parse, ots::ots_glyf_serialise,
+    ots::ots_glyf_should_serialise, ots::ots_glyf_free, 0, 0 },
+  { Tag("CFF "), ots::ots_cff_parse, ots::ots_cff_serialise,
+    ots::ots_cff_should_serialise, ots::ots_cff_free, 0, 0 },
+  { Tag("VDMX"), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
+    ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, 0, 0 },
+  { Tag("hdmx"), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
+    ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, 0, 0 },
+  { Tag("gasp"), ots::ots_gasp_parse, ots::ots_gasp_serialise,
+    ots::ots_gasp_should_serialise, ots::ots_gasp_free, 0, 0 },
+  { Tag("cvt "), ots::ots_cvt_parse, ots::ots_cvt_serialise,
+    ots::ots_cvt_should_serialise, ots::ots_cvt_free, 0, 0 },
+  { Tag("fpgm"), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
+    ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, 0, 0 },
+  { Tag("prep"), ots::ots_prep_parse, ots::ots_prep_serialise,
+    ots::ots_prep_should_serialise, ots::ots_prep_free, 0, 0 },
+  { Tag("LTSH"), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
+    ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, 0, 0 },
+  { Tag("VORG"), ots::ots_vorg_parse, ots::ots_vorg_serialise,
+    ots::ots_vorg_should_serialise, ots::ots_vorg_free, 0, 0 },
+  { 0, NULL, NULL, NULL, 0, 0, 0 },
+};
+
+bool DoProcess(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();
+  }
+
+  if (!file.ReadTag(&header->version)) {
+    return OTS_FAILURE();
+  }
+  if ((header->version != Tag("\x00\x01\x00\x00")) &&
+      // OpenType fonts with CFF data have 'OTTO' tag.
+      (header->version != Tag("OTTO")) &&
+      // Older Mac fonts might have 'true' or 'typ1' tag.
+      (header->version != Tag("true")) && (header->version != Tag("typ1"))) {
+    return OTS_FAILURE();
+  }
+
+  if (!file.ReadU16(&header->num_tables) ||
+      !file.ReadU16(&header->search_range) ||
+      !file.ReadU16(&header->entry_selector) ||
+      !file.ReadU16(&header->range_shift)) {
+    return OTS_FAILURE();
+  }
+
+  // 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();
+  }
+
+  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 bad search_range value.
+  if (header->search_range != expected_search_range) {
+    OTS_WARNING("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();
+  }
+
+  // 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 uint32_t expected_range_shift
+      = 16 * header->num_tables - header->search_range;
+  if (header->range_shift != expected_range_shift) {
+    OTS_WARNING("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();
+    }
+
+    tables.push_back(table);
+  }
+
+  const size_t data_offset = file.offset();
+
+  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();
+      }
+    }
+
+    // all tag names must be built from printing characters
+    if (!CheckTag(tables[i].tag)) {
+      return OTS_FAILURE();
+    }
+
+    // tables must be 4-byte aligned
+    if (tables[i].offset & 3) {
+      return OTS_FAILURE();
+    }
+
+    // and must be within the file
+    if (tables[i].offset < data_offset || tables[i].offset >= length) {
+      return OTS_FAILURE();
+    }
+    // disallow all tables with a zero length
+    if (tables[i].length < 1) {
+      // Note: malayalam.ttf has zero length CVT table...
+      return OTS_FAILURE();
+    }
+    // disallow all tables with a length > 1GB
+    if (tables[i].length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE();
+    }
+    // since we required that the file be < 1GB in length, and that the table
+    // length is < 1GB, the following addtion doesn't overflow
+    const uint32_t end_byte = Round4(tables[i].offset + tables[i].length);
+    if (!end_byte || end_byte > length) {
+      return OTS_FAILURE();
+    }
+  }
+
+  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, 1 /* start */));
+    overlap_checker.push_back(
+        std::make_pair(tables[i].offset + tables[i].length, 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();
+    }
+  }
+
+  std::vector<BypassTable> bypass_tables;
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+
+    const std::map<uint32_t, OpenTypeTable>::const_iterator it
+        = table_map.find(table_parsers[i].tag);
+
+    if (it == table_map.end()) {
+      if (table_parsers[i].required) {
+        return OTS_FAILURE();
+      }
+      continue;
+    }
+
+    if (table_parsers[i].bypass) {
+      BypassTable bypass;
+      bypass.offset = it->second.offset;
+      bypass.length = it->second.length;
+      bypass.tag = table_parsers[i].tag;
+      bypass_tables.push_back(bypass);
+    }
+
+    if (!table_parsers[i].parse(
+        header, data + it->second.offset, it->second.length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (header->cff) {
+    // font with PostScript glyph
+    if (header->version != Tag("OTTO")) {
+      return OTS_FAILURE();
+    }
+    if (header->glyf || header->loca) {
+      // mixing outline formats are not recommended
+      return OTS_FAILURE();
+    }
+  } else {
+    if (!header->glyf || !header->loca) {
+      // No TrueType glyph found.
+      // Note: bitmap-only fonts are not supported.
+      return OTS_FAILURE();
+    }
+  }
+
+  unsigned num_output_tables = 0;
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (table_parsers[i].bypass) {
+      continue;
+    }
+
+    if (table_parsers[i].should_serialise(header)) {
+      num_output_tables++;
+    }
+  }
+
+  num_output_tables += bypass_tables.size();
+
+  max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_output_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+
+  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();
+  }
+  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();
+  }
+
+  std::vector<OutputTable> out_tables;
+
+  size_t head_table_offset = 0;
+  for (unsigned i = 0; i < bypass_tables.size(); ++i) {
+    const BypassTable &bypass = bypass_tables[i];
+
+    OutputTable out;
+    out.tag = bypass.tag;
+    out.offset = output->Tell();
+
+    output->ResetChecksum();
+    if (bypass.tag == Tag("head")) {
+      head_table_offset = out.offset;
+    }
+    if (!output->Write(data + bypass.offset, bypass.length)) {
+      return OTS_FAILURE();
+    }
+    const size_t end_offset = output->Tell();
+    out.length = end_offset - out.offset;
+
+    // align tables to four bytes
+    if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+      return OTS_FAILURE();
+    }
+    out.chksum = output->chksum();
+    out_tables.push_back(out);
+  }
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (table_parsers[i].bypass) {
+      continue;
+    }
+
+    if (!table_parsers[i].should_serialise(header)) {
+      continue;
+    }
+
+    OutputTable out;
+    out.tag = table_parsers[i].tag;
+    out.offset = output->Tell();
+
+    output->ResetChecksum();
+    if (table_parsers[i].tag == Tag("head")) {
+      head_table_offset = out.offset;
+    }
+    if (!table_parsers[i].serialise(output, header)) {
+      return OTS_FAILURE();
+    }
+
+    const size_t end_offset = output->Tell();
+    out.length = end_offset - out.offset;
+
+    // align tables to four bytes
+    if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+      return OTS_FAILURE();
+    }
+    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();
+  }
+
+  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();
+    }
+    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
+  assert(head_table_offset != 0);
+  if (!output->Seek(head_table_offset + 8)) {
+    return OTS_FAILURE();
+  }
+  if (!output->WriteU32(chksum_magic)) {
+    return OTS_FAILURE();
+  }
+
+  if (!output->Seek(end_of_file)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+void DisableDebugOutput() {
+  g_debug_output = false;
+}
+
+bool Process(OTSStream *output, const uint8_t *data, size_t length) {
+  OpenTypeFile header;
+  const bool result = DoProcess(&header, output, data, length);
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+    table_parsers[i].free(&header);
+  }
+  return result;
+}
+
+#if !defined(_MSC_VER) && defined(OTS_DEBUG)
+bool Failure(const char *f, int l, const char *fn) {
+  if (g_debug_output) {
+    std::fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn);
+    std::fflush(stderr);
+  }
+  return false;
+}
+
+void Warning(const char *f, int l, const char *format, ...) {
+  if (g_debug_output) {
+    std::fprintf(stderr, "WARNING at %s:%d: ", f, l);
+    std::va_list va;
+    va_start(va, format);
+    std::vfprintf(stderr, format, va);
+    va_end(va);
+    std::fprintf(stderr, "\n");
+    std::fflush(stderr);
+  }
+}
+#endif
+
+}  // namespace ots
diff --git a/src/ots.h b/src/ots.h
new file mode 100644
index 0000000..d026d34
--- /dev/null
+++ b/src/ots.h
@@ -0,0 +1,192 @@
+// 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 <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+#if defined(_MSC_VER) || !defined(OTS_DEBUG)
+#define OTS_FAILURE() false
+#else
+#define OTS_FAILURE() ots::Failure(__FILE__, __LINE__, __PRETTY_FUNCTION__)
+bool Failure(const char *f, int l, const char *fn);
+#endif
+
+#if defined(_MSC_VER)
+// MSVC supports C99 style variadic macros.
+#define OTS_WARNING(format, ...)
+#else
+// GCC
+#if defined(OTS_DEBUG)
+#define OTS_WARNING(format, args...) \
+    ots::Warning(__FILE__, __LINE__, format, ##args)
+void Warning(const char *f, int l, const char *format, ...)
+     __attribute__((format(printf, 3, 4)));
+#else
+#define OTS_WARNING(format, args...)
+#endif
+#endif
+
+// Define OTS_NO_TRANSCODE_HINTS (i.e., g++ -DOTS_NO_TRANSCODE_HINTS) if you
+// want to omit TrueType hinting instructions and variables in glyf, fpgm, prep,
+// and cvt tables.
+#if defined(OTS_NO_TRANSCODE_HINTS)
+const bool g_transcode_hints = false;
+#else
+const bool g_transcode_hints = true;
+#endif
+
+// -----------------------------------------------------------------------------
+// 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 *buffer, size_t len)
+      : buffer_(buffer),
+        length_(len),
+        offset_(0) { }
+
+  bool Skip(size_t n_bytes) {
+    return Read(NULL, n_bytes);
+  }
+
+  bool Read(uint8_t *buffer, 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 (buffer) {
+      std::memcpy(buffer, 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 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;
+  }
+
+  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_;
+};
+
+#define FOR_EACH_TABLE_TYPE \
+  F(cff, CFF) \
+  F(cmap, CMAP) \
+  F(cvt, CVT) \
+  F(fpgm, FPGM) \
+  F(gasp, GASP) \
+  F(glyf, GLYF) \
+  F(hdmx, HDMX) \
+  F(head, HEAD) \
+  F(hhea, HHEA) \
+  F(hmtx, HMTX) \
+  F(loca, LOCA) \
+  F(ltsh, LTSH) \
+  F(maxp, MAXP) \
+  F(name, NAME) \
+  F(os2, OS2) \
+  F(post, POST) \
+  F(prep, PREP) \
+  F(vdmx, VDMX) \
+  F(vorg, VORG)
+
+#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;
+
+#define F(name, capname) OpenType##capname *name;
+FOR_EACH_TABLE_TYPE
+#undef F
+};
+
+}  // namespace ots
+
+#endif  // OTS_H_
diff --git a/src/post.cc b/src/post.cc
new file mode 100644
index 0000000..cb42417
--- /dev/null
+++ b/src/post.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 "post.h"
+
+#include "maxp.h"
+
+// post - PostScript
+// http://www.microsoft.com/opentype/otspec/post.htm
+
+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();
+  }
+
+  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();
+  }
+
+  // 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();
+  }
+
+  uint16_t num_glyphs;
+  if (!table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE();
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+
+  if (num_glyphs == 0) {
+    if (file->maxp->num_glyphs > 258) {
+      return OTS_FAILURE();
+    }
+    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();
+  }
+
+  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();
+    }
+    if (post->glyph_name_index[i] >= 32768) {
+      // Note: droid_arialuni.ttf fails this test.
+      return OTS_FAILURE();  // reserved area.
+    }
+  }
+
+  // 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();
+    }
+    if (std::memchr(strings + 1, '\0', string_length)) {
+      return OTS_FAILURE();
+    }
+    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();
+    }
+  }
+
+  return true;
+}
+
+bool ots_post_should_serialise(OpenTypeFile *file) {
+  return file->post;
+}
+
+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();
+  }
+
+  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();
+  }
+
+  if (post->version != 0x00020000) {
+    return true;  // v1.0 and v3.0 does not have glyph names.
+  }
+
+  if (!out->WriteU16(post->glyph_name_index.size())) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < post->glyph_name_index.size(); ++i) {
+    if (!out->WriteU16(post->glyph_name_index[i])) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // 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 = s.size();
+    if (!out->Write(&string_length, 1)) {
+      return OTS_FAILURE();
+    }
+    // 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();
+    }
+  }
+
+  return true;
+}
+
+void ots_post_free(OpenTypeFile *file) {
+  delete file->post;
+}
+
+}  // namespace ots
diff --git a/src/post.h b/src/post.h
new file mode 100644
index 0000000..f220d4f
--- /dev/null
+++ b/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/src/prep.cc b/src/prep.cc
new file mode 100644
index 0000000..6567a6f
--- /dev/null
+++ b/src/prep.cc
@@ -0,0 +1,50 @@
+// 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/opentype/otspec/prep.htm
+
+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();  // almost all prep tables are less than 9k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE();
+  }
+
+  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 g_transcode_hints && file->prep;
+}
+
+bool ots_prep_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypePREP *prep = file->prep;
+
+  if (!out->Write(prep->data, prep->length)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_prep_free(OpenTypeFile *file) {
+  delete file->prep;
+}
+
+}  // namespace ots
diff --git a/src/prep.h b/src/prep.h
new file mode 100644
index 0000000..935ca11
--- /dev/null
+++ b/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/src/vdmx.cc b/src/vdmx.cc
new file mode 100644
index 0000000..73236c1
--- /dev/null
+++ b/src/vdmx.cc
@@ -0,0 +1,176 @@
+// 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/opentype/otspec/vdmx.htm
+
+#define DROP_THIS_TABLE \
+  do { 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();
+  }
+
+  if (vdmx->version > 1) {
+    OTS_WARNING("bad version: %u", vdmx->version);
+    DROP_THIS_TABLE;
+    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();
+    }
+
+    if (rec.charset > 1) {
+      OTS_WARNING("bad charset: %u", rec.charset);
+      DROP_THIS_TABLE;
+      return true;
+    }
+    if (rec.y_start_ratio < rec.y_end_ratio) {
+      OTS_WARNING("bad y ratio");
+      DROP_THIS_TABLE;
+      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.
+      OTS_WARNING("superfluous terminator found");
+      DROP_THIS_TABLE;
+      return true;  // continue transcoding
+    }
+
+    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();
+    }
+    if (current_offset + offset >= length) {  // thus doesn't overflow.
+      return OTS_FAILURE();
+    }
+
+    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();
+    }
+    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();
+      }
+      if (vt.y_max < vt.y_min) {
+        OTS_WARNING("bad y min/max");
+        DROP_THIS_TABLE;
+        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)) {
+        OTS_WARNING("the table is not sorted");
+        DROP_THIS_TABLE;
+        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;
+}
+
+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();
+  }
+
+  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();
+    }
+  }
+
+  for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
+    if (!out->WriteU16(vdmx->offsets[i])) {
+      return OTS_FAILURE();
+    }
+  }
+
+  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();
+    }
+    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();
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_vdmx_free(OpenTypeFile *file) {
+  delete file->vdmx;
+}
+
+}  // namespace ots
diff --git a/src/vdmx.h b/src/vdmx.h
new file mode 100644
index 0000000..1d959ef
--- /dev/null
+++ b/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/src/vorg.cc b/src/vorg.cc
new file mode 100644
index 0000000..9b70a98
--- /dev/null
+++ b/src/vorg.cc
@@ -0,0 +1,97 @@
+// 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/opentype/otspec/vorg.htm
+
+#define DROP_THIS_TABLE \
+  do { 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();
+  }
+  if (vorg->major_version != 1) {
+    OTS_WARNING("bad major version: %u", vorg->major_version);
+    DROP_THIS_TABLE;
+    return true;
+  }
+  if (vorg->minor_version != 0) {
+    OTS_WARNING("bad minor version: %u", vorg->minor_version);
+    DROP_THIS_TABLE;
+    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();
+    }
+    if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
+      OTS_WARNING("the table is not sorted");
+      DROP_THIS_TABLE;
+      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;
+}
+
+bool ots_vorg_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeVORG * const vorg = file->vorg;
+
+  if (!out->WriteU16(vorg->major_version) ||
+      !out->WriteU16(vorg->minor_version) ||
+      !out->WriteS16(vorg->default_vert_origin_y) ||
+      !out->WriteU16(vorg->metrics.size())) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i < vorg->metrics.size(); ++i) {
+    const OpenTypeVORGMetrics& rec = vorg->metrics[i];
+    if (!out->WriteU16(rec.glyph_index) ||
+        !out->WriteS16(rec.vert_origin_y)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+void ots_vorg_free(OpenTypeFile *file) {
+  delete file->vorg;
+}
+
+}  // namespace ots
diff --git a/src/vorg.h b/src/vorg.h
new file mode 100644
index 0000000..c3d3ffd
--- /dev/null
+++ b/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/test/README b/test/README
new file mode 100644
index 0000000..c31f7ce
--- /dev/null
+++ b/test/README
@@ -0,0 +1,243 @@
+------------------------------------------------------------------------------
+ot-sanitise - TTF/OTF font transcoder
+
+Description:
+  ot-sanitise is a program which validates and 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 > transcoded_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/test/SConstruct b/test/SConstruct
new file mode 100644
index 0000000..4e688d3
--- /dev/null
+++ b/test/SConstruct
@@ -0,0 +1,43 @@
+# Build script for Linux
+#
+# Usage:
+#   $ cd ots/test/
+#   $ scons -c         # clean
+#   $ scons            # build
+#
+
+# Since the validator-checker tool might handle malicious font files, all hardening options for recent g++/ld are enabled just in case.
+# See http://wiki.debian.org/Hardening for details.
+env = Environment(CCFLAGS = ['-O2', '-I../include', '-I/usr/include/freetype2', '-ggdb', '-Wall', '-W', '-Wno-unused-parameter', '-fPIE', '-fstack-protector', '-D_FORTIFY_SOURCE=2', '-DOTS_DEBUG'], LINKFLAGS = ['-ggdb', '-Wl,-z,relro', '-Wl,-z,now', '-pie'])
+# TODO(yusukes): better to use pkg-config freetype2 --cflags
+ 
+env.Library('../src/libots.a',
+            ['../src/cff.cc',
+	    '../src/cmap.cc',
+	    '../src/cvt.cc',
+	    '../src/fpgm.cc',
+	    '../src/gasp.cc',
+	    '../src/glyf.cc',
+	    '../src/hdmx.cc',
+	    '../src/head.cc',
+	    '../src/hhea.cc',
+	    '../src/hmtx.cc',
+	    '../src/loca.cc',
+	    '../src/ltsh.cc',
+	    '../src/maxp.cc',
+	    '../src/name.cc',
+	    '../src/os2.cc',
+	    '../src/ots.cc',
+	    '../src/post.cc',
+	    '../src/prep.cc',
+	    '../src/vdmx.cc',
+	    '../src/vorg.cc'
+            ])
+ 
+env.Program('../test/ot-sanitise.cc', LIBS = ['ots'], LIBPATH='../src')
+env.Program('../test/idempotent.cc', LIBS = ['ots'], LIBPATH='../src')
+env.Program('../test/perf.cc', LIBS = ['ots'], LIBPATH='../src')
+
+# TODO(yusukes): better to use pkg-config freetype2 --libs
+env.Program('../test/side-by-side.cc', LIBS = ['ots', 'freetype', 'z', 'm'], LIBPATH = '../src')
+env.Program('../test/validator-checker.cc', LIBS = ['ots', 'freetype', 'z', 'm'], LIBPATH = '../src')
diff --git a/test/file-stream.h b/test/file-stream.h
new file mode 100644
index 0000000..58f85e1
--- /dev/null
+++ b/test/file-stream.h
@@ -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.
+
+#ifndef OTS_FILE_STREAM_H_
+#define OTS_FILE_STREAM_H_
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+class FILEStream : public OTSStream {
+ public:
+  explicit FILEStream(FILE *stream)
+      : file_(stream) {
+  }
+
+  ~FILEStream() {
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    return fwrite(data, length, 1, file_) == 1;
+  }
+
+  bool Seek(off_t position) {
+    return fseek(file_, position, SEEK_SET) == 0;
+  }
+
+  off_t Tell() const {
+    return ftell(file_);
+  }
+
+ private:
+  FILE * const file_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_FILE_STREAM_H_
diff --git a/test/idempotent.cc b/test/idempotent.cc
new file mode 100644
index 0000000..7f33a47
--- /dev/null
+++ b/test/idempotent.cc
@@ -0,0 +1,94 @@
+// 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 <unistd.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) {
+    ::close(fd);
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+  ::close(fd);
+
+  // 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];
+  ots::MemoryStream output(result, st.st_size + kPadLen);
+
+  bool r = ots::Process(&output, data, st.st_size);
+  if (!r) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+    return 1;
+  }
+  const size_t result_len = output.Tell();
+  free(data);
+
+  uint8_t *result2 = new uint8_t[result_len];
+  ots::MemoryStream output2(result2, result_len);
+  r = ots::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");
+    int fd1 = ::open("out1.ttf", O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    int fd2 = ::open("out2.ttf", O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    if (fd1 < 0 || fd2 < 0) {
+      ::perror("opening output file");
+      return 1;
+    }
+    if ((::write(fd1, result, result_len) < 0) ||
+        (::write(fd2, result2, result2_len) < 0)) {
+      ::perror("writing output file");
+      return 1;
+    }
+  }
+
+  return 0;
+}
diff --git a/test/ot-sanitise.cc b/test/ot-sanitise.cc
new file mode 100644
index 0000000..313a6bc
--- /dev/null
+++ b/test/ot-sanitise.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.
+
+// 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>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+
+#include "file-stream.h"
+#include "opentype-sanitiser.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s ttf_file > dest_ttf_file\n", argv0);
+  return 1;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) return Usage(argv[0]);
+  if (::isatty(1)) 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) {
+    ::perror("read");
+    return 1;
+  }
+  ::close(fd);
+
+  ots::FILEStream output(stdout);
+  const bool result = ots::Process(&output, data, st.st_size);
+
+  if (!result) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+  }
+  return !result;
+}
diff --git a/test/perf.cc b/test/perf.cc
new file mode 100644
index 0000000..34fe931
--- /dev/null
+++ b/test/perf.cc
@@ -0,0 +1,79 @@
+// 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>  // for timersub macro.
+#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) {
+  ots::DisableDebugOutput();  // turn off ERROR and WARNING outputs.
+
+  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);
+    bool r = ots::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);
+
+  uint64_t us = ((elapsed.tv_sec * 1000 * 1000) + elapsed.tv_usec) / num_repeat;
+  std::fprintf(stderr, "%lu [us] %s (%lu bytes, %lu [byte/us])\n",
+               us, argv[1], st.st_size, (us ? st.st_size / us : 0));
+
+  return 0;
+}
diff --git a/test/side-by-side.cc b/test/side-by-side.cc
new file mode 100644
index 0000000..1b5ff90
--- /dev/null
+++ b/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 <freetype/ftoutln.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <sys/stat.h>
+#include <sys/types.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) {
+  ots::DisableDebugOutput();  // turn off ERROR and WARNING outputs.
+
+  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);
+
+  bool result = ots::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/test/validator-checker.cc b/test/validator-checker.cc
new file mode 100644
index 0000000..66c2b75
--- /dev/null
+++ b/test/validator-checker.cc
@@ -0,0 +1,117 @@
+// 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+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(FT_Library library, const char *file_name,
+                     uint8_t *trans_font, size_t trans_len) {
+  FT_Face trans_face;
+  FT_Error 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;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  ots::DisableDebugOutput();  // turn off ERROR and WARNING outputs.
+
+  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);
+
+  bool result = ots::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();
+
+  FT_Library library;
+  FT_Error error = FT_Init_FreeType(&library);
+  if (error) {
+    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
+    return 1;
+  }
+
+  return OpenAndLoadChars(library, argv[1], trans_font, trans_len);
+}
diff --git a/tools/ttf-checksum.py b/tools/ttf-checksum.py
new file mode 100755
index 0000000..802e3cc
--- /dev/null
+++ b/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'))