Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/2d/SFNTNameTable.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "SFNTNameTable.h"
8
9
#include "BigEndianInts.h"
10
#include "Logging.h"
11
#include "mozilla/Move.h"
12
13
#if defined(XP_MACOSX)
14
#include <CoreFoundation/CoreFoundation.h>
15
#endif
16
17
namespace mozilla {
18
namespace gfx {
19
20
static const BigEndianUint16 FORMAT_0 = 0;
21
22
static const BigEndianUint16 NAME_ID_FAMILY = 1;
23
static const BigEndianUint16 NAME_ID_STYLE = 2;
24
static const BigEndianUint16 NAME_ID_FULL = 4;
25
26
static const BigEndianUint16 PLATFORM_ID_UNICODE = 0;
27
static const BigEndianUint16 PLATFORM_ID_MAC = 1;
28
static const BigEndianUint16 PLATFORM_ID_MICROSOFT = 3;
29
30
static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL = 0;
31
static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP = 1;
32
static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL = 10;
33
34
static const BigEndianUint16 ENCODING_ID_MAC_ROMAN = 0;
35
36
static const BigEndianUint16 LANG_ID_MAC_ENGLISH = 0;
37
38
static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US = 0x0409;
39
40
#pragma pack(push, 1)
41
42
// Name table has a header, followed by name records, followed by string data.
43
struct NameHeader
44
{
45
  BigEndianUint16 format;       // Format selector (=0).
46
  BigEndianUint16 count;        // Number of name records.
47
  BigEndianUint16 stringOffset; // Offset to string storage from start of table.
48
};
49
50
struct NameRecord
51
{
52
  BigEndianUint16 platformID;
53
  BigEndianUint16 encodingID; // Platform-specific encoding ID
54
  BigEndianUint16 languageID;
55
  BigEndianUint16 nameID;
56
  BigEndianUint16 length;     // String length in bytes.
57
  BigEndianUint16 offset;     // String offset from start of storage in bytes.
58
};
59
60
#pragma pack(pop)
61
62
enum ENameDecoder : int
63
{
64
  eNameDecoderUTF16,
65
#if defined(XP_MACOSX)
66
  eNameDecoderMacRoman,
67
#endif
68
  eNameDecoderNone
69
};
70
71
/* static */
72
UniquePtr<SFNTNameTable>
73
SFNTNameTable::Create(const uint8_t *aNameData, uint32_t aDataLength)
74
0
{
75
0
  MOZ_ASSERT(aNameData);
76
0
77
0
  if (aDataLength < sizeof(NameHeader)) {
78
0
    gfxWarning() << "Name data too short to contain NameHeader.";
79
0
    return nullptr;
80
0
  }
81
0
82
0
  const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
83
0
  if (nameHeader->format != FORMAT_0) {
84
0
    gfxWarning() << "Only Name Table Format 0 is supported.";
85
0
    return nullptr;
86
0
  }
87
0
88
0
  uint16_t stringOffset = nameHeader->stringOffset;
89
0
90
0
  if (stringOffset !=
91
0
      sizeof(NameHeader) + (nameHeader->count * sizeof(NameRecord))) {
92
0
    gfxWarning() << "Name table string offset is incorrect.";
93
0
    return nullptr;
94
0
  }
95
0
96
0
  if (aDataLength < stringOffset) {
97
0
    gfxWarning() << "Name data too short to contain name records.";
98
0
    return nullptr;
99
0
  }
100
0
101
0
  return UniquePtr<SFNTNameTable>(
102
0
    new SFNTNameTable(nameHeader, aNameData, aDataLength));
103
0
}
104
105
SFNTNameTable::SFNTNameTable(const NameHeader *aNameHeader,
106
                             const uint8_t *aNameData, uint32_t aDataLength)
107
  : mFirstRecord(reinterpret_cast<const NameRecord*>(aNameData
108
                                                     + sizeof(NameHeader)))
109
  , mEndOfRecords(mFirstRecord + aNameHeader->count)
110
  , mStringData(aNameData + aNameHeader->stringOffset)
111
  , mStringDataLength(aDataLength - aNameHeader->stringOffset)
112
0
{
113
0
  MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader) == aNameData);
114
0
}
115
116
static bool
117
IsUTF16Encoding(const NameRecord *aNameRecord)
118
0
{
119
0
  if (aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
120
0
      (aNameRecord->encodingID == ENCODING_ID_MICROSOFT_UNICODEBMP ||
121
0
       aNameRecord->encodingID == ENCODING_ID_MICROSOFT_SYMBOL)) {
122
0
    return true;
123
0
  }
124
0
125
0
  if (aNameRecord->platformID == PLATFORM_ID_UNICODE) {
126
0
    return true;
127
0
  }
128
0
129
0
  return false;
130
0
}
131
132
#if defined(XP_MACOSX)
133
static bool
134
IsMacRomanEncoding(const NameRecord *aNameRecord)
135
{
136
  if (aNameRecord->platformID == PLATFORM_ID_MAC &&
137
      aNameRecord->encodingID == ENCODING_ID_MAC_ROMAN) {
138
    return true;
139
  }
140
141
  return false;
142
}
143
#endif
144
145
static NameRecordMatchers*
146
CreateCanonicalMatchers(const BigEndianUint16& aNameID)
147
0
{
148
0
  // For Windows, we return only Microsoft platform name record
149
0
  // matchers. On Mac, we return matchers for both Microsoft platform
150
0
  // records and Mac platform records.
151
0
  NameRecordMatchers *matchers = new NameRecordMatchers();
152
0
153
#if defined(XP_MACOSX)
154
  // First, look for the English name.
155
  if (!matchers->append(
156
    [=](const NameRecord *aNameRecord) {
157
        if (aNameRecord->nameID == aNameID &&
158
            aNameRecord->languageID == LANG_ID_MAC_ENGLISH &&
159
            aNameRecord->platformID == PLATFORM_ID_MAC &&
160
            IsMacRomanEncoding(aNameRecord)) {
161
          return eNameDecoderMacRoman;
162
        } else  {
163
          return eNameDecoderNone;
164
        }
165
    })) {
166
    MOZ_CRASH();
167
  }
168
169
  // Second, look for all languages.
170
  if (!matchers->append(
171
    [=](const NameRecord *aNameRecord) {
172
        if (aNameRecord->nameID == aNameID &&
173
            aNameRecord->platformID == PLATFORM_ID_MAC &&
174
            IsMacRomanEncoding(aNameRecord)) {
175
          return eNameDecoderMacRoman;
176
        } else  {
177
          return eNameDecoderNone;
178
        }
179
    })) {
180
    MOZ_CRASH();
181
  }
182
#endif /* defined(XP_MACOSX) */
183
184
0
  // First, look for the English name (this will normally succeed).
185
0
  if (!matchers->append(
186
0
    [=](const NameRecord *aNameRecord) {
187
0
        if (aNameRecord->nameID == aNameID &&
188
0
            aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US &&
189
0
            aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
190
0
            IsUTF16Encoding(aNameRecord)) {
191
0
          return eNameDecoderUTF16;
192
0
        } else {
193
0
          return eNameDecoderNone;
194
0
        }
195
0
    })) {
196
0
    MOZ_CRASH();
197
0
  }
198
0
199
0
  // Second, look for all languages.
200
0
  if (!matchers->append(
201
0
    [=](const NameRecord *aNameRecord) {
202
0
        if (aNameRecord->nameID == aNameID &&
203
0
            aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
204
0
            IsUTF16Encoding(aNameRecord)) {
205
0
          return eNameDecoderUTF16;
206
0
        } else {
207
0
          return eNameDecoderNone;
208
0
        }
209
0
    })) {
210
0
    MOZ_CRASH();
211
0
  }
212
0
213
0
  return matchers;
214
0
}
215
216
static const NameRecordMatchers&
217
FullNameMatchers()
218
0
{
219
0
  static const NameRecordMatchers *sFullNameMatchers =
220
0
    CreateCanonicalMatchers(NAME_ID_FULL);
221
0
  return *sFullNameMatchers;
222
0
}
223
224
static const NameRecordMatchers&
225
FamilyMatchers()
226
0
{
227
0
  static const NameRecordMatchers *sFamilyMatchers =
228
0
    CreateCanonicalMatchers(NAME_ID_FAMILY);
229
0
  return *sFamilyMatchers;
230
0
}
231
232
static const NameRecordMatchers&
233
StyleMatchers()
234
0
{
235
0
  static const NameRecordMatchers *sStyleMatchers =
236
0
    CreateCanonicalMatchers(NAME_ID_STYLE);
237
0
  return *sStyleMatchers;
238
0
}
239
240
bool
241
SFNTNameTable::GetU16FullName(mozilla::u16string& aU16FullName)
242
0
{
243
0
  if (ReadU16Name(FullNameMatchers(), aU16FullName)) {
244
0
    return true;
245
0
  }
246
0
247
0
  // If the full name record doesn't exist create the name from the family space
248
0
  // concatenated with the style.
249
0
  mozilla::u16string familyName;
250
0
  if (!ReadU16Name(FamilyMatchers(), familyName)) {
251
0
    return false;
252
0
  }
253
0
254
0
  mozilla::u16string styleName;
255
0
  if (!ReadU16Name(StyleMatchers(), styleName)) {
256
0
    return false;
257
0
  }
258
0
259
0
  aU16FullName.assign(std::move(familyName));
260
0
  aU16FullName.append(u" ");
261
0
  aU16FullName.append(styleName);
262
0
  return true;
263
0
}
264
265
bool
266
SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
267
                           mozilla::u16string& aU16Name)
268
0
{
269
0
  MOZ_ASSERT(!aMatchers.empty());
270
0
271
0
  for (size_t i = 0; i < aMatchers.length(); ++i) {
272
0
    const NameRecord* record = mFirstRecord;
273
0
    while (record != mEndOfRecords) {
274
0
      switch (aMatchers[i](record)) {
275
0
        case eNameDecoderUTF16:
276
0
          return ReadU16NameFromU16Record(record, aU16Name);
277
#if defined(XP_MACOSX)
278
        case eNameDecoderMacRoman:
279
          return ReadU16NameFromMacRomanRecord(record, aU16Name);
280
#endif
281
0
        case eNameDecoderNone:
282
0
          break;
283
0
        default:
284
0
          MOZ_CRASH("Invalid matcher encoding type");
285
0
          break;
286
0
      }
287
0
      ++record;
288
0
    }
289
0
  }
290
0
291
0
  return false;
292
0
}
293
294
bool
295
SFNTNameTable::ReadU16NameFromU16Record(const NameRecord *aNameRecord,
296
                                        mozilla::u16string& aU16Name)
297
0
{
298
0
  uint32_t offset = aNameRecord->offset;
299
0
  uint32_t length = aNameRecord->length;
300
0
  if (mStringDataLength < offset + length) {
301
0
    gfxWarning() << "Name data too short to contain name string.";
302
0
    return false;
303
0
  }
304
0
305
0
  const uint8_t *startOfName = mStringData + offset;
306
0
  size_t actualLength = length / sizeof(char16_t);
307
0
  UniquePtr<char16_t[]> nameData(new char16_t[actualLength]);
308
0
  NativeEndian::copyAndSwapFromBigEndian(nameData.get(), startOfName,
309
0
                                         actualLength);
310
0
311
0
  aU16Name.assign(nameData.get(), actualLength);
312
0
  return true;
313
0
}
314
315
#if defined(XP_MACOSX)
316
bool
317
SFNTNameTable::ReadU16NameFromMacRomanRecord(const NameRecord *aNameRecord,
318
                                             mozilla::u16string& aU16Name)
319
{
320
  uint32_t offset = aNameRecord->offset;
321
  uint32_t length = aNameRecord->length;
322
  if (mStringDataLength < offset + length) {
323
    gfxWarning() << "Name data too short to contain name string.";
324
    return false;
325
  }
326
  if (length > INT_MAX) {
327
    gfxWarning() << "Name record too long to decode.";
328
    return false;
329
  }
330
331
  // pointer to the Mac Roman encoded string in the name record
332
  const uint8_t *encodedStr = mStringData + offset;
333
334
  CFStringRef cfString;
335
  cfString = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, encodedStr,
336
                                           length, kCFStringEncodingMacRoman,
337
                                           false, kCFAllocatorNull);
338
339
  // length (in UTF-16 code pairs) of the decoded string
340
  CFIndex decodedLength = CFStringGetLength(cfString);
341
342
  // temporary buffer
343
  UniquePtr<UniChar[]> u16Buffer = MakeUnique<UniChar[]>(decodedLength);
344
345
  CFStringGetCharacters(cfString, CFRangeMake(0, decodedLength),
346
                        u16Buffer.get());
347
348
  CFRelease(cfString);
349
350
  aU16Name.assign(reinterpret_cast<char16_t*>(u16Buffer.get()), decodedLength);
351
352
  return true;
353
}
354
#endif
355
356
} // gfx
357
} // mozilla