gfx/ots/src/ots.cc

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
     2 // Use of this source code is governed by a BSD-style license that can be
     3 // found in the LICENSE file.
     5 #include "ots.h"
     7 #include <sys/types.h>
     8 #include <zlib.h>
    10 #include <algorithm>
    11 #include <cstdlib>
    12 #include <cstring>
    13 #include <limits>
    14 #include <map>
    15 #include <vector>
    17 #ifdef MOZ_OTS_WOFF2
    18 #include "woff2.h"
    19 #endif
    21 // The OpenType Font File
    22 // http://www.microsoft.com/typography/otspec/cmap.htm
    24 namespace {
    26 bool g_debug_output = true;
    27 #ifdef MOZ_OTS_WOFF2
    28 bool g_enable_woff2 = false;
    29 #endif
    31 ots::MessageFunc  g_message_func = NULL;
    32 void             *g_message_user_data = NULL;
    34 ots::TableActionFunc  g_table_action_func = NULL;
    35 void                 *g_table_action_user_data = NULL;
    37 // Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
    38 #define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
    39 #define OTS_FAILURE_MSG_HDR(msg_)      OTS_FAILURE_MSG_(header, msg_)
    42 struct OpenTypeTable {
    43   uint32_t tag;
    44   uint32_t chksum;
    45   uint32_t offset;
    46   uint32_t length;
    47   uint32_t uncompressed_length;
    48 };
    50 bool CheckTag(uint32_t tag_value) {
    51   for (unsigned i = 0; i < 4; ++i) {
    52     const uint32_t check = tag_value & 0xff;
    53     if (check < 32 || check > 126) {
    54       return false;  // non-ASCII character found.
    55     }
    56     tag_value >>= 8;
    57   }
    58   return true;
    59 }
    61 uint32_t Tag(const char *tag_str) {	
    62   uint32_t ret;	
    63   std::memcpy(&ret, tag_str, 4);	
    64   return ret;	
    65 }
    67 struct OutputTable {
    68   uint32_t tag;
    69   size_t offset;
    70   size_t length;
    71   uint32_t chksum;
    73   static bool SortByTag(const OutputTable& a, const OutputTable& b) {
    74     const uint32_t atag = ntohl(a.tag);
    75     const uint32_t btag = ntohl(b.tag);
    76     return atag < btag;
    77   }
    78 };
    80 struct Arena {
    81  public:
    82   ~Arena() {
    83     for (std::vector<uint8_t*>::iterator
    84          i = hunks_.begin(); i != hunks_.end(); ++i) {
    85       delete[] *i;
    86     }
    87   }
    89   uint8_t* Allocate(size_t length) {
    90     uint8_t* p = new uint8_t[length];
    91     hunks_.push_back(p);
    92     return p;
    93   }
    95  private:
    96   std::vector<uint8_t*> hunks_;
    97 };
    99 const struct {
   100   const char* tag;
   101   bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
   102   bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
   103   bool (*should_serialise)(ots::OpenTypeFile *file);
   104   void (*free)(ots::OpenTypeFile *file);
   105   bool required;
   106 } table_parsers[] = {
   107   { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise,
   108     ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
   109   { "head", ots::ots_head_parse, ots::ots_head_serialise,
   110     ots::ots_head_should_serialise, ots::ots_head_free, true },
   111   { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise,
   112     ots::ots_os2_should_serialise, ots::ots_os2_free, true },
   113   { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise,
   114     ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
   115   { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise,
   116     ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
   117   { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
   118     ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
   119   { "name", ots::ots_name_parse, ots::ots_name_serialise,
   120     ots::ots_name_should_serialise, ots::ots_name_free, true },
   121   { "post", ots::ots_post_parse, ots::ots_post_serialise,
   122     ots::ots_post_should_serialise, ots::ots_post_free, true },
   123   { "loca", ots::ots_loca_parse, ots::ots_loca_serialise,
   124     ots::ots_loca_should_serialise, ots::ots_loca_free, false },
   125   { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise,
   126     ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
   127   { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise,
   128     ots::ots_cff_should_serialise, ots::ots_cff_free, false },
   129   { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
   130     ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
   131   { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
   132     ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
   133   { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise,
   134     ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
   135   { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise,
   136     ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
   137   { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
   138     ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
   139   { "prep", ots::ots_prep_parse, ots::ots_prep_serialise,
   140     ots::ots_prep_should_serialise, ots::ots_prep_free, false },
   141   { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
   142     ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
   143   { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise,
   144     ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
   145   { "kern", ots::ots_kern_parse, ots::ots_kern_serialise,
   146     ots::ots_kern_should_serialise, ots::ots_kern_free, false },
   147   // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
   148   // because they could refer GDEF table.
   149   { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise,
   150     ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
   151   { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
   152     ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
   153   { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
   154     ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
   155   { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
   156     ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
   157   { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
   158     ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
   159   { "MATH", ots::ots_math_parse, ots::ots_math_serialise,
   160     ots::ots_math_should_serialise, ots::ots_math_free, false },
   161   // TODO(bashi): Support mort, base, and jstf tables.
   162   { 0, NULL, NULL, NULL, NULL, false },
   163 };
   165 bool ProcessGeneric(ots::OpenTypeFile *header,
   166                     uint32_t signature,
   167                     ots::OTSStream *output,
   168                     const uint8_t *data, size_t length,
   169                     const std::vector<OpenTypeTable>& tables,
   170                     ots::Buffer& file);
   172 bool ProcessTTF(ots::OpenTypeFile *header,
   173                 ots::OTSStream *output, const uint8_t *data, size_t length) {
   174   ots::Buffer file(data, length);
   176   // we disallow all files > 1GB in size for sanity.
   177   if (length > 1024 * 1024 * 1024) {
   178     return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
   179   }
   181   if (!file.ReadTag(&header->version)) {
   182     return OTS_FAILURE_MSG_HDR("error reading version tag");
   183   }
   184   if (!ots::IsValidVersionTag(header->version)) {
   185       return OTS_FAILURE_MSG_HDR("invalid version tag");
   186   }
   188   if (!file.ReadU16(&header->num_tables) ||
   189       !file.ReadU16(&header->search_range) ||
   190       !file.ReadU16(&header->entry_selector) ||
   191       !file.ReadU16(&header->range_shift)) {
   192     return OTS_FAILURE_MSG_HDR("error reading table directory search header");
   193   }
   195   // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
   196   // overflow num_tables is, at most, 2^16 / 16 = 2^12
   197   if (header->num_tables >= 4096 || header->num_tables < 1) {
   198     return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
   199   }
   201   unsigned max_pow2 = 0;
   202   while (1u << (max_pow2 + 1) <= header->num_tables) {
   203     max_pow2++;
   204   }
   205   const uint16_t expected_search_range = (1u << max_pow2) << 4;
   207   // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
   208   // http://www.princexml.com/fonts/ have unexpected search_range value.
   209   if (header->search_range != expected_search_range) {
   210     OTS_WARNING("bad search range");
   211     header->search_range = expected_search_range;  // Fix the value.
   212   }
   214   // entry_selector is Log2(maximum power of 2 <= numTables)
   215   if (header->entry_selector != max_pow2) {
   216     return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory");
   217   }
   219   // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
   220   // doesn't over flow because we range checked it above. Also, we know that
   221   // it's > header->search_range by construction of search_range.
   222   const uint32_t expected_range_shift
   223       = 16 * header->num_tables - header->search_range;
   224   if (header->range_shift != expected_range_shift) {
   225     OTS_WARNING("bad range shift");
   226     header->range_shift = expected_range_shift;  // the same as above.
   227   }
   229   // Next up is the list of tables.
   230   std::vector<OpenTypeTable> tables;
   232   for (unsigned i = 0; i < header->num_tables; ++i) {
   233     OpenTypeTable table;
   234     if (!file.ReadTag(&table.tag) ||
   235         !file.ReadU32(&table.chksum) ||
   236         !file.ReadU32(&table.offset) ||
   237         !file.ReadU32(&table.length)) {
   238       return OTS_FAILURE_MSG_HDR("error reading table directory");
   239     }
   241     table.uncompressed_length = table.length;
   242     tables.push_back(table);
   243   }
   245   return ProcessGeneric(header, header->version, output, data, length,
   246                         tables, file);
   247 }
   249 bool ProcessWOFF(ots::OpenTypeFile *header,
   250                  ots::OTSStream *output, const uint8_t *data, size_t length) {
   251   ots::Buffer file(data, length);
   253   // we disallow all files > 1GB in size for sanity.
   254   if (length > 1024 * 1024 * 1024) {
   255     return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
   256   }
   258   uint32_t woff_tag;
   259   if (!file.ReadTag(&woff_tag)) {
   260     return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
   261   }
   263   if (woff_tag != Tag("wOFF")) {
   264     return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
   265   }
   267   if (!file.ReadTag(&header->version)) {
   268     return OTS_FAILURE_MSG_HDR("error reading version tag");
   269   }
   270   if (!ots::IsValidVersionTag(header->version)) {
   271     return OTS_FAILURE_MSG_HDR("invalid version tag");
   272   }
   274   header->search_range = 0;
   275   header->entry_selector = 0;
   276   header->range_shift = 0;
   278   uint32_t reported_length;
   279   if (!file.ReadU32(&reported_length) || length != reported_length) {
   280     return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
   281   }
   283   if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
   284     return OTS_FAILURE_MSG_HDR("error reading number of tables");
   285   }
   287   uint16_t reserved_value;
   288   if (!file.ReadU16(&reserved_value) || reserved_value) {
   289     return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
   290   }
   292   uint32_t reported_total_sfnt_size;
   293   if (!file.ReadU32(&reported_total_sfnt_size)) {
   294     return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
   295   }
   297   // We don't care about these fields of the header:
   298   //   uint16_t major_version, minor_version
   299   if (!file.Skip(2 * 2)) {
   300     return OTS_FAILURE_MSG_HDR("error skipping WOFF header fields");
   301   }
   303   // Checks metadata block size.
   304   uint32_t meta_offset;
   305   uint32_t meta_length;
   306   uint32_t meta_length_orig;
   307   if (!file.ReadU32(&meta_offset) ||
   308       !file.ReadU32(&meta_length) ||
   309       !file.ReadU32(&meta_length_orig)) {
   310     return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
   311   }
   312   if (meta_offset) {
   313     if (meta_offset >= length || length - meta_offset < meta_length) {
   314       return OTS_FAILURE_MSG_HDR("invalid metadata block location/size");
   315     }
   316   }
   318   // Checks private data block size.
   319   uint32_t priv_offset;
   320   uint32_t priv_length;
   321   if (!file.ReadU32(&priv_offset) ||
   322       !file.ReadU32(&priv_length)) {
   323     return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
   324   }
   325   if (priv_offset) {
   326     if (priv_offset >= length || length - priv_offset < priv_length) {
   327       return OTS_FAILURE_MSG_HDR("invalid private block location/size");
   328     }
   329   }
   331   // Next up is the list of tables.
   332   std::vector<OpenTypeTable> tables;
   334   uint32_t first_index = 0;
   335   uint32_t last_index = 0;
   336   // Size of sfnt header plus size of table records.
   337   uint64_t total_sfnt_size = 12 + 16 * header->num_tables;
   338   for (unsigned i = 0; i < header->num_tables; ++i) {
   339     OpenTypeTable table;
   340     if (!file.ReadTag(&table.tag) ||
   341         !file.ReadU32(&table.offset) ||
   342         !file.ReadU32(&table.length) ||
   343         !file.ReadU32(&table.uncompressed_length) ||
   344         !file.ReadU32(&table.chksum)) {
   345       return OTS_FAILURE_MSG_HDR("error reading table directory");
   346     }
   348     total_sfnt_size += ots::Round4(table.uncompressed_length);
   349     if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
   350       return OTS_FAILURE_MSG_HDR("sfnt size overflow");
   351     }
   352     tables.push_back(table);
   353     if (i == 0 || tables[first_index].offset > table.offset)
   354       first_index = i;
   355     if (i == 0 || tables[last_index].offset < table.offset)
   356       last_index = i;
   357   }
   359   if (reported_total_sfnt_size != total_sfnt_size) {
   360     return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
   361   }
   363   // Table data must follow immediately after the header.
   364   if (tables[first_index].offset != ots::Round4(file.offset())) {
   365     return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
   366   }
   368   if (tables[last_index].offset >= length ||
   369       length - tables[last_index].offset < tables[last_index].length) {
   370     return OTS_FAILURE_MSG_HDR("invalid table location/size");
   371   }
   372   // Blocks must follow immediately after the previous block.
   373   // (Except for padding with a maximum of three null bytes)
   374   uint64_t block_end = ots::Round4(
   375       static_cast<uint64_t>(tables[last_index].offset) +
   376       static_cast<uint64_t>(tables[last_index].length));
   377   if (block_end > std::numeric_limits<uint32_t>::max()) {
   378     return OTS_FAILURE_MSG_HDR("invalid table location/size");
   379   }
   380   if (meta_offset) {
   381     if (block_end != meta_offset) {
   382       return OTS_FAILURE_MSG_HDR("invalid metadata block location");
   383     }
   384     block_end = ots::Round4(static_cast<uint64_t>(meta_offset) +
   385                             static_cast<uint64_t>(meta_length));
   386     if (block_end > std::numeric_limits<uint32_t>::max()) {
   387       return OTS_FAILURE_MSG_HDR("invalid metadata block size");
   388     }
   389   }
   390   if (priv_offset) {
   391     if (block_end != priv_offset) {
   392       return OTS_FAILURE_MSG_HDR("invalid private block location");
   393     }
   394     block_end = ots::Round4(static_cast<uint64_t>(priv_offset) +
   395                             static_cast<uint64_t>(priv_length));
   396     if (block_end > std::numeric_limits<uint32_t>::max()) {
   397       return OTS_FAILURE_MSG_HDR("invalid private block size");
   398     }
   399   }
   400   if (block_end != ots::Round4(length)) {
   401     return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
   402   }
   404   return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
   405 }
   407 #ifdef MOZ_OTS_WOFF2
   408 bool ProcessWOFF2(ots::OpenTypeFile *header,
   409                   ots::OTSStream *output, const uint8_t *data, size_t length) {
   410   size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
   411   if (decompressed_size == 0) {
   412     return OTS_FAILURE();
   413   }
   414   // decompressed font must be <= 30MB
   415   if (decompressed_size > 30 * 1024 * 1024) {
   416     return OTS_FAILURE();
   417   }
   419   std::vector<uint8_t> decompressed_buffer(decompressed_size);
   420   if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size,
   421                               data, length)) {
   422     return OTS_FAILURE();
   423   }
   424   return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
   425 }
   426 #endif
   428 ots::TableAction GetTableAction(uint32_t tag) {
   429   ots::TableAction action = ots::TABLE_ACTION_DEFAULT;
   431   if (g_table_action_func != NULL) {
   432     action = g_table_action_func(htonl(tag), g_table_action_user_data);
   433   }
   435   if (action == ots::TABLE_ACTION_DEFAULT) {
   436     action = ots::TABLE_ACTION_DROP;
   438     for (unsigned i = 0; ; ++i) {
   439       if (table_parsers[i].parse == NULL) break;
   441       if (Tag(table_parsers[i].tag) == tag) {
   442         action = ots::TABLE_ACTION_SANITIZE;
   443         break;
   444       }
   445     }
   446   }
   448   assert(action != ots::TABLE_ACTION_DEFAULT); // Should never return this.
   449   return action;
   450 }
   452 bool GetTableData(const uint8_t *data,
   453                   const OpenTypeTable table,
   454                   Arena *arena,
   455                   size_t *table_length,
   456                   const uint8_t **table_data) {
   457   if (table.uncompressed_length != table.length) {
   458     // Compressed table. Need to uncompress into memory first.
   459     *table_length = table.uncompressed_length;
   460     *table_data = (*arena).Allocate(*table_length);
   461     uLongf dest_len = *table_length;
   462     int r = uncompress((Bytef*) *table_data, &dest_len,
   463                        data + table.offset, table.length);
   464     if (r != Z_OK || dest_len != *table_length) {
   465       return false;
   466     }
   467   } else {
   468     // Uncompressed table. We can process directly from memory.
   469     *table_data = data + table.offset;
   470     *table_length = table.length;
   471   }
   473   return true;
   474 }
   476 bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature,
   477                     ots::OTSStream *output,
   478                     const uint8_t *data, size_t length,
   479                     const std::vector<OpenTypeTable>& tables,
   480                     ots::Buffer& file) {
   481   const size_t data_offset = file.offset();
   483   uint32_t uncompressed_sum = 0;
   485   for (unsigned i = 0; i < header->num_tables; ++i) {
   486     // the tables must be sorted by tag (when taken as big-endian numbers).
   487     // This also remove the possibility of duplicate tables.
   488     if (i) {
   489       const uint32_t this_tag = ntohl(tables[i].tag);
   490       const uint32_t prev_tag = ntohl(tables[i - 1].tag);
   491       if (this_tag <= prev_tag) {
   492         return OTS_FAILURE_MSG_HDR("table directory not correctly ordered");
   493       }
   494     }
   496     // all tag names must be built from printable ASCII characters
   497     if (!CheckTag(tables[i].tag)) {
   498       return OTS_FAILURE_MSG_TAG("invalid table tag", &tables[i].tag);
   499     }
   501     // tables must be 4-byte aligned
   502     if (tables[i].offset & 3) {
   503       return OTS_FAILURE_MSG_TAG("misaligned table", &tables[i].tag);
   504     }
   506     // and must be within the file
   507     if (tables[i].offset < data_offset || tables[i].offset >= length) {
   508       return OTS_FAILURE_MSG_TAG("invalid table offset", &tables[i].tag);
   509     }
   510     // disallow all tables with a zero length
   511     if (tables[i].length < 1) {
   512       // Note: malayalam.ttf has zero length CVT table...
   513       return OTS_FAILURE_MSG_TAG("zero-length table", &tables[i].tag);
   514     }
   515     // disallow all tables with a length > 1GB
   516     if (tables[i].length > 1024 * 1024 * 1024) {
   517       return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", &tables[i].tag);
   518     }
   519     // disallow tables where the uncompressed size is < the compressed size.
   520     if (tables[i].uncompressed_length < tables[i].length) {
   521       return OTS_FAILURE_MSG_TAG("invalid compressed table", &tables[i].tag);
   522     }
   523     if (tables[i].uncompressed_length > tables[i].length) {
   524       // We'll probably be decompressing this table.
   526       // disallow all tables which uncompress to > 30 MB
   527       if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
   528         return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", &tables[i].tag);
   529       }
   530       if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
   531         return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", &tables[i].tag);
   532       }
   534       uncompressed_sum += tables[i].uncompressed_length;
   535     }
   536     // since we required that the file be < 1GB in length, and that the table
   537     // length is < 1GB, the following addtion doesn't overflow
   538     uint32_t end_byte = tables[i].offset + tables[i].length;
   539     // Tables in the WOFF file must be aligned 4-byte boundary.
   540     if (signature == Tag("wOFF")) {
   541         end_byte = ots::Round4(end_byte);
   542     }
   543     if (!end_byte || end_byte > length) {
   544       return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables[i].tag);
   545     }
   546   }
   548   // All decompressed tables uncompressed must be <= 30MB.
   549   if (uncompressed_sum > 30 * 1024 * 1024) {
   550     return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
   551   }
   553   std::map<uint32_t, OpenTypeTable> table_map;
   554   for (unsigned i = 0; i < header->num_tables; ++i) {
   555     table_map[tables[i].tag] = tables[i];
   556   }
   558   // check that the tables are not overlapping.
   559   std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
   560   for (unsigned i = 0; i < header->num_tables; ++i) {
   561     overlap_checker.push_back(
   562         std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */));
   563     overlap_checker.push_back(
   564         std::make_pair(tables[i].offset + tables[i].length,
   565                        static_cast<uint8_t>(0) /* end */));
   566   }
   567   std::sort(overlap_checker.begin(), overlap_checker.end());
   568   int overlap_count = 0;
   569   for (unsigned i = 0; i < overlap_checker.size(); ++i) {
   570     overlap_count += (overlap_checker[i].second ? 1 : -1);
   571     if (overlap_count > 1) {
   572       return OTS_FAILURE_MSG_HDR("overlapping tables");
   573     }
   574   }
   576   Arena arena;
   578   for (unsigned i = 0; ; ++i) {
   579     if (table_parsers[i].parse == NULL) break;
   581     const std::map<uint32_t, OpenTypeTable>::const_iterator it
   582         = table_map.find(Tag(table_parsers[i].tag));
   584     ots::TableAction action = GetTableAction(Tag(table_parsers[i].tag));
   585     if (it == table_map.end()) {
   586       if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
   587         return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
   588       }
   589       continue;
   590     }
   592     const uint8_t* table_data;
   593     size_t table_length;
   595     if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
   596       return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
   597     }
   599     if (action == ots::TABLE_ACTION_SANITIZE &&
   600         !table_parsers[i].parse(header, table_data, table_length)) {
   601       // TODO: parsers should generate specific messages detailing the failure;
   602       // once those are all added, we won't need a generic failure message here
   603       return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers[i].tag);
   604     }
   605   }
   607   if (header->cff) {
   608     // font with PostScript glyph
   609     if (header->version != Tag("OTTO")) {
   610       return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
   611     }
   612     if (header->glyf || header->loca) {
   613       // mixing outline formats is not recommended
   614       return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
   615     }
   616   } else {
   617     if (!header->glyf || !header->loca) {
   618       // No TrueType glyph found.
   619       // Note: bitmap-only fonts are not supported.
   620       return OTS_FAILURE_MSG_HDR("neither PS nor TT glyphs present");
   621     }
   622   }
   624   unsigned num_output_tables = 0;
   625   for (unsigned i = 0; ; ++i) {
   626     if (table_parsers[i].parse == NULL) {
   627       break;
   628     }
   630     if (table_parsers[i].should_serialise(header)) {
   631       num_output_tables++;
   632     }
   633   }
   635   for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
   636        it != table_map.end(); ++it) {
   637     ots::TableAction action = GetTableAction(it->first);
   638     if (action == ots::TABLE_ACTION_PASSTHRU) {
   639       num_output_tables++;
   640     }
   641   }
   643   unsigned max_pow2 = 0;
   644   while (1u << (max_pow2 + 1) <= num_output_tables) {
   645     max_pow2++;
   646   }
   647   const uint16_t output_search_range = (1u << max_pow2) << 4;
   649   // most of the errors here are highly unlikely - they'd only occur if the
   650   // output stream returns a failure, e.g. lack of space to write
   651   output->ResetChecksum();
   652   if (!output->WriteTag(header->version) ||
   653       !output->WriteU16(num_output_tables) ||
   654       !output->WriteU16(output_search_range) ||
   655       !output->WriteU16(max_pow2) ||
   656       !output->WriteU16((num_output_tables << 4) - output_search_range)) {
   657     return OTS_FAILURE_MSG_HDR("error writing output");
   658   }
   659   const uint32_t offset_table_chksum = output->chksum();
   661   const size_t table_record_offset = output->Tell();
   662   if (!output->Pad(16 * num_output_tables)) {
   663     return OTS_FAILURE_MSG_HDR("error writing output");
   664   }
   666   std::vector<OutputTable> out_tables;
   668   size_t head_table_offset = 0;
   669   for (unsigned i = 0; ; ++i) {
   670     if (table_parsers[i].parse == NULL) {
   671       break;
   672     }
   674     if (!table_parsers[i].should_serialise(header)) {
   675       continue;
   676     }
   678     OutputTable out;
   679     uint32_t tag = Tag(table_parsers[i].tag);
   680     out.tag = tag;
   681     out.offset = output->Tell();
   683     output->ResetChecksum();
   684     if (tag == Tag("head")) {
   685       head_table_offset = out.offset;
   686     }
   687     if (!table_parsers[i].serialise(output, header)) {
   688       return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
   689     }
   691     const size_t end_offset = output->Tell();
   692     if (end_offset <= out.offset) {
   693       // paranoid check. |end_offset| is supposed to be greater than the offset,
   694       // as long as the Tell() interface is implemented correctly.
   695       return OTS_FAILURE_MSG_HDR("error writing output");
   696     }
   697     out.length = end_offset - out.offset;
   699     // align tables to four bytes
   700     if (!output->Pad((4 - (end_offset & 3)) % 4)) {
   701       return OTS_FAILURE_MSG_HDR("error writing output");
   702     }
   703     out.chksum = output->chksum();
   704     out_tables.push_back(out);
   705   }
   707   for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
   708        it != table_map.end(); ++it) {
   709     ots::TableAction action = GetTableAction(it->first);
   710     if (action == ots::TABLE_ACTION_PASSTHRU) {
   711       OutputTable out;
   712       out.tag = it->second.tag;
   713       out.offset = output->Tell();
   715       output->ResetChecksum();
   716       if (it->second.tag == Tag("head")) {
   717         head_table_offset = out.offset;
   718       }
   720       const uint8_t* table_data;
   721       size_t table_length;
   723       if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
   724         return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
   725       }
   727       if (!output->Write(table_data, table_length)) {
   728         return OTS_FAILURE_MSG_HDR("Failed to serialize table");
   729       }
   731       const size_t end_offset = output->Tell();
   732       if (end_offset <= out.offset) {
   733         // paranoid check. |end_offset| is supposed to be greater than the offset,
   734         // as long as the Tell() interface is implemented correctly.
   735         return OTS_FAILURE_MSG_HDR("error writing output");
   736       }
   737       out.length = end_offset - out.offset;
   739       // align tables to four bytes
   740       if (!output->Pad((4 - (end_offset & 3)) % 4)) {
   741         return OTS_FAILURE_MSG_HDR("error writing output");
   742       }
   743       out.chksum = output->chksum();
   744       out_tables.push_back(out);
   745     }
   746   }
   748   const size_t end_of_file = output->Tell();
   750   // Need to sort the output tables for inclusion in the file
   751   std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
   752   if (!output->Seek(table_record_offset)) {
   753     return OTS_FAILURE_MSG_HDR("error writing output");
   754   }
   756   output->ResetChecksum();
   757   uint32_t tables_chksum = 0;
   758   for (unsigned i = 0; i < out_tables.size(); ++i) {
   759     if (!output->WriteTag(out_tables[i].tag) ||
   760         !output->WriteU32(out_tables[i].chksum) ||
   761         !output->WriteU32(out_tables[i].offset) ||
   762         !output->WriteU32(out_tables[i].length)) {
   763       return OTS_FAILURE_MSG_HDR("error writing output");
   764     }
   765     tables_chksum += out_tables[i].chksum;
   766   }
   767   const uint32_t table_record_chksum = output->chksum();
   769   // http://www.microsoft.com/typography/otspec/otff.htm
   770   const uint32_t file_chksum
   771       = offset_table_chksum + tables_chksum + table_record_chksum;
   772   const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum;
   774   // seek into the 'head' table and write in the checksum magic value
   775   if (!head_table_offset) {
   776     return OTS_FAILURE_MSG_HDR("internal error!");
   777   }
   778   if (!output->Seek(head_table_offset + 8)) {
   779     return OTS_FAILURE_MSG_HDR("error writing output");
   780   }
   781   if (!output->WriteU32(chksum_magic)) {
   782     return OTS_FAILURE_MSG_HDR("error writing output");
   783   }
   785   if (!output->Seek(end_of_file)) {
   786     return OTS_FAILURE_MSG_HDR("error writing output");
   787   }
   789   return true;
   790 }
   792 }  // namespace
   794 namespace ots {
   796 bool IsValidVersionTag(uint32_t tag) {
   797   return tag == Tag("\x00\x01\x00\x00") ||
   798          // OpenType fonts with CFF data have 'OTTO' tag.
   799          tag == Tag("OTTO") ||
   800          // Older Mac fonts might have 'true' or 'typ1' tag.
   801          tag == Tag("true") ||
   802          tag == Tag("typ1");
   803 }
   805 void DisableDebugOutput() {
   806   g_debug_output = false;
   807 }
   809 #ifdef MOZ_OTS_WOFF2
   810 void EnableWOFF2() {
   811   g_enable_woff2 = true;
   812 }
   813 #endif
   815 void SetMessageCallback(MessageFunc func, void *user_data) {
   816   g_message_func = func;
   817   g_message_user_data = user_data;
   818 }
   820 void SetTableActionCallback(TableActionFunc func, void *user_data) {
   821   g_table_action_func = func;
   822   g_table_action_user_data = user_data;
   823 }
   825 bool Process(OTSStream *output, const uint8_t *data, size_t length) {
   826   OpenTypeFile header;
   828   header.message_func = g_message_func;
   829   header.user_data = g_message_user_data;
   831   if (length < 4) {
   832     return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
   833   }
   835   bool result;
   836   if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
   837     result = ProcessWOFF(&header, output, data, length);
   838 #ifdef MOZ_OTS_WOFF2
   839   } else if (g_enable_woff2 &&
   840              data[0] == 'w' && data[1] == 'O' && data[2] == 'F' &&
   841              data[3] == '2') {
   842     result = ProcessWOFF2(&header, output, data, length);
   843 #endif
   844   } else {
   845     result = ProcessTTF(&header, output, data, length);
   846   }
   848   for (unsigned i = 0; ; ++i) {
   849     if (table_parsers[i].parse == NULL) break;
   850     table_parsers[i].free(&header);
   851   }
   852   return result;
   853 }
   855 #if !defined(_MSC_VER) && defined(OTS_DEBUG)
   856 bool Failure(const char *f, int l, const char *fn) {
   857   if (g_debug_output) {
   858     std::fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn);
   859     std::fflush(stderr);
   860   }
   861   return false;
   862 }
   864 void Warning(const char *f, int l, const char *format, ...) {
   865   if (g_debug_output) {
   866     std::fprintf(stderr, "WARNING at %s:%d: ", f, l);
   867     std::va_list va;
   868     va_start(va, format);
   869     std::vfprintf(stderr, format, va);
   870     va_end(va);
   871     std::fprintf(stderr, "\n");
   872     std::fflush(stderr);
   873   }
   874 }
   875 #endif
   877 }  // namespace ots

mercurial