• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008, 2009 Apple Inc.  All rights reserved.
3  * Copyright (C) 2009 Torch Mobile, Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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.
25  */
26 
27 #include "config.h"
28 #include "OpenTypeUtilities.h"
29 
30 #include "SharedBuffer.h"
31 
32 namespace WebCore {
33 
34 struct BigEndianUShort {
operator unsigned shortWebCore::BigEndianUShort35     operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; }
BigEndianUShortWebCore::BigEndianUShort36     BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { }
37     unsigned short v;
38 };
39 
40 struct BigEndianULong {
operator unsignedWebCore::BigEndianULong41     operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; }
BigEndianULongWebCore::BigEndianULong42     BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { }
43     unsigned v;
44 };
45 
46 #pragma pack(1)
47 
48 struct EOTPrefix {
49     unsigned eotSize;
50     unsigned fontDataSize;
51     unsigned version;
52     unsigned flags;
53     uint8_t fontPANOSE[10];
54     uint8_t charset;
55     uint8_t italic;
56     unsigned weight;
57     unsigned short fsType;
58     unsigned short magicNumber;
59     unsigned unicodeRange[4];
60     unsigned codePageRange[2];
61     unsigned checkSumAdjustment;
62     unsigned reserved[4];
63     unsigned short padding1;
64 };
65 
66 struct TableDirectoryEntry {
67     BigEndianULong tag;
68     BigEndianULong checkSum;
69     BigEndianULong offset;
70     BigEndianULong length;
71 };
72 
73 #if !USE(CG) || !defined(COREGRAPHICS_INCLUDES_CORESERVICES_HEADER)
74 // Fixed type is not defined on non-CG and Windows 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;
80 #endif
81 
82 struct sfntHeader {
83     Fixed version;
84     BigEndianUShort numTables;
85     BigEndianUShort searchRange;
86     BigEndianUShort entrySelector;
87     BigEndianUShort rangeShift;
88     TableDirectoryEntry tables[1];
89 };
90 
91 struct OS2Table {
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;
108     uint8_t panose[10];
109     BigEndianULong unicodeRange[4];
110     uint8_t vendID[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;
125 };
126 
127 struct headTable {
128     Fixed version;
129     Fixed fontRevision;
130     BigEndianULong checkSumAdjustment;
131     BigEndianULong magicNumber;
132     BigEndianUShort flags;
133     BigEndianUShort unitsPerEm;
134     long long created;
135     long long modified;
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;
145 };
146 
147 struct nameRecord {
148     BigEndianUShort platformID;
149     BigEndianUShort encodingID;
150     BigEndianUShort languageID;
151     BigEndianUShort nameID;
152     BigEndianUShort length;
153     BigEndianUShort offset;
154 };
155 
156 struct nameTable {
157     BigEndianUShort format;
158     BigEndianUShort count;
159     BigEndianUShort stringOffset;
160     nameRecord nameRecords[1];
161 };
162 
163 #pragma pack()
164 
EOTHeader()165 EOTHeader::EOTHeader()
166 {
167     m_buffer.resize(sizeof(EOTPrefix));
168 }
169 
updateEOTSize(size_t fontDataSize)170 void EOTHeader::updateEOTSize(size_t fontDataSize)
171 {
172     prefix()->eotSize = m_buffer.size() + fontDataSize;
173 }
174 
appendBigEndianString(const BigEndianUShort * string,unsigned short length)175 void EOTHeader::appendBigEndianString(const BigEndianUShort* string, unsigned short length)
176 {
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);
180     unsigned i = 0;
181     dst[i++] = length;
182     unsigned numCharacters = length / 2;
183     for (unsigned j = 0; j < numCharacters; j++)
184         dst[i++] = string[j];
185     dst[i] = 0;
186 }
187 
appendPaddingShort()188 void EOTHeader::appendPaddingShort()
189 {
190     unsigned short padding = 0;
191     m_buffer.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding));
192 }
193 
getEOTHeader(SharedBuffer * fontData,EOTHeader & eotHeader,size_t & overlayDst,size_t & overlaySrc,size_t & overlayLength)194 bool getEOTHeader(SharedBuffer* fontData, EOTHeader& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength)
195 {
196     overlayDst = 0;
197     overlaySrc = 0;
198     overlayLength = 0;
199 
200     size_t dataLength = fontData->size();
201     const char* data = fontData->data();
202 
203     EOTPrefix* prefix = eotHeader.prefix();
204 
205     prefix->fontDataSize = dataLength;
206     prefix->version = 0x00020001;
207     prefix->flags = 0;
208 
209     if (dataLength < offsetof(sfntHeader, tables))
210         return false;
211 
212     const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data);
213 
214     if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry))
215         return false;
216 
217     bool haveOS2 = false;
218     bool haveHead = false;
219     bool haveName = false;
220 
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;
229 
230     for (unsigned i = 0; i < sfnt->numTables; i++) {
231         unsigned tableOffset = sfnt->tables[i].offset;
232         unsigned tableLength = sfnt->tables[i].length;
233 
234         if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength)
235             return false;
236 
237         unsigned tableTag = sfnt->tables[i].tag;
238         switch (tableTag) {
239             case 'OS/2':
240                 {
241                     if (dataLength < tableOffset + sizeof(OS2Table))
242                         return false;
243 
244                     haveOS2 = true;
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.
252                     prefix->fsType = 0;
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];
257                     break;
258                 }
259             case 'head':
260                 {
261                     if (dataLength < tableOffset + sizeof(headTable))
262                         return false;
263 
264                     haveHead = true;
265                     const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset);
266                     prefix->checkSumAdjustment = head->checkSumAdjustment;
267                     break;
268                 }
269             case 'name':
270                 {
271                     if (dataLength < tableOffset + offsetof(nameTable, nameRecords))
272                         return false;
273 
274                     haveName = true;
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))
278                             return false;
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)
281                                 return false;
282 
283                             unsigned short nameLength = name->nameRecords[j].length;
284                             const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset);
285 
286                             switch (name->nameRecords[j].nameID) {
287                                 case 1:
288                                     familyNameLength = nameLength;
289                                     familyName = nameString;
290                                     break;
291                                 case 2:
292                                     subfamilyNameLength = nameLength;
293                                     subfamilyName = nameString;
294                                     break;
295                                 case 4:
296                                     fullNameLength = nameLength;
297                                     fullName = nameString;
298                                     break;
299                                 case 5:
300                                     versionStringLength = nameLength;
301                                     versionString = nameString;
302                                     break;
303                                 default:
304                                     break;
305                             }
306                         }
307                     }
308                     break;
309                 }
310             default:
311                 break;
312         }
313         if (haveOS2 && haveHead && haveName)
314             break;
315     }
316 
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;
324 
325     eotHeader.appendBigEndianString(familyName, familyNameLength);
326     eotHeader.appendBigEndianString(subfamilyName, subfamilyNameLength);
327     eotHeader.appendBigEndianString(versionString, versionStringLength);
328 
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;
334     }
335     eotHeader.appendBigEndianString(fullName, fullNameLength);
336 
337     eotHeader.appendPaddingShort();
338     eotHeader.updateEOTSize(fontData->size());
339 
340     return true;
341 }
342 
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
renameFontInternal(SharedBuffer * fontData,const String & fontName,Vector<char> & rewrittenFontData)346 static size_t renameFontInternal(SharedBuffer* fontData, const String& fontName, Vector<char> &rewrittenFontData)
347 {
348     size_t originalDataSize = fontData->size();
349     const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data());
350 
351     unsigned t;
352     for (t = 0; t < sfnt->numTables; ++t) {
353         if (sfnt->tables[t].tag == 'name')
354             break;
355     }
356     if (t == sfnt->numTables)
357         return 0;
358 
359     const int nameRecordCount = 5;
360 
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;
363 
364     rewrittenFontData.resize(fontData->size() + nameTableSize);
365     char* data = rewrittenFontData.data();
366     memcpy(data, fontData->data(), originalDataSize);
367 
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;
372 
373     // Write the new 'name' table after the original font data.
374     nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize);
375     name->format = 0;
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);
384     }
385 
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;
392 
393     for (unsigned i = 0; i < fontName.length(); ++i)
394         reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i];
395 
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];
400 
401     return nameTableSize;
402 }
403 
404 #if OS(WINCE)
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
renameFont(SharedBuffer * fontData,const String & fontName)407 bool renameFont(SharedBuffer* fontData, const String& fontName)
408 {
409     // abort if the data is too small to be a font header with a "tables" entry
410     if (fontData->size() < offsetof(sfntHeader, tables))
411         return false;
412 
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))
416         return false;
417 
418     Vector<char> rewrittenFontData;
419     if (!renameFontInternal(fontData, fontName, rewrittenFontData))
420         return false;
421 
422     fontData->clear();
423     fontData->append(rewrittenFontData.data(), rewrittenFontData.size());
424     return true;
425 }
426 #else
427 // Rename the font and install the new font data into the system
renameAndActivateFont(SharedBuffer * fontData,const String & fontName)428 HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName)
429 {
430     Vector<char> rewrittenFontData;
431     size_t nameTableSize = renameFontInternal(fontData, fontName, rewrittenFontData);
432     if (!nameTableSize)
433         return 0;
434 
435     DWORD numFonts = 0;
436     HANDLE fontHandle = AddFontMemResourceEx(rewrittenFontData.data(), fontData->size() + nameTableSize, 0, &numFonts);
437 
438     if (fontHandle && numFonts < 1) {
439         RemoveFontMemResourceEx(fontHandle);
440         return 0;
441     }
442 
443     return fontHandle;
444 }
445 #endif
446 
447 }
448