tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

normalize.cc (9114B)


      1 /* Copyright 2013 Google Inc. All Rights Reserved.
      2 
      3   Distributed under MIT license.
      4   See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
      5 */
      6 
      7 /* Glyph normalization */
      8 
      9 #include "./normalize.h"
     10 
     11 #include <inttypes.h>
     12 #include <stddef.h>
     13 
     14 #include "./buffer.h"
     15 #include "./port.h"
     16 #include "./font.h"
     17 #include "./glyph.h"
     18 #include "./round.h"
     19 #include "./store_bytes.h"
     20 #include "./table_tags.h"
     21 #include "./woff2_common.h"
     22 
     23 namespace woff2 {
     24 
     25 namespace {
     26 
     27 void StoreLoca(int index_fmt, uint32_t value, size_t* offset, uint8_t* dst) {
     28  if (index_fmt == 0) {
     29    Store16(value >> 1, offset, dst);
     30  } else {
     31    StoreU32(value, offset, dst);
     32  }
     33 }
     34 
     35 }  // namespace
     36 
     37 namespace {
     38 
     39 bool WriteNormalizedLoca(int index_fmt, int num_glyphs, Font* font) {
     40  Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
     41  Font::Table* loca_table = font->FindTable(kLocaTableTag);
     42 
     43  int glyph_sz = index_fmt == 0 ? 2 : 4;
     44  loca_table->buffer.resize(Round4(num_glyphs + 1) * glyph_sz);
     45  loca_table->length = (num_glyphs + 1) * glyph_sz;
     46 
     47  uint8_t* glyf_dst = num_glyphs ? &glyf_table->buffer[0] : NULL;
     48  uint8_t* loca_dst = &loca_table->buffer[0];
     49  uint32_t glyf_offset = 0;
     50  size_t loca_offset = 0;
     51 
     52  for (int i = 0; i < num_glyphs; ++i) {
     53    StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
     54    Glyph glyph;
     55    const uint8_t* glyph_data;
     56    size_t glyph_size;
     57    if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
     58        (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
     59      return FONT_COMPRESSION_FAILURE();
     60    }
     61    size_t glyf_dst_size = glyf_table->buffer.size() - glyf_offset;
     62    if (!StoreGlyph(glyph, glyf_dst + glyf_offset, &glyf_dst_size)) {
     63      return FONT_COMPRESSION_FAILURE();
     64    }
     65    glyf_dst_size = Round4(glyf_dst_size);
     66    if (glyf_dst_size > std::numeric_limits<uint32_t>::max() ||
     67        glyf_offset + static_cast<uint32_t>(glyf_dst_size) < glyf_offset ||
     68        (index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) {
     69      return FONT_COMPRESSION_FAILURE();
     70    }
     71    glyf_offset += glyf_dst_size;
     72  }
     73 
     74  StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
     75 
     76  glyf_table->buffer.resize(glyf_offset);
     77  glyf_table->data = glyf_offset ? &glyf_table->buffer[0] : NULL;
     78  glyf_table->length = glyf_offset;
     79  loca_table->data = loca_offset ? &loca_table->buffer[0] : NULL;
     80 
     81  return true;
     82 }
     83 
     84 }  // namespace
     85 
     86 namespace {
     87 
     88 bool MakeEditableBuffer(Font* font, int tableTag) {
     89  Font::Table* table = font->FindTable(tableTag);
     90  if (table == NULL) {
     91    return FONT_COMPRESSION_FAILURE();
     92  }
     93  if (table->IsReused()) {
     94    return true;
     95  }
     96  int sz = Round4(table->length);
     97  table->buffer.resize(sz);
     98  uint8_t* buf = &table->buffer[0];
     99  memcpy(buf, table->data, table->length);
    100  if (PREDICT_FALSE(sz > table->length)) {
    101    memset(buf + table->length, 0, sz - table->length);
    102  }
    103  table->data = buf;
    104  return true;
    105 }
    106 
    107 }  // namespace
    108 
    109 bool NormalizeGlyphs(Font* font) {
    110  Font::Table* head_table = font->FindTable(kHeadTableTag);
    111  Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
    112  Font::Table* loca_table = font->FindTable(kLocaTableTag);
    113  if (head_table == NULL) {
    114    return FONT_COMPRESSION_FAILURE();
    115  }
    116  // If you don't have glyf/loca this transform isn't very interesting
    117  if (loca_table == NULL && glyf_table == NULL) {
    118    return true;
    119  }
    120  // It would be best if you didn't have just one of glyf/loca
    121  if ((glyf_table == NULL) != (loca_table == NULL)) {
    122    return FONT_COMPRESSION_FAILURE();
    123  }
    124  // Must share neither or both loca & glyf
    125  if (loca_table->IsReused() != glyf_table->IsReused()) {
    126    return FONT_COMPRESSION_FAILURE();
    127  }
    128  if (loca_table->IsReused()) {
    129    return true;
    130  }
    131 
    132  int index_fmt = head_table->data[51];
    133  int num_glyphs = NumGlyphs(*font);
    134 
    135  // We need to allocate a bit more than its original length for the normalized
    136  // glyf table, since it can happen that the glyphs in the original table are
    137  // 2-byte aligned, while in the normalized table they are 4-byte aligned.
    138  // That gives a maximum of 2 bytes increase per glyph. However, there is no
    139  // theoretical guarantee that the total size of the flags plus the coordinates
    140  // is the smallest possible in the normalized version, so we have to allow
    141  // some general overhead.
    142  // TODO(user) Figure out some more precise upper bound on the size of
    143  // the overhead.
    144  size_t max_normalized_glyf_size = 1.1 * glyf_table->length + 2 * num_glyphs;
    145 
    146  glyf_table->buffer.resize(max_normalized_glyf_size);
    147 
    148  // if we can't write a loca using short's (index_fmt 0)
    149  // try again using longs (index_fmt 1)
    150  if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
    151    if (index_fmt != 0) {
    152      return FONT_COMPRESSION_FAILURE();
    153    }
    154 
    155    // Rewrite loca with 4-byte entries & update head to match
    156    index_fmt = 1;
    157    if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
    158      return FONT_COMPRESSION_FAILURE();
    159    }
    160    head_table->buffer[51] = 1;
    161  }
    162 
    163  return true;
    164 }
    165 
    166 bool NormalizeOffsets(Font* font) {
    167  uint32_t offset = 12 + 16 * font->num_tables;
    168  for (auto tag : font->OutputOrderedTags()) {
    169    auto& table = font->tables[tag];
    170    table.offset = offset;
    171    offset += Round4(table.length);
    172  }
    173  return true;
    174 }
    175 
    176 namespace {
    177 
    178 uint32_t ComputeHeaderChecksum(const Font& font) {
    179  uint32_t checksum = font.flavor;
    180  uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
    181  uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0;
    182  uint16_t range_shift = (font.num_tables << 4) - search_range;
    183  checksum += (font.num_tables << 16 | search_range);
    184  checksum += (max_pow2 << 16 | range_shift);
    185  for (const auto& i : font.tables) {
    186    const Font::Table* table = &i.second;
    187    if (table->IsReused()) {
    188      table = table->reuse_of;
    189    }
    190    checksum += table->tag;
    191    checksum += table->checksum;
    192    checksum += table->offset;
    193    checksum += table->length;
    194  }
    195  return checksum;
    196 }
    197 
    198 }  // namespace
    199 
    200 bool FixChecksums(Font* font) {
    201  Font::Table* head_table = font->FindTable(kHeadTableTag);
    202  if (head_table == NULL) {
    203    return FONT_COMPRESSION_FAILURE();
    204  }
    205  if (head_table->reuse_of != NULL) {
    206    head_table = head_table->reuse_of;
    207  }
    208  if (head_table->length < 12) {
    209    return FONT_COMPRESSION_FAILURE();
    210  }
    211 
    212  uint8_t* head_buf = &head_table->buffer[0];
    213  size_t offset = 8;
    214  StoreU32(0, &offset, head_buf);
    215  uint32_t file_checksum = 0;
    216  uint32_t head_checksum = 0;
    217  for (auto& i : font->tables) {
    218    Font::Table* table = &i.second;
    219    if (table->IsReused()) {
    220      table = table->reuse_of;
    221    }
    222    table->checksum = ComputeULongSum(table->data, table->length);
    223    file_checksum += table->checksum;
    224 
    225    if (table->tag == kHeadTableTag) {
    226      head_checksum = table->checksum;
    227    }
    228  }
    229 
    230  file_checksum += ComputeHeaderChecksum(*font);
    231  offset = 8;
    232  StoreU32(0xb1b0afba - file_checksum, &offset, head_buf);
    233 
    234  return true;
    235 }
    236 
    237 namespace {
    238 bool MarkTransformed(Font* font) {
    239  Font::Table* head_table = font->FindTable(kHeadTableTag);
    240  if (head_table == NULL) {
    241    return FONT_COMPRESSION_FAILURE();
    242  }
    243  if (head_table->reuse_of != NULL) {
    244    head_table = head_table->reuse_of;
    245  }
    246  if (head_table->length < 17) {
    247    return FONT_COMPRESSION_FAILURE();
    248  }
    249  // set bit 11 of head table 'flags' to indicate that font has undergone
    250  // lossless modifying transform
    251  int head_flags = head_table->data[16];
    252  head_table->buffer[16] = head_flags | 0x08;
    253  return true;
    254 }
    255 }  // namespace
    256 
    257 
    258 bool NormalizeWithoutFixingChecksums(Font* font) {
    259  return (MakeEditableBuffer(font, kHeadTableTag) &&
    260          RemoveDigitalSignature(font) &&
    261          MarkTransformed(font) &&
    262          NormalizeGlyphs(font) &&
    263          NormalizeOffsets(font));
    264 }
    265 
    266 bool NormalizeFont(Font* font) {
    267  return (NormalizeWithoutFixingChecksums(font) &&
    268          FixChecksums(font));
    269 }
    270 
    271 bool NormalizeFontCollection(FontCollection* font_collection) {
    272  if (font_collection->fonts.size() == 1) {
    273    return NormalizeFont(&font_collection->fonts[0]);
    274  }
    275 
    276  uint32_t offset = CollectionHeaderSize(font_collection->header_version,
    277    font_collection->fonts.size());
    278  for (auto& font : font_collection->fonts) {
    279    if (!NormalizeWithoutFixingChecksums(&font)) {
    280 #ifdef FONT_COMPRESSION_BIN
    281      fprintf(stderr, "Font normalization failed.\n");
    282 #endif
    283      return FONT_COMPRESSION_FAILURE();
    284    }
    285    offset += kSfntHeaderSize + kSfntEntrySize * font.num_tables;
    286  }
    287 
    288  // Start table offsets after TTC Header and Sfnt Headers
    289  for (auto& font : font_collection->fonts) {
    290    for (auto tag : font.OutputOrderedTags()) {
    291      Font::Table& table = font.tables[tag];
    292      if (table.IsReused()) {
    293        table.offset = table.reuse_of->offset;
    294      } else {
    295        table.offset = offset;
    296        offset += Round4(table.length);
    297      }
    298    }
    299  }
    300 
    301  // Now we can fix the checksums
    302  for (auto& font : font_collection->fonts) {
    303    if (!FixChecksums(&font)) {
    304 #ifdef FONT_COMPRESSION_BIN
    305      fprintf(stderr, "Failed to fix checksums\n");
    306 #endif
    307      return FONT_COMPRESSION_FAILURE();
    308    }
    309  }
    310 
    311  return true;
    312 }
    313 
    314 } // namespace woff2