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
15namespace {
16
17bool ValidInPsName(char c) {
18  return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
19}
20
21bool 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
30bool 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
45void 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
57namespace ots {
58
59bool 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
265bool ots_name_should_serialise(OpenTypeFile* file) {
266  return file->name != NULL;
267}
268
269bool 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
328void ots_name_free(OpenTypeFile* file) {
329  delete file->name;
330}
331
332}  // namespace
333