bug 747816 - update OTS to upstream r.91 to resolve endianness bug and other fixes. r=emk
authorJonathan Kew <jkew@mozilla.com>
Wed, 23 May 2012 16:59:26 +0100
changeset 94719 7ffffcb45b9402c0b2cb12e8fdd0d8293a7e48fb
parent 94718 7ae630f43357096ea87c534e07ed4da398ea2653
child 94720 dd6c4f6a244814e6c0d80ae625abea6b21f4c000
push idunknown
push userunknown
push dateunknown
reviewersemk
bugs747816
milestone15.0a1
bug 747816 - update OTS to upstream r.91 to resolve endianness bug and other fixes. r=emk
gfx/ots/README.mozilla
gfx/ots/include/opentype-sanitiser.h
gfx/ots/ots-fix-sparc64.patch
gfx/ots/ots-graphite.patch
gfx/ots/ots-visibility.patch
gfx/ots/src/cff.cc
gfx/ots/src/ots.cc
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,8 +1,12 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
-Current revision: r81
+Current revision: r91
 
 Applied local patches:
     ots-fix-vc10.patch - workaround for VS10 STL wrappers (bug 602558)
 
     ots-fix-sparc64.patch - fix alignment error on sparc64 (bug 643137)
+
+    ots-graphite.patch - preserve Graphite layout tables (bug 631479)
+
+    ots-visibility.patch - make Process function externally visible for Windows DLL (bug 711079)
--- a/gfx/ots/include/opentype-sanitiser.h
+++ b/gfx/ots/include/opentype-sanitiser.h
@@ -21,25 +21,29 @@
   #else
     #define OTS_API OTS_DLL_IMPORT
   #endif
 #else
   #define OTS_API
 #endif
 
 #if defined(_WIN32)
+#include <stdlib.h>
 typedef signed char int8_t;
 typedef unsigned char uint8_t;
 typedef short int16_t;
 typedef unsigned short uint16_t;
 typedef int int32_t;
 typedef unsigned int uint32_t;
 typedef __int64 int64_t;
 typedef unsigned __int64 uint64_t;
-#include <winsock2.h>  // for htons/ntohs
+#define ntohl(x) _byteswap_ulong (x)
+#define ntohs(x) _byteswap_ushort (x)
+#define htonl(x) _byteswap_ulong (x)
+#define htons(x) _byteswap_ushort (x)
 #else
 #include <arpa/inet.h>
 #include <stdint.h>
 #endif
 
 #include <algorithm>  // for std::min
 #include <cassert>
 #include <cstddef>
--- a/gfx/ots/ots-fix-sparc64.patch
+++ b/gfx/ots/ots-fix-sparc64.patch
@@ -1,16 +1,16 @@
 diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
 --- a/gfx/ots/include/opentype-sanitiser.h
 +++ b/gfx/ots/include/opentype-sanitiser.h
-@@ -57,18 +57,20 @@ class OTSStream {
- 
+@@ -83,18 +83,20 @@ class OTSStream {
      if (chksum_buffer_offset_ == 4) {
-       // TODO(yusukes): This cast breaks the strict-aliasing rule.
-       chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(chksum_buffer_));
+       uint32_t chksum;
+       std::memcpy(&chksum, chksum_buffer_, 4);
+       chksum_ += ntohl(chksum);
        chksum_buffer_offset_ = 0;
      }
  
      while (length >= 4) {
 -      chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(
 -          reinterpret_cast<const uint8_t*>(data) + offset));
 +      uint32_t tmp;
 +      std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
new file mode 100644
--- /dev/null
+++ b/gfx/ots/ots-graphite.patch
@@ -0,0 +1,131 @@
+diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
+--- a/gfx/ots/include/opentype-sanitiser.h
++++ b/gfx/ots/include/opentype-sanitiser.h
+@@ -176,18 +176,20 @@ class OTSStream {
+ 
+ // -----------------------------------------------------------------------------
+ // Process a given OpenType file and write out a sanitised version
+ //   output: a pointer to an object implementing the OTSStream interface. The
+ //     sanitisied output will be written to this. In the even of a failure,
+ //     partial output may have been written.
+ //   input: the OpenType file
+ //   length: the size, in bytes, of |input|
++//   preserve_graphite_tables: whether to preserve Graphite Layout tables
+ // -----------------------------------------------------------------------------
+-bool Process(OTSStream *output, const uint8_t *input, size_t length);
++bool Process(OTSStream *output, const uint8_t *input, size_t length,
++             bool preserve_graphite_tables = false);
+ 
+ // 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/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc
+--- a/gfx/ots/src/ots.cc
++++ b/gfx/ots/src/ots.cc
+@@ -138,16 +138,27 @@ const struct {
+   { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
+     ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
+   { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
+     ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
+   { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
+     ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
+   { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+     ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
++  // SILGraphite layout tables - not actually parsed, just copied
++  { "Silf", ots::ots_silf_parse, ots::ots_silf_serialise,
++    ots::ots_silf_should_serialise, ots::ots_silf_free, false },
++  { "Sill", ots::ots_sill_parse, ots::ots_sill_serialise,
++    ots::ots_sill_should_serialise, ots::ots_sill_free, false },
++  { "Gloc", ots::ots_gloc_parse, ots::ots_gloc_serialise,
++    ots::ots_gloc_should_serialise, ots::ots_gloc_free, false },
++  { "Glat", ots::ots_glat_parse, ots::ots_glat_serialise,
++    ots::ots_glat_should_serialise, ots::ots_glat_free, false },
++  { "Feat", ots::ots_feat_parse, ots::ots_feat_serialise,
++    ots::ots_feat_should_serialise, ots::ots_feat_free, false },
+   // TODO(bashi): Support mort, base, and jstf tables.
+   { 0, NULL, NULL, NULL, NULL, false },
+ };
+ 
+ bool IsValidVersionTag(uint32_t tag) {
+   return tag == Tag("\x00\x01\x00\x00") ||
+          // OpenType fonts with CFF data have 'OTTO' tag.
+          tag == Tag("OTTO") ||
+@@ -581,22 +592,25 @@ bool ProcessGeneric(ots::OpenTypeFile *h
+ }  // namespace
+ 
+ namespace ots {
+ 
+ void DisableDebugOutput() {
+   g_debug_output = false;
+ }
+ 
+-bool Process(OTSStream *output, const uint8_t *data, size_t length) {
++bool Process(OTSStream *output, const uint8_t *data, size_t length,
++             bool preserveGraphite) {
+   OpenTypeFile header;
+   if (length < 4) {
+     return OTS_FAILURE();
+   }
+ 
++  header.preserve_graphite = preserveGraphite;
++
+   bool result;
+   if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
+     result = ProcessWOFF(&header, output, data, length);
+   } else {
+     result = ProcessTTF(&header, output, data, length);
+   }
+ 
+   for (unsigned i = 0; ; ++i) {
+diff --git a/gfx/ots/src/ots.h b/gfx/ots/src/ots.h
+--- a/gfx/ots/src/ots.h
++++ b/gfx/ots/src/ots.h
+@@ -178,17 +178,22 @@ class Buffer {
+   F(maxp, MAXP) \
+   F(name, NAME) \
+   F(os2, OS2) \
+   F(post, POST) \
+   F(prep, PREP) \
+   F(vdmx, VDMX) \
+   F(vorg, VORG) \
+   F(vhea, VHEA) \
+-  F(vmtx, VMTX)
++  F(vmtx, VMTX) \
++  F(silf, SILF) \
++  F(sill, SILL) \
++  F(glat, GLAT) \
++  F(gloc, GLOC) \
++  F(feat, FEAT)
+ 
+ #define F(name, capname) struct OpenType##capname;
+ FOR_EACH_TABLE_TYPE
+ #undef F
+ 
+ struct OpenTypeFile {
+   OpenTypeFile() {
+ #define F(name, capname) name = NULL;
+@@ -197,16 +202,20 @@ struct OpenTypeFile {
+   }
+ 
+   uint32_t version;
+   uint16_t num_tables;
+   uint16_t search_range;
+   uint16_t entry_selector;
+   uint16_t range_shift;
+ 
++  // This is used to tell the relevant parsers whether to preserve the
++  // Graphite layout tables (currently _without_ any checking)
++  bool preserve_graphite;
++
+ #define F(name, capname) OpenType##capname *name;
+ FOR_EACH_TABLE_TYPE
+ #undef F
+ };
+ 
+ #define F(name, capname) \
+ bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
+ bool ots_##name##_should_serialise(OpenTypeFile *f); \
new file mode 100644
--- /dev/null
+++ b/gfx/ots/ots-visibility.patch
@@ -0,0 +1,60 @@
+diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
+--- a/gfx/ots/include/opentype-sanitiser.h
++++ b/gfx/ots/include/opentype-sanitiser.h
+@@ -1,15 +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 OPENTYPE_SANITISER_H_
+ #define OPENTYPE_SANITISER_H_
+ 
++#if defined(_WIN32) || defined(__CYGWIN__)
++  #define OTS_DLL_IMPORT __declspec(dllimport)
++  #define OTS_DLL_EXPORT __declspec(dllexport)
++#else
++  #if __GNUC__ >= 4
++    #define OTS_DLL_IMPORT __attribute__((visibility ("default")))
++    #define OTS_DLL_EXPORT __attribute__((visibility ("default")))
++  #endif
++#endif
++
++#ifdef OTS_DLL
++  #ifdef OTS_DLL_EXPORTS
++    #define OTS_API OTS_DLL_EXPORT
++  #else
++    #define OTS_API OTS_DLL_IMPORT
++  #endif
++#else
++  #define OTS_API
++#endif
++
+ #if defined(_WIN32)
+ #include <stdlib.h>
+ typedef signed char int8_t;
+ typedef unsigned char uint8_t;
+ typedef short int16_t;
+ typedef unsigned short uint16_t;
+ typedef int int32_t;
+ typedef unsigned int uint32_t;
+@@ -178,18 +198,18 @@ class OTSStream {
+ // Process a given OpenType file and write out a sanitised version
+ //   output: a pointer to an object implementing the OTSStream interface. The
+ //     sanitisied output will be written to this. In the even of a failure,
+ //     partial output may have been written.
+ //   input: the OpenType file
+ //   length: the size, in bytes, of |input|
+ //   preserve_graphite_tables: whether to preserve Graphite Layout tables
+ // -----------------------------------------------------------------------------
+-bool Process(OTSStream *output, const uint8_t *input, size_t length,
+-             bool preserve_graphite_tables = false);
++bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length,
++                     bool preserve_graphite_tables = false);
+ 
+ // Force to disable debug output even when the library is compiled with
+ // -DOTS_DEBUG.
+ void DisableDebugOutput();
+ 
+ }  // namespace ots
+ 
+ #endif  // OPENTYPE_SANITISER_H_
--- a/gfx/ots/src/cff.cc
+++ b/gfx/ots/src/cff.cc
@@ -1,37 +1,43 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "cff.h"
 
 #include <cstring>
 #include <utility>  // std::pair
 #include <vector>
 
 #include "cff_type2_charstring.h"
 
 // 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
+// http://www.microsoft.com/typography/otspec/cff.htm
+// http://www.microsoft.com/typography/otspec/cffspec.htm
 
 namespace {
 
 enum DICT_OPERAND_TYPE {
   DICT_OPERAND_INTEGER,
   DICT_OPERAND_REAL,
   DICT_OPERATOR,
 };
 
 enum DICT_DATA_TYPE {
   DICT_DATA_TOPLEVEL,
   DICT_DATA_FDARRAY,
 };
 
+enum FONT_FORMAT {
+  FORMAT_UNKNOWN,
+  FORMAT_CID_KEYED,
+  FORMAT_OTHER,  // Including synthetic fonts
+};
+
 // see Appendix. A
 const size_t kNStdString = 390;
 
 bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
   if (off_size > 4) {
     return OTS_FAILURE();
   }
 
@@ -412,17 +418,17 @@ bool ParsePrivateDictData(
         ots::Buffer table(data, table_length);
         table.set_offset(operands.back().first + offset);
         ots::CFFIndex *local_subrs_index = NULL;
         if (type == DICT_DATA_FDARRAY) {
           if (out_cff->local_subrs_per_font.empty()) {
             return OTS_FAILURE();  // not reached.
           }
           local_subrs_index = out_cff->local_subrs_per_font.back();
-        } else if (type == DICT_DATA_TOPLEVEL) {
+        } else { // type == DICT_DATA_TOPLEVEL
           if (out_cff->local_subrs) {
             return OTS_FAILURE();  // two or more local_subrs?
           }
           local_subrs_index = new ots::CFFIndex;
           out_cff->local_subrs = local_subrs_index;
         }
         if (!ParseIndex(&table, local_subrs_index)) {
           return OTS_FAILURE();
@@ -459,16 +465,17 @@ bool ParseDictData(const uint8_t *data, 
     if (type == DICT_DATA_TOPLEVEL) {
       out_cff->char_strings_array.push_back(new ots::CFFIndex);
     }
     size_t dict_length = index.offsets[i] - index.offsets[i - 1];
     ots::Buffer table(data + index.offsets[i - 1], dict_length);
 
     std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
 
+    FONT_FORMAT font_format = FORMAT_UNKNOWN;
     bool have_ros = false;
     size_t glyphs = 0;
     size_t charset_offset = 0;
 
     while (table.offset() < dict_length) {
       if (!ParseDictDataReadNext(&table, &operands)) {
         return OTS_FAILURE();
       }
@@ -517,24 +524,31 @@ bool ParseDictData(const uint8_t *data, 
         // number
         case 13:  // UniqueID
         case (12U << 8) + 2:   // ItalicAngle
         case (12U << 8) + 3:   // UnderlinePosition
         case (12U << 8) + 4:   // UnderlineThickness
         case (12U << 8) + 5:   // PaintType
         case (12U << 8) + 8:   // StrokeWidth
         case (12U << 8) + 20:  // SyntheticBase
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          break;
         case (12U << 8) + 31:  // CIDFontVersion
         case (12U << 8) + 32:  // CIDFontRevision
         case (12U << 8) + 33:  // CIDFontType
         case (12U << 8) + 34:  // CIDCount
         case (12U << 8) + 35:  // UIDBase
           if (operands.size() != 1) {
             return OTS_FAILURE();
           }
+          if (font_format != FORMAT_CID_KEYED) {
+            return OTS_FAILURE();
+          }
           break;
         case (12U << 8) + 6:   // CharstringType
           if (operands.size() != 1) {
             return OTS_FAILURE();
           }
           if(operands.back().second != DICT_OPERAND_INTEGER) {
             return OTS_FAILURE();
           }
@@ -775,19 +789,20 @@ bool ParseDictData(const uint8_t *data, 
                                     type, out_cff)) {
             return OTS_FAILURE();
           }
           break;
         }
 
         // ROS
         case (12U << 8) + 30:
-          if (type != DICT_DATA_TOPLEVEL) {
+          if (font_format != FORMAT_UNKNOWN) {
             return OTS_FAILURE();
           }
+          font_format = FORMAT_CID_KEYED;
           if (operands.size() != 3) {
             return OTS_FAILURE();
           }
           // check SIDs
           operands.pop_back();  // ignore the first number.
           if (!CheckSid(operands.back(), sid_max)) {
             return OTS_FAILURE();
           }
@@ -800,16 +815,20 @@ bool ParseDictData(const uint8_t *data, 
           }
           have_ros = true;
           break;
 
         default:
           return OTS_FAILURE();
       }
       operands.clear();
+
+      if (font_format == FORMAT_UNKNOWN) {
+        font_format = FORMAT_OTHER;
+      }
     }
 
     // parse "13. Charsets"
     if (charset_offset) {
       ots::Buffer table(data, table_length);
       table.set_offset(charset_offset);
       uint8_t format = 0;
       if (!table.ReadU8(&format)) {
--- a/gfx/ots/src/ots.cc
+++ b/gfx/ots/src/ots.cc
@@ -44,16 +44,22 @@ bool CheckTag(uint32_t tag_value) {
     if (check < 32 || check > 126) {
       return false;  // non-ASCII character found.
     }
     tag_value >>= 8;
   }
   return true;
 }
 
+uint32_t Tag(const char *tag_str) {	
+  uint32_t ret;	
+  std::memcpy(&ret, tag_str, 4);	
+  return ret;	
+}
+
 struct OutputTable {
   uint32_t tag;
   size_t offset;
   size_t length;
   uint32_t chksum;
 
   static bool SortByTag(const OutputTable& a, const OutputTable& b) {
     const uint32_t atag = ntohl(a.tag);
@@ -76,102 +82,98 @@ struct Arena {
     hunks_.push_back(p);
     return p;
   }
 
  private:
   std::vector<uint8_t*> hunks_;
 };
 
-// Use a macro instead of a function because gcc 4.4.3 creates static
-// initializers in that case. Note this macro assumes a little-endian system.
-#define TAG(a, b, c, d) (a | (b << 8) | (c << 16) | (d << 24))
-
 const struct {
-  uint32_t tag;
+  const char* tag;
   bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
   bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
   bool (*should_serialise)(ots::OpenTypeFile *file);
   void (*free)(ots::OpenTypeFile *file);
   bool required;
 } table_parsers[] = {
-  { TAG('m', 'a', 'x', 'p'), ots::ots_maxp_parse, ots::ots_maxp_serialise,
+  { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise,
     ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
-  { TAG('h', 'e', 'a', 'd'), ots::ots_head_parse, ots::ots_head_serialise,
+  { "head", ots::ots_head_parse, ots::ots_head_serialise,
     ots::ots_head_should_serialise, ots::ots_head_free, true },
-  { TAG('O', 'S', '/', '2'), ots::ots_os2_parse, ots::ots_os2_serialise,
+  { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise,
     ots::ots_os2_should_serialise, ots::ots_os2_free, true },
-  { TAG('c', 'm', 'a', 'p'), ots::ots_cmap_parse, ots::ots_cmap_serialise,
+  { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise,
     ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
-  { TAG('h', 'h', 'e', 'a'), ots::ots_hhea_parse, ots::ots_hhea_serialise,
+  { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise,
     ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
-  { TAG('h', 'm', 't', 'x'), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
+  { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
     ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
-  { TAG('n', 'a', 'm', 'e'), ots::ots_name_parse, ots::ots_name_serialise,
+  { "name", ots::ots_name_parse, ots::ots_name_serialise,
     ots::ots_name_should_serialise, ots::ots_name_free, true },
-  { TAG('p', 'o', 's', 't'), ots::ots_post_parse, ots::ots_post_serialise,
+  { "post", ots::ots_post_parse, ots::ots_post_serialise,
     ots::ots_post_should_serialise, ots::ots_post_free, true },
-  { TAG('l', 'o', 'c', 'a'), ots::ots_loca_parse, ots::ots_loca_serialise,
+  { "loca", ots::ots_loca_parse, ots::ots_loca_serialise,
     ots::ots_loca_should_serialise, ots::ots_loca_free, false },
-  { TAG('g', 'l', 'y', 'f'), ots::ots_glyf_parse, ots::ots_glyf_serialise,
+  { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise,
     ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
-  { TAG('C', 'F', 'F', ' '), ots::ots_cff_parse, ots::ots_cff_serialise,
+  { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise,
     ots::ots_cff_should_serialise, ots::ots_cff_free, false },
-  { TAG('V', 'D', 'M', 'X'), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
+  { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
     ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
-  { TAG('h', 'd', 'm', 'x'), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
+  { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
     ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
-  { TAG('g', 'a', 's', 'p'), ots::ots_gasp_parse, ots::ots_gasp_serialise,
+  { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise,
     ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
-  { TAG('c', 'v', 't', ' '), ots::ots_cvt_parse, ots::ots_cvt_serialise,
+  { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise,
     ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
-  { TAG('f', 'p', 'g', 'm'), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
+  { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
     ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
-  { TAG('p', 'r', 'e', 'p'), ots::ots_prep_parse, ots::ots_prep_serialise,
+  { "prep", ots::ots_prep_parse, ots::ots_prep_serialise,
     ots::ots_prep_should_serialise, ots::ots_prep_free, false },
-  { TAG('L', 'T', 'S', 'H'), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
+  { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
     ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
-  { TAG('V', 'O', 'R', 'G'), ots::ots_vorg_parse, ots::ots_vorg_serialise,
+  { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise,
     ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
-  { TAG('k', 'e', 'r', 'n'), ots::ots_kern_parse, ots::ots_kern_serialise,
+  { "kern", ots::ots_kern_parse, ots::ots_kern_serialise,
     ots::ots_kern_should_serialise, ots::ots_kern_free, false },
   // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
   // because they could refer GDEF table.
-  { TAG('G', 'D', 'E', 'F'), ots::ots_gdef_parse, ots::ots_gdef_serialise,
+  { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise,
     ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
-  { TAG('G', 'P', 'O', 'S'), ots::ots_gpos_parse, ots::ots_gpos_serialise,
+  { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
     ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
-  { TAG('G', 'S', 'U', 'B'), ots::ots_gsub_parse, ots::ots_gsub_serialise,
+  { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
     ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
-  { TAG('v', 'h', 'e', 'a'), ots::ots_vhea_parse, ots::ots_vhea_serialise,
+  { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
     ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
-  { TAG('v', 'm', 't', 'x'), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+  { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
     ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
   // SILGraphite layout tables - not actually parsed, just copied
-  { TAG('S', 'i', 'l', 'f'), ots::ots_silf_parse, ots::ots_silf_serialise,
+  { "Silf", ots::ots_silf_parse, ots::ots_silf_serialise,
     ots::ots_silf_should_serialise, ots::ots_silf_free, false },
-  { TAG('S', 'i', 'l', 'l'), ots::ots_sill_parse, ots::ots_sill_serialise,
+  { "Sill", ots::ots_sill_parse, ots::ots_sill_serialise,
     ots::ots_sill_should_serialise, ots::ots_sill_free, false },
-  { TAG('G', 'l', 'o', 'c'), ots::ots_gloc_parse, ots::ots_gloc_serialise,
+  { "Gloc", ots::ots_gloc_parse, ots::ots_gloc_serialise,
     ots::ots_gloc_should_serialise, ots::ots_gloc_free, false },
-  { TAG('G', 'l', 'a', 't'), ots::ots_glat_parse, ots::ots_glat_serialise,
+  { "Glat", ots::ots_glat_parse, ots::ots_glat_serialise,
     ots::ots_glat_should_serialise, ots::ots_glat_free, false },
-  { TAG('F', 'e', 'a', 't'), ots::ots_feat_parse, ots::ots_feat_serialise,
+  { "Feat", ots::ots_feat_parse, ots::ots_feat_serialise,
     ots::ots_feat_should_serialise, ots::ots_feat_free, false },
   // TODO(bashi): Support mort, base, and jstf tables.
   { 0, NULL, NULL, NULL, NULL, false },
 };
 
 bool IsValidVersionTag(uint32_t tag) {
-  return tag == TAG('\x00', '\x01', '\x00', '\x00') ||
+  return tag == Tag("\x00\x01\x00\x00") ||
          // OpenType fonts with CFF data have 'OTTO' tag.
-         tag == TAG('O', 'T', 'T', 'O') ||
+         tag == Tag("OTTO") ||
          // Older Mac fonts might have 'true' or 'typ1' tag.
-         tag == TAG('t', 'r', 'u', 'e') ||
-         tag == TAG('t', 'y', 'p', '1');
+         tag == Tag("true") ||
+         tag == Tag("typ1");
 }
 
 bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
                     const uint8_t *data, size_t length,
                     const std::vector<OpenTypeTable>& tables,
                     ots::Buffer& file);
 
 bool ProcessTTF(ots::OpenTypeFile *header,
@@ -259,17 +261,17 @@ bool ProcessWOFF(ots::OpenTypeFile *head
     return OTS_FAILURE();
   }
 
   uint32_t woff_tag;
   if (!file.ReadTag(&woff_tag)) {
     return OTS_FAILURE();
   }
 
-  if (woff_tag != TAG('w', 'O', 'F', 'F')) {
+  if (woff_tag != Tag("wOFF")) {
     return OTS_FAILURE();
   }
 
   if (!file.ReadTag(&header->version)) {
     return OTS_FAILURE();
   }
   if (!IsValidVersionTag(header->version)) {
       return OTS_FAILURE();
@@ -507,17 +509,17 @@ bool ProcessGeneric(ots::OpenTypeFile *h
   }
 
   Arena arena;
 
   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);
+        = table_map.find(Tag(table_parsers[i].tag));
 
     if (it == table_map.end()) {
       if (table_parsers[i].required) {
         return OTS_FAILURE();
       }
       continue;
     }
 
@@ -542,17 +544,17 @@ bool ProcessGeneric(ots::OpenTypeFile *h
 
     if (!table_parsers[i].parse(header, table_data, table_length)) {
       return OTS_FAILURE();
     }
   }
 
   if (header->cff) {
     // font with PostScript glyph
-    if (header->version != TAG('O', 'T', 'T', 'O')) {
+    if (header->version != Tag("OTTO")) {
       return OTS_FAILURE();
     }
     if (header->glyf || header->loca) {
       // mixing outline formats is not recommended
       return OTS_FAILURE();
     }
   } else {
     if (!header->glyf || !header->loca) {
@@ -602,21 +604,22 @@ bool ProcessGeneric(ots::OpenTypeFile *h
       break;
     }
 
     if (!table_parsers[i].should_serialise(header)) {
       continue;
     }
 
     OutputTable out;
-    out.tag = table_parsers[i].tag;
+    uint32_t tag = Tag(table_parsers[i].tag);
+    out.tag = tag;
     out.offset = output->Tell();
 
     output->ResetChecksum();
-    if (table_parsers[i].tag == TAG('h', 'e', 'a', 'd')) {
+    if (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();
     if (end_offset <= out.offset) {