2
* Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
3
* Copyright (C) 2009 Torch Mobile, Inc.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
14
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
#include "OpenTypeUtilities.h"
30
#include "SharedBuffer.h"
34
struct BigEndianUShort {
35
operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; }
36
BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { }
40
struct BigEndianULong {
41
operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; }
42
BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { }
50
unsigned fontDataSize;
53
uint8_t fontPANOSE[10];
57
unsigned short fsType;
58
unsigned short magicNumber;
59
unsigned unicodeRange[4];
60
unsigned codePageRange[2];
61
unsigned checkSumAdjustment;
63
unsigned short padding1;
66
struct TableDirectoryEntry {
68
BigEndianULong checkSum;
69
BigEndianULong offset;
70
BigEndianULong length;
74
// Fixed type is not defined on non-CG platforms. |version| in sfntHeader
75
// and headTable and |fontRevision| in headTable are of Fixed, but they're
76
// not actually refered to anywhere. Therefore, we just have to match
77
// the size (4 bytes). For the definition of Fixed type, see
78
// http://developer.apple.com/documentation/mac/Legacy/GXEnvironment/GXEnvironment-356.html#HEADING356-6.
79
typedef int32_t Fixed;
84
BigEndianUShort numTables;
85
BigEndianUShort searchRange;
86
BigEndianUShort entrySelector;
87
BigEndianUShort rangeShift;
88
TableDirectoryEntry tables[1];
92
BigEndianUShort version;
93
BigEndianUShort avgCharWidth;
94
BigEndianUShort weightClass;
95
BigEndianUShort widthClass;
96
BigEndianUShort fsType;
97
BigEndianUShort subscriptXSize;
98
BigEndianUShort subscriptYSize;
99
BigEndianUShort subscriptXOffset;
100
BigEndianUShort subscriptYOffset;
101
BigEndianUShort superscriptXSize;
102
BigEndianUShort superscriptYSize;
103
BigEndianUShort superscriptXOffset;
104
BigEndianUShort superscriptYOffset;
105
BigEndianUShort strikeoutSize;
106
BigEndianUShort strikeoutPosition;
107
BigEndianUShort familyClass;
109
BigEndianULong unicodeRange[4];
111
BigEndianUShort fsSelection;
112
BigEndianUShort firstCharIndex;
113
BigEndianUShort lastCharIndex;
114
BigEndianUShort typoAscender;
115
BigEndianUShort typoDescender;
116
BigEndianUShort typoLineGap;
117
BigEndianUShort winAscent;
118
BigEndianUShort winDescent;
119
BigEndianULong codePageRange[2];
120
BigEndianUShort xHeight;
121
BigEndianUShort capHeight;
122
BigEndianUShort defaultChar;
123
BigEndianUShort breakChar;
124
BigEndianUShort maxContext;
130
BigEndianULong checkSumAdjustment;
131
BigEndianULong magicNumber;
132
BigEndianUShort flags;
133
BigEndianUShort unitsPerEm;
136
BigEndianUShort xMin;
137
BigEndianUShort xMax;
138
BigEndianUShort yMin;
139
BigEndianUShort yMax;
140
BigEndianUShort macStyle;
141
BigEndianUShort lowestRectPPEM;
142
BigEndianUShort fontDirectionHint;
143
BigEndianUShort indexToLocFormat;
144
BigEndianUShort glyphDataFormat;
148
BigEndianUShort platformID;
149
BigEndianUShort encodingID;
150
BigEndianUShort languageID;
151
BigEndianUShort nameID;
152
BigEndianUShort length;
153
BigEndianUShort offset;
157
BigEndianUShort format;
158
BigEndianUShort count;
159
BigEndianUShort stringOffset;
160
nameRecord nameRecords[1];
165
EOTHeader::EOTHeader()
167
m_buffer.resize(sizeof(EOTPrefix));
170
void EOTHeader::updateEOTSize(size_t fontDataSize)
172
prefix()->eotSize = m_buffer.size() + fontDataSize;
175
void EOTHeader::appendBigEndianString(const BigEndianUShort* string, unsigned short length)
177
size_t oldSize = m_buffer.size();
178
m_buffer.resize(oldSize + length + 2 * sizeof(unsigned short));
179
UChar* dst = reinterpret_cast<UChar*>(m_buffer.data() + oldSize);
182
unsigned numCharacters = length / 2;
183
for (unsigned j = 0; j < numCharacters; j++)
184
dst[i++] = string[j];
188
void EOTHeader::appendPaddingShort()
190
unsigned short padding = 0;
191
m_buffer.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding));
194
bool getEOTHeader(SharedBuffer* fontData, EOTHeader& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength)
200
size_t dataLength = fontData->size();
201
const char* data = fontData->data();
203
EOTPrefix* prefix = eotHeader.prefix();
205
prefix->fontDataSize = dataLength;
206
prefix->version = 0x00020001;
209
if (dataLength < offsetof(sfntHeader, tables))
212
const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data);
214
if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry))
217
bool haveOS2 = false;
218
bool haveHead = false;
219
bool haveName = false;
221
const BigEndianUShort* familyName = 0;
222
unsigned short familyNameLength = 0;
223
const BigEndianUShort* subfamilyName = 0;
224
unsigned short subfamilyNameLength = 0;
225
const BigEndianUShort* fullName = 0;
226
unsigned short fullNameLength = 0;
227
const BigEndianUShort* versionString = 0;
228
unsigned short versionStringLength = 0;
230
for (unsigned i = 0; i < sfnt->numTables; i++) {
231
unsigned tableOffset = sfnt->tables[i].offset;
232
unsigned tableLength = sfnt->tables[i].length;
234
if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength)
237
unsigned tableTag = sfnt->tables[i].tag;
241
if (dataLength < tableOffset + sizeof(OS2Table))
245
const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data + tableOffset);
246
for (unsigned j = 0; j < 10; j++)
247
prefix->fontPANOSE[j] = OS2->panose[j];
248
prefix->italic = OS2->fsSelection & 0x01;
249
prefix->weight = OS2->weightClass;
250
// FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value.
251
// Since ATS does not enforce this on Mac OS X, we do not enforce it either.
253
for (unsigned j = 0; j < 4; j++)
254
prefix->unicodeRange[j] = OS2->unicodeRange[j];
255
for (unsigned j = 0; j < 2; j++)
256
prefix->codePageRange[j] = OS2->codePageRange[j];
261
if (dataLength < tableOffset + sizeof(headTable))
265
const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset);
266
prefix->checkSumAdjustment = head->checkSumAdjustment;
271
if (dataLength < tableOffset + offsetof(nameTable, nameRecords))
275
const nameTable* name = reinterpret_cast<const nameTable*>(data + tableOffset);
276
for (int j = 0; j < name->count; j++) {
277
if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord))
279
if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) {
280
if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length)
283
unsigned short nameLength = name->nameRecords[j].length;
284
const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset);
286
switch (name->nameRecords[j].nameID) {
288
familyNameLength = nameLength;
289
familyName = nameString;
292
subfamilyNameLength = nameLength;
293
subfamilyName = nameString;
296
fullNameLength = nameLength;
297
fullName = nameString;
300
versionStringLength = nameLength;
301
versionString = nameString;
313
if (haveOS2 && haveHead && haveName)
317
prefix->charset = DEFAULT_CHARSET;
318
prefix->magicNumber = 0x504c;
319
prefix->reserved[0] = 0;
320
prefix->reserved[1] = 0;
321
prefix->reserved[2] = 0;
322
prefix->reserved[3] = 0;
323
prefix->padding1 = 0;
325
eotHeader.appendBigEndianString(familyName, familyNameLength);
326
eotHeader.appendBigEndianString(subfamilyName, subfamilyNameLength);
327
eotHeader.appendBigEndianString(versionString, versionStringLength);
329
// If possible, ensure that the family name is a prefix of the full name.
330
if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) {
331
overlaySrc = reinterpret_cast<const char*>(fullName) - data;
332
overlayDst = reinterpret_cast<const char*>(familyName) - data;
333
overlayLength = familyNameLength;
335
eotHeader.appendBigEndianString(fullName, fullNameLength);
337
eotHeader.appendPaddingShort();
338
eotHeader.updateEOTSize(fontData->size());
343
// code shared by renameFont and renameAndActivateFont
344
// adds fontName to the font table in fontData, and writes the new font table to rewrittenFontTable
345
// returns the size of the name table (which is used by renameAndActivateFont), or 0 on early abort
346
static size_t renameFontInternal(SharedBuffer* fontData, const String& fontName, Vector<char> &rewrittenFontData)
348
size_t originalDataSize = fontData->size();
349
const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data());
352
for (t = 0; t < sfnt->numTables; ++t) {
353
if (sfnt->tables[t].tag == 'name')
356
if (t == sfnt->numTables)
359
const int nameRecordCount = 5;
361
// Rounded up to a multiple of 4 to simplify the checksum calculation.
362
size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4;
364
rewrittenFontData.resize(fontData->size() + nameTableSize);
365
char* data = rewrittenFontData.data();
366
memcpy(data, fontData->data(), originalDataSize);
368
// Make the table directory entry point to the new 'name' table.
369
sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data);
370
rewrittenSfnt->tables[t].length = nameTableSize;
371
rewrittenSfnt->tables[t].offset = originalDataSize;
373
// Write the new 'name' table after the original font data.
374
nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize);
376
name->count = nameRecordCount;
377
name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord);
378
for (unsigned i = 0; i < nameRecordCount; ++i) {
379
name->nameRecords[i].platformID = 3;
380
name->nameRecords[i].encodingID = 1;
381
name->nameRecords[i].languageID = 0x0409;
382
name->nameRecords[i].offset = 0;
383
name->nameRecords[i].length = fontName.length() * sizeof(UChar);
386
// The required 'name' record types: Family, Style, Unique, Full and PostScript.
387
name->nameRecords[0].nameID = 1;
388
name->nameRecords[1].nameID = 2;
389
name->nameRecords[2].nameID = 3;
390
name->nameRecords[3].nameID = 4;
391
name->nameRecords[4].nameID = 6;
393
for (unsigned i = 0; i < fontName.length(); ++i)
394
reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i];
396
// Update the table checksum in the directory entry.
397
rewrittenSfnt->tables[t].checkSum = 0;
398
for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i)
399
rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i];
401
return nameTableSize;
405
// AddFontMemResourceEx does not exist on WinCE, so we must handle the font data manually
406
// This function just renames the font and overwrites the old font data with the new
407
bool renameFont(SharedBuffer* fontData, const String& fontName)
409
// abort if the data is too small to be a font header with a "tables" entry
410
if (fontData->size() < offsetof(sfntHeader, tables))
413
// abort if the data is too small to hold all the tables specified in the header
414
const sfntHeader* header = reinterpret_cast<const sfntHeader*>(fontData->data());
415
if (fontData->size() < offsetof(sfntHeader, tables) + header->numTables * sizeof(TableDirectoryEntry))
418
Vector<char> rewrittenFontData;
419
if (!renameFontInternal(fontData, fontName, rewrittenFontData))
423
fontData->append(rewrittenFontData.data(), rewrittenFontData.size());
427
// Rename the font and install the new font data into the system
428
HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName)
430
Vector<char> rewrittenFontData;
431
size_t nameTableSize = renameFontInternal(fontData, fontName, rewrittenFontData);
436
HANDLE fontHandle = AddFontMemResourceEx(rewrittenFontData.data(), fontData->size() + nameTableSize, 0, &numFonts);
438
if (fontHandle && numFonts != 1) {
439
RemoveFontMemResourceEx(fontHandle);