Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2011 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.
      4 
      5 #include "name.h"
      6 
      7 #include <algorithm>
      8 #include <cstring>
      9 
     10 #include "cff.h"
     11 
     12 // name - Naming Table
     13 // http://www.microsoft.com/opentype/otspec/name.htm
     14 
     15 namespace {
     16 
     17 bool ValidInPsName(char c) {
     18   return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
     19 }
     20 
     21 bool CheckPsNameAscii(const std::string& name) {
     22   for (unsigned i = 0; i < name.size(); ++i) {
     23     if (!ValidInPsName(name[i])) {
     24       return false;
     25     }
     26   }
     27   return true;
     28 }
     29 
     30 bool CheckPsNameUtf16Be(const std::string& name) {
     31   if ((name.size() & 1) != 0)
     32     return false;
     33 
     34   for (unsigned i = 0; i < name.size(); i += 2) {
     35     if (name[i] != 0) {
     36       return false;
     37     }
     38     if (!ValidInPsName(name[i+1])) {
     39       return false;
     40     }
     41   }
     42   return true;
     43 }
     44 
     45 void AssignToUtf16BeFromAscii(std::string* target,
     46                               const std::string& source) {
     47   target->resize(source.size() * 2);
     48   for (unsigned i = 0, j = 0; i < source.size(); i++) {
     49     (*target)[j++] = '\0';
     50     (*target)[j++] = source[i];
     51   }
     52 }
     53 
     54 }  // namespace
     55 
     56 
     57 namespace ots {
     58 
     59 bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) {
     60   Buffer table(data, length);
     61 
     62   OpenTypeNAME* name = new OpenTypeNAME;
     63   file->name = name;
     64 
     65   uint16_t format = 0;
     66   if (!table.ReadU16(&format) || format > 1) {
     67     return OTS_FAILURE();
     68   }
     69 
     70   uint16_t count = 0;
     71   if (!table.ReadU16(&count)) {
     72     return OTS_FAILURE();
     73   }
     74 
     75   uint16_t string_offset = 0;
     76   if (!table.ReadU16(&string_offset) || string_offset > length) {
     77     return OTS_FAILURE();
     78   }
     79   const char* string_base = reinterpret_cast<const char*>(data) +
     80       string_offset;
     81 
     82   NameRecord prev_record;
     83   bool sort_required = false;
     84 
     85   // Read all the names, discarding any with invalid IDs,
     86   // and any where the offset/length would be outside the table.
     87   // A stricter alternative would be to reject the font if there
     88   // are invalid name records, but it's not clear that is necessary.
     89   for (unsigned i = 0; i < count; ++i) {
     90     NameRecord rec;
     91     uint16_t name_length, name_offset;
     92     if (!table.ReadU16(&rec.platform_id) ||
     93         !table.ReadU16(&rec.encoding_id) ||
     94         !table.ReadU16(&rec.language_id) ||
     95         !table.ReadU16(&rec.name_id) ||
     96         !table.ReadU16(&name_length) ||
     97         !table.ReadU16(&name_offset)) {
     98       return OTS_FAILURE();
     99     }
    100     // check platform & encoding, discard names with unknown values
    101     switch (rec.platform_id) {
    102       case 0:  // Unicode
    103         if (rec.encoding_id > 6) {
    104           continue;
    105         }
    106         break;
    107       case 1:  // Macintosh
    108         if (rec.encoding_id > 32) {
    109           continue;
    110         }
    111         break;
    112       case 2:  // ISO
    113         if (rec.encoding_id > 2) {
    114           continue;
    115         }
    116         break;
    117       case 3:  // Windows: IDs 7 to 9 are "reserved"
    118         if (rec.encoding_id > 6 && rec.encoding_id != 10) {
    119           continue;
    120         }
    121         break;
    122       case 4:  // Custom (OTF Windows NT compatibility)
    123         if (rec.encoding_id > 255) {
    124           continue;
    125         }
    126         break;
    127       default:  // unknown platform
    128         continue;
    129     }
    130 
    131     const unsigned name_end = static_cast<unsigned>(string_offset) +
    132         name_offset + name_length;
    133     if (name_end > length) {
    134       continue;
    135     }
    136     rec.text.resize(name_length);
    137     rec.text.assign(string_base + name_offset, name_length);
    138 
    139     if (rec.name_id == 6) {
    140       // PostScript name: check that it is valid, if not then discard it
    141       if (rec.platform_id == 1) {
    142         if (file->cff && !file->cff->name.empty()) {
    143           rec.text = file->cff->name;
    144         } else if (!CheckPsNameAscii(rec.text)) {
    145           continue;
    146         }
    147       } else if (rec.platform_id == 0 || rec.platform_id == 3) {
    148         if (file->cff && !file->cff->name.empty()) {
    149           AssignToUtf16BeFromAscii(&rec.text, file->cff->name);
    150         } else if (!CheckPsNameUtf16Be(rec.text)) {
    151           continue;
    152         }
    153       }
    154     }
    155 
    156     if ((i > 0) && !(prev_record < rec)) {
    157       OTS_WARNING("name records are not sorted.");
    158       sort_required = true;
    159     }
    160 
    161     name->names.push_back(rec);
    162     prev_record = rec;
    163   }
    164 
    165   if (format == 1) {
    166     // extended name table format with language tags
    167     uint16_t lang_tag_count;
    168     if (!table.ReadU16(&lang_tag_count)) {
    169       return OTS_FAILURE();
    170     }
    171     for (unsigned i = 0; i < lang_tag_count; ++i) {
    172       uint16_t tag_length = 0;
    173       uint16_t tag_offset = 0;
    174       if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
    175         return OTS_FAILURE();
    176       }
    177       const unsigned tag_end = static_cast<unsigned>(string_offset) +
    178           tag_offset + tag_length;
    179       if (tag_end > length) {
    180         return OTS_FAILURE();
    181       }
    182       std::string tag(string_base + tag_offset, tag_length);
    183       name->lang_tags.push_back(tag);
    184     }
    185   }
    186 
    187   if (table.offset() > string_offset) {
    188     // the string storage apparently overlapped the name/tag records;
    189     // consider this font to be badly broken
    190     return OTS_FAILURE();
    191   }
    192 
    193   // check existence of required name strings (synthesize if necessary)
    194   //  [0 - copyright - skip]
    195   //   1 - family
    196   //   2 - subfamily
    197   //  [3 - unique ID - skip]
    198   //   4 - full name
    199   //   5 - version
    200   //   6 - postscript name
    201   static const unsigned kStdNameCount = 7;
    202   static const char* kStdNames[kStdNameCount] = {
    203     NULL,
    204     "OTS derived font",
    205     "Unspecified",
    206     NULL,
    207     "OTS derived font",
    208     "1.000",
    209     "OTS-derived-font"
    210   };
    211   // The spec says that "In CFF OpenType fonts, these two name strings, when
    212   // translated to ASCII, must also be identical to the font name as stored in
    213   // the CFF's Name INDEX." And actually, Mac OS X's font parser requires that.
    214   if (file->cff && !file->cff->name.empty()) {
    215     kStdNames[6] = file->cff->name.c_str();
    216   }
    217 
    218   // scan the names to check whether the required "standard" ones are present;
    219   // if not, we'll add our fixed versions here
    220   bool mac_name[kStdNameCount] = { 0 };
    221   bool win_name[kStdNameCount] = { 0 };
    222   for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
    223        name_iter != name->names.end(); name_iter++) {
    224     const uint16_t id = name_iter->name_id;
    225     if (id >= kStdNameCount || kStdNames[id] == NULL) {
    226       continue;
    227     }
    228     if (name_iter->platform_id == 1) {
    229       mac_name[id] = true;
    230       continue;
    231     }
    232     if (name_iter->platform_id == 3) {
    233       win_name[id] = true;
    234       continue;
    235     }
    236   }
    237 
    238   for (unsigned i = 0; i < kStdNameCount; ++i) {
    239     if (kStdNames[i] == NULL) {
    240       continue;
    241     }
    242     if (!mac_name[i]) {
    243       NameRecord rec(1 /* platform_id */, 0 /* encoding_id */,
    244                      0 /* language_id */ , i /* name_id */);
    245       rec.text.assign(kStdNames[i]);
    246       name->names.push_back(rec);
    247       sort_required = true;
    248     }
    249     if (!win_name[i]) {
    250       NameRecord rec(3 /* platform_id */, 1 /* encoding_id */,
    251                      1033 /* language_id */ , i /* name_id */);
    252       AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i]));
    253       name->names.push_back(rec);
    254       sort_required = true;
    255     }
    256   }
    257 
    258   if (sort_required) {
    259     std::sort(name->names.begin(), name->names.end());
    260   }
    261 
    262   return true;
    263 }
    264 
    265 bool ots_name_should_serialise(OpenTypeFile* file) {
    266   return file->name != NULL;
    267 }
    268 
    269 bool ots_name_serialise(OTSStream* out, OpenTypeFile* file) {
    270   const OpenTypeNAME* name = file->name;
    271 
    272   uint16_t name_count = name->names.size();
    273   uint16_t lang_tag_count = name->lang_tags.size();
    274   uint16_t format = 0;
    275   size_t string_offset = 6 + name_count * 12;
    276 
    277   if (name->lang_tags.size() > 0) {
    278     // lang tags require a format-1 name table
    279     format = 1;
    280     string_offset += 2 + lang_tag_count * 4;
    281   }
    282   if (string_offset > 0xffff) {
    283     return OTS_FAILURE();
    284   }
    285   if (!out->WriteU16(format) ||
    286       !out->WriteU16(name_count) ||
    287       !out->WriteU16(string_offset)) {
    288     return OTS_FAILURE();
    289   }
    290 
    291   std::string string_data;
    292   for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
    293        name_iter != name->names.end(); name_iter++) {
    294     const NameRecord& rec = *name_iter;
    295     if (!out->WriteU16(rec.platform_id) ||
    296         !out->WriteU16(rec.encoding_id) ||
    297         !out->WriteU16(rec.language_id) ||
    298         !out->WriteU16(rec.name_id) ||
    299         !out->WriteU16(rec.text.size()) ||
    300         !out->WriteU16(string_data.size()) ) {
    301       return OTS_FAILURE();
    302     }
    303     string_data.append(rec.text);
    304   }
    305 
    306   if (format == 1) {
    307     if (!out->WriteU16(lang_tag_count)) {
    308       return OTS_FAILURE();
    309     }
    310     for (std::vector<std::string>::const_iterator tag_iter =
    311              name->lang_tags.begin();
    312          tag_iter != name->lang_tags.end(); tag_iter++) {
    313       if (!out->WriteU16(tag_iter->size()) ||
    314           !out->WriteU16(string_data.size())) {
    315         return OTS_FAILURE();
    316       }
    317       string_data.append(*tag_iter);
    318     }
    319   }
    320 
    321   if (!out->Write(string_data.data(), string_data.size())) {
    322     return OTS_FAILURE();
    323   }
    324 
    325   return true;
    326 }
    327 
    328 void ots_name_free(OpenTypeFile* file) {
    329   delete file->name;
    330 }
    331 
    332 }  // namespace
    333