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 94695 7ffffcb45b9402c0b2cb12e8fdd0d8293a7e48fb
parent 94694 7ae630f43357096ea87c534e07ed4da398ea2653
child 94696 dd6c4f6a244814e6c0d80ae625abea6b21f4c000
push id9754
push userjkew@mozilla.com
push dateWed, 23 May 2012 15:59:54 +0000
treeherdermozilla-inbound@7ffffcb45b94 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs747816
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 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) {