bug 601487 - add support for cmap subtable format 14 (variation sequences) to OTS sanitizer. r=jdaggett a=blocking2.0
authorJonathan Kew <jfkthame@gmail.com>
Thu, 07 Oct 2010 08:59:18 +0100
changeset 55082 b45ce9db9d727eeb732a5ccd6dc7029656cbfa75
parent 55081 88ff5f0a9681a0d87d684a563c3a5ea375b87699
child 55083 ff826d1ba112c80188264de55a9cecb49aa8f96c
push idunknown
push userunknown
push dateunknown
reviewersjdaggett, blocking2.0
bugs601487
milestone2.0b8pre
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 601487 - add support for cmap subtable format 14 (variation sequences) to OTS sanitizer. r=jdaggett a=blocking2.0
gfx/ots/src/cmap.cc
gfx/ots/src/cmap.h
gfx/ots/src/ots.h
--- a/gfx/ots/src/cmap.cc
+++ b/gfx/ots/src/cmap.cc
@@ -384,16 +384,121 @@ bool Parse31013(ots::OpenTypeFile *file,
     if (groups[i].start_range <= groups[i - 1].end_range) {
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
+// Parses 0.5.14 tables.
+bool Parse0514(ots::OpenTypeFile *file,
+               const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Format 14 subtables are a bit complex, so rather than rebuilding the
+  // entire thing, we validate it and then include it verbatim in the output.
+
+  const off_t offset_var_selector_records = 10;
+  const size_t size_of_var_selector_record = 11;
+  const size_t size_of_def_uvs_table = 4;
+  const size_t size_of_non_def_uvs_table = 5;
+
+  if (!subtable.Skip(6)) { // skip format and length
+    return OTS_FAILURE();
+  }
+  uint32_t num_var_selector_records = 0;
+  if (!subtable.ReadU32(&num_var_selector_records)) {
+    return OTS_FAILURE();
+  }
+  if ((length - offset_var_selector_records) / size_of_var_selector_record <
+      num_var_selector_records) {
+    return OTS_FAILURE();
+  }
+
+  uint32_t prev_var_selector = 0;
+  for (uint32_t i = 0; i < num_var_selector_records; i++) {
+    uint32_t var_selector, def_uvs_offset, non_def_uvs_offset;
+    if (!subtable.ReadU24(&var_selector) ||
+        !subtable.ReadU32(&def_uvs_offset) ||
+        !subtable.ReadU32(&non_def_uvs_offset)) {
+      return OTS_FAILURE();
+    }
+    if (var_selector <= prev_var_selector ||
+        var_selector > kUnicodeUpperLimit ||
+        def_uvs_offset > length - 4 ||
+        non_def_uvs_offset > length - 4) {
+      return OTS_FAILURE();
+    }
+    prev_var_selector = var_selector;
+
+    if (def_uvs_offset) {
+      uint32_t num_unicode_value_ranges;
+      memcpy(&num_unicode_value_ranges, data + def_uvs_offset, 4);
+      num_unicode_value_ranges = ntohl(num_unicode_value_ranges);
+      if ((length - def_uvs_offset) / size_of_def_uvs_table <
+           num_unicode_value_ranges) {
+        return OTS_FAILURE();
+      }
+
+      const uint8_t *tables = data + def_uvs_offset + 4;
+      uint32_t prev_end_unicode = 0;
+      for (uint32_t j = 0; j < num_unicode_value_ranges;
+           j++, tables += size_of_def_uvs_table) {
+        uint32_t start_unicode = 0, end_unicode;
+        memcpy(reinterpret_cast<uint8_t*>(&start_unicode) + 1, tables, 3);
+        start_unicode = ntohl(start_unicode);
+        end_unicode = start_unicode + *(tables + 3);
+        if ((j > 0 && start_unicode <= prev_end_unicode) ||
+            end_unicode > kUnicodeUpperLimit) {
+          return OTS_FAILURE();
+        }
+        prev_end_unicode = end_unicode;
+      }
+    }
+
+    if (non_def_uvs_offset) {
+      uint32_t num_uvs_mappings;
+      memcpy(&num_uvs_mappings, data + non_def_uvs_offset, 4);
+      num_uvs_mappings = ntohl(num_uvs_mappings);
+      if ((length - non_def_uvs_offset) / size_of_non_def_uvs_table <
+          num_uvs_mappings) {
+        return OTS_FAILURE();
+      }
+
+      const uint8_t *tables = data + non_def_uvs_offset + 4;
+      uint32_t prev_unicode = 0;
+      for (uint32_t j = 0; j < num_uvs_mappings;
+           j++, tables += size_of_non_def_uvs_table) {
+        uint32_t unicode_value = 0;
+        memcpy(reinterpret_cast<uint8_t*>(&unicode_value) + 1, tables, 3);
+        unicode_value = ntohl(unicode_value);
+        if ((j > 0 && unicode_value <= prev_unicode) ||
+            unicode_value > kUnicodeUpperLimit) {
+          return OTS_FAILURE();
+        }
+        uint16_t glyph;
+        memcpy(&glyph, tables + 3, 2);
+        glyph = ntohs(glyph);
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE();
+        }
+        prev_unicode = unicode_value;
+      }
+    }
+  }
+
+  // We accept the table.
+  // TODO: transcode the subtable.
+  file->cmap->subtable_0_5_14_data = data;
+  file->cmap->subtable_0_5_14_length = length;
+
+  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 = 0;
@@ -498,16 +603,21 @@ bool ots_cmap_parse(OpenTypeFile *file, 
       case 13:
         if (!table.Skip(2)) {
           return OTS_FAILURE();
         }
         if (!table.ReadU32(&subtable_headers[i].length)) {
           return OTS_FAILURE();
         }
         break;
+      case 14:
+        if (!table.ReadU32(&subtable_headers[i].length)) {
+          return OTS_FAILURE();
+        }
+        break;
       default:
         subtable_headers[i].length = 0;
         break;
     }
   }
 
   // Now, verify that all the lengths are sane
   for (unsigned i = 0; i < num_tables; ++i) {
@@ -560,16 +670,17 @@ bool ots_cmap_parse(OpenTypeFile *file, 
 
   // 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)
+  //   0             5            14      (Unicode Variation Sequences)
   //   1             0            0       (Mac Roman)
   //   3             0            4       (MS Symbol)
   //   3             1            4       (MS Unicode BMP)
   //   3             10           12      (MS Unicode UCS-4)
   //   3             10           13      (MS UCS-4 Fallback mapping)
   //
   // Note:
   //  * 0-0-4 table is (usually) written as a 3-1-4 table. If 3-1-4 table
@@ -603,16 +714,22 @@ bool ots_cmap_parse(OpenTypeFile *file, 
         }
       } 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].encoding == 5) &&
+                 (subtable_headers[i].format == 14)) {
+        if (!Parse0514(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.
@@ -659,24 +776,26 @@ bool ots_cmap_parse(OpenTypeFile *file, 
   return true;
 }
 
 bool ots_cmap_should_serialise(OpenTypeFile *file) {
   return file->cmap;
 }
 
 bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
+  const bool have_0514 = file->cmap->subtable_0_5_14_data;
   const bool have_100 = file->cmap->subtable_1_0_0.size();
   const bool have_304 = file->cmap->subtable_3_0_4_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_3_1_4_data;
   const bool have_31012 = file->cmap->subtable_3_10_12.size();
   const bool have_31013 = file->cmap->subtable_3_10_13.size();
-  const unsigned num_subtables = static_cast<unsigned>(have_100) +
+  const unsigned num_subtables = static_cast<unsigned>(have_0514) +
+                                 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.
@@ -761,27 +880,43 @@ bool ots_cmap_serialise(OTSStream *out, 
       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_0514 = out->Tell();
+  if (have_0514) {
+    if (!out->Write(file->cmap->subtable_0_5_14_data,
+                    file->cmap->subtable_0_5_14_length)) {
+      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_0514) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(5) ||
+        !out->WriteU32(offset_0514 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
   if (have_100) {
     if (!out->WriteU16(1) ||
         !out->WriteU16(0) ||
         !out->WriteU32(offset_100 - table_start)) {
       return OTS_FAILURE();
     }
   }
 
--- a/gfx/ots/src/cmap.h
+++ b/gfx/ots/src/cmap.h
@@ -17,25 +17,30 @@ struct OpenTypeCMAPSubtableRange {
   uint32_t start_glyph_id;
 };
 
 struct OpenTypeCMAP {
   OpenTypeCMAP()
       : subtable_3_0_4_data(NULL),
         subtable_3_0_4_length(0),
         subtable_3_1_4_data(NULL),
-        subtable_3_1_4_length(0) {
+        subtable_3_1_4_length(0),
+        subtable_0_5_14_data(NULL),
+        subtable_0_5_14_length(0) {
   }
 
   // Platform 3, Encoding 0, Format 4, MS Symbol table.
   const uint8_t *subtable_3_0_4_data;
   size_t subtable_3_0_4_length;
   // Platform 3, Encoding 1, Format 4, MS Unicode BMP table.
   const uint8_t *subtable_3_1_4_data;
   size_t subtable_3_1_4_length;
+  // Platform 0, Encoding 5, Format 14, Unicode Variation Sequences table.
+  const uint8_t *subtable_0_5_14_data;
+  size_t subtable_0_5_14_length;
 
   // Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
   std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
   // Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
   std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
   // Platform 1, Encoding 0, Format 0, Mac Roman table.
   std::vector<uint8_t> subtable_1_0_0;
 };
--- a/gfx/ots/src/ots.h
+++ b/gfx/ots/src/ots.h
@@ -96,16 +96,28 @@ class Buffer {
     offset_ += 2;
     return true;
   }
 
   bool ReadS16(int16_t *value) {
     return ReadU16(reinterpret_cast<uint16_t*>(value));
   }
 
+  bool ReadU24(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return OTS_FAILURE();
+    }
+    *value = 0;
+    std::memcpy(reinterpret_cast<uint8_t*>(value) + 1,
+                buffer_ + offset_, 3);
+    *value = ntohl(*value);
+    offset_ += 3;
+    return true;
+  }
+
   bool ReadU32(uint32_t *value) {
     if (offset_ + 4 > length_) {
       return OTS_FAILURE();
     }
     std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
     *value = ntohl(*value);
     offset_ += 4;
     return true;