Reviewed by Mark Rowe.
[WebKit-https.git] / WebCore / platform / graphics / opentype / OpenTypeUtilities.cpp
1 /*
2  * Copyright (C) 2008, 2009 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "OpenTypeUtilities.h"
28
29 #include "SharedBuffer.h"
30
31 namespace WebCore {
32
33 struct BigEndianUShort { 
34     operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; }
35     BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { }
36     unsigned short v;
37 };
38
39 struct BigEndianULong { 
40     operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; }
41     BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { }
42     unsigned v;
43 };
44
45 #pragma pack(1)
46
47 struct EOTPrefix {
48     unsigned eotSize;
49     unsigned fontDataSize;
50     unsigned version;
51     unsigned flags;
52     uint8_t fontPANOSE[10];
53     uint8_t charset;
54     uint8_t italic;
55     unsigned weight;
56     unsigned short fsType;
57     unsigned short magicNumber;
58     unsigned unicodeRange[4];
59     unsigned codePageRange[2];
60     unsigned checkSumAdjustment;
61     unsigned reserved[4];
62     unsigned short padding1;
63 };
64
65 struct TableDirectoryEntry {
66     BigEndianULong tag;
67     BigEndianULong checkSum;
68     BigEndianULong offset;
69     BigEndianULong length;
70 };
71
72 #if !PLATFORM(CG)
73 // Fixed type is not defined on non-CG platforms. |version| in sfntHeader
74 // and headTable and |fontRevision| in headTable are of Fixed, but they're
75 // not actually refered to anywhere. Therefore, we just have to match
76 // the size (4 bytes). For the definition of Fixed type, see
77 // http://developer.apple.com/documentation/mac/Legacy/GXEnvironment/GXEnvironment-356.html#HEADING356-6.
78 typedef int32_t Fixed;
79 #endif
80
81 struct sfntHeader {
82     Fixed version;
83     BigEndianUShort numTables;
84     BigEndianUShort searchRange;
85     BigEndianUShort entrySelector;
86     BigEndianUShort rangeShift;
87     TableDirectoryEntry tables[1];
88 };
89
90 struct OS2Table {
91     BigEndianUShort version;
92     BigEndianUShort avgCharWidth;
93     BigEndianUShort weightClass;
94     BigEndianUShort widthClass;
95     BigEndianUShort fsType;
96     BigEndianUShort subscriptXSize;
97     BigEndianUShort subscriptYSize;
98     BigEndianUShort subscriptXOffset;
99     BigEndianUShort subscriptYOffset;
100     BigEndianUShort superscriptXSize;
101     BigEndianUShort superscriptYSize;
102     BigEndianUShort superscriptXOffset;
103     BigEndianUShort superscriptYOffset;
104     BigEndianUShort strikeoutSize;
105     BigEndianUShort strikeoutPosition;
106     BigEndianUShort familyClass;
107     uint8_t panose[10];
108     BigEndianULong unicodeRange[4];
109     uint8_t vendID[4];
110     BigEndianUShort fsSelection;
111     BigEndianUShort firstCharIndex;
112     BigEndianUShort lastCharIndex;
113     BigEndianUShort typoAscender;
114     BigEndianUShort typoDescender;
115     BigEndianUShort typoLineGap;
116     BigEndianUShort winAscent;
117     BigEndianUShort winDescent;
118     BigEndianULong codePageRange[2];
119     BigEndianUShort xHeight;
120     BigEndianUShort capHeight;
121     BigEndianUShort defaultChar;
122     BigEndianUShort breakChar;
123     BigEndianUShort maxContext;
124 };
125
126 struct headTable {
127     Fixed version;
128     Fixed fontRevision;
129     BigEndianULong checkSumAdjustment;
130     BigEndianULong magicNumber;
131     BigEndianUShort flags;
132     BigEndianUShort unitsPerEm;
133     long long created;
134     long long modified;
135     BigEndianUShort xMin;
136     BigEndianUShort xMax;
137     BigEndianUShort yMin;
138     BigEndianUShort yMax;
139     BigEndianUShort macStyle;
140     BigEndianUShort lowestRectPPEM;
141     BigEndianUShort fontDirectionHint;
142     BigEndianUShort indexToLocFormat;
143     BigEndianUShort glyphDataFormat;
144 };
145
146 struct nameRecord {
147     BigEndianUShort platformID;
148     BigEndianUShort encodingID;
149     BigEndianUShort languageID;
150     BigEndianUShort nameID;
151     BigEndianUShort length;
152     BigEndianUShort offset;
153 };
154
155 struct nameTable {
156     BigEndianUShort format;
157     BigEndianUShort count;
158     BigEndianUShort stringOffset;
159     nameRecord nameRecords[1];
160 };
161
162 #pragma pack()
163
164 EOTHeader::EOTHeader()
165 {
166     m_buffer.resize(sizeof(EOTPrefix));
167 }
168
169 void EOTHeader::updateEOTSize(size_t fontDataSize)
170 {
171     prefix()->eotSize = m_buffer.size() + fontDataSize;
172 }
173
174 void EOTHeader::appendBigEndianString(const BigEndianUShort* string, unsigned short length)
175 {
176     size_t oldSize = m_buffer.size();
177     m_buffer.resize(oldSize + length + 2 * sizeof(unsigned short));
178     UChar* dst = reinterpret_cast<UChar*>(m_buffer.data() + oldSize);
179     unsigned i = 0;
180     dst[i++] = length;
181     unsigned numCharacters = length / 2;
182     for (unsigned j = 0; j < numCharacters; j++)
183         dst[i++] = string[j];
184     dst[i] = 0;
185 }
186
187 void EOTHeader::appendPaddingShort()
188 {
189     unsigned short padding = 0;
190     m_buffer.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding));
191 }
192
193 bool getEOTHeader(SharedBuffer* fontData, EOTHeader& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength)
194 {
195     overlayDst = 0;
196     overlaySrc = 0;
197     overlayLength = 0;
198
199     size_t dataLength = fontData->size();
200     const char* data = fontData->data();
201
202     EOTPrefix* prefix = eotHeader.prefix();
203
204     prefix->fontDataSize = dataLength;
205     prefix->version = 0x00020001;
206     prefix->flags = 0;
207
208     if (dataLength < offsetof(sfntHeader, tables))
209         return false;
210
211     const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data);
212
213     if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry))
214         return false;
215
216     bool haveOS2 = false;
217     bool haveHead = false;
218     bool haveName = false;
219
220     const BigEndianUShort* familyName = 0;
221     unsigned short familyNameLength = 0;
222     const BigEndianUShort* subfamilyName = 0;
223     unsigned short subfamilyNameLength = 0;
224     const BigEndianUShort* fullName = 0;
225     unsigned short fullNameLength = 0;
226     const BigEndianUShort* versionString = 0;
227     unsigned short versionStringLength = 0;
228
229     for (unsigned i = 0; i < sfnt->numTables; i++) {
230         unsigned tableOffset = sfnt->tables[i].offset;
231         unsigned tableLength = sfnt->tables[i].length;
232
233         if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength)
234             return false;
235
236         unsigned tableTag = sfnt->tables[i].tag;
237         switch (tableTag) {
238             case 'OS/2':
239                 {
240                     if (dataLength < tableOffset + sizeof(OS2Table))
241                         return false;
242
243                     haveOS2 = true;
244                     const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data + tableOffset);
245                     for (unsigned j = 0; j < 10; j++)
246                         prefix->fontPANOSE[j] = OS2->panose[j];
247                     prefix->italic = OS2->fsSelection & 0x01;
248                     prefix->weight = OS2->weightClass;
249                     // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value.
250                     // Since ATS does not enforce this on Mac OS X, we do not enforce it either.
251                     prefix->fsType = 0;            
252                     for (unsigned j = 0; j < 4; j++)
253                         prefix->unicodeRange[j] = OS2->unicodeRange[j];
254                     for (unsigned j = 0; j < 2; j++)
255                         prefix->codePageRange[j] = OS2->codePageRange[j];
256                     break;
257                 }
258             case 'head':
259                 {
260                     if (dataLength < tableOffset + sizeof(headTable))
261                         return false;
262
263                     haveHead = true;
264                     const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset);
265                     prefix->checkSumAdjustment = head->checkSumAdjustment;
266                     break;
267                 }
268             case 'name':
269                 {
270                     if (dataLength < tableOffset + offsetof(nameTable, nameRecords))
271                         return false;
272
273                     haveName = true;
274                     const nameTable* name = reinterpret_cast<const nameTable*>(data + tableOffset);
275                     for (int j = 0; j < name->count; j++) {
276                         if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord))
277                             return false;
278                         if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) {
279                             if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length)
280                                 return false;
281
282                             unsigned short nameLength = name->nameRecords[j].length;
283                             const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset);
284                             
285                             switch (name->nameRecords[j].nameID) {
286                                 case 1:
287                                     familyNameLength = nameLength;
288                                     familyName = nameString;
289                                     break;
290                                 case 2:
291                                     subfamilyNameLength = nameLength;
292                                     subfamilyName = nameString;
293                                     break;
294                                 case 4:
295                                     fullNameLength = nameLength;
296                                     fullName = nameString;
297                                     break;
298                                 case 5:
299                                     versionStringLength = nameLength;
300                                     versionString = nameString;
301                                     break;
302                                 default:
303                                     break;
304                             }
305                         }
306                     }
307                     break;
308                 }
309             default:
310                 break;
311         }
312         if (haveOS2 && haveHead && haveName)
313             break;
314     }
315
316     prefix->charset = DEFAULT_CHARSET;
317     prefix->magicNumber = 0x504c;
318     prefix->reserved[0] = 0;
319     prefix->reserved[1] = 0;
320     prefix->reserved[2] = 0;
321     prefix->reserved[3] = 0;
322     prefix->padding1 = 0;
323
324     eotHeader.appendBigEndianString(familyName, familyNameLength);
325     eotHeader.appendBigEndianString(subfamilyName, subfamilyNameLength);
326     eotHeader.appendBigEndianString(versionString, versionStringLength);
327
328     // If possible, ensure that the family name is a prefix of the full name.
329     if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) {
330         overlaySrc = reinterpret_cast<const char*>(fullName) - data;
331         overlayDst = reinterpret_cast<const char*>(familyName) - data;
332         overlayLength = familyNameLength;
333     }
334     eotHeader.appendBigEndianString(fullName, fullNameLength);
335
336     eotHeader.appendPaddingShort();
337     eotHeader.updateEOTSize(fontData->size());
338
339     return true;
340 }
341
342 HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName)
343 {
344     size_t originalDataSize = fontData->size();
345     const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data());
346
347     unsigned t;
348     for (t = 0; t < sfnt->numTables; ++t) {
349         if (sfnt->tables[t].tag == 'name')
350             break;
351     }
352     if (t == sfnt->numTables)
353         return 0;
354
355     const int nameRecordCount = 5;
356
357     // Rounded up to a multiple of 4 to simplify the checksum calculation.
358     size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4;
359
360     Vector<char> rewrittenFontData(fontData->size() + nameTableSize);
361     char* data = rewrittenFontData.data();
362     memcpy(data, fontData->data(), originalDataSize);
363
364     // Make the table directory entry point to the new 'name' table.
365     sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data);
366     rewrittenSfnt->tables[t].length = nameTableSize;
367     rewrittenSfnt->tables[t].offset = originalDataSize;
368
369     // Write the new 'name' table after the original font data.
370     nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize);
371     name->format = 0;
372     name->count = nameRecordCount;
373     name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord);
374     for (unsigned i = 0; i < nameRecordCount; ++i) {
375         name->nameRecords[i].platformID = 3;
376         name->nameRecords[i].encodingID = 1;
377         name->nameRecords[i].languageID = 0x0409;
378         name->nameRecords[i].offset = 0;
379         name->nameRecords[i].length = fontName.length() * sizeof(UChar);
380     }
381
382     // The required 'name' record types: Family, Style, Unique, Full and PostScript.
383     name->nameRecords[0].nameID = 1;
384     name->nameRecords[1].nameID = 2;
385     name->nameRecords[2].nameID = 3;
386     name->nameRecords[3].nameID = 4;
387     name->nameRecords[4].nameID = 6;
388
389     for (unsigned i = 0; i < fontName.length(); ++i)
390         reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i];
391
392     // Update the table checksum in the directory entry.
393     rewrittenSfnt->tables[t].checkSum = 0;
394     for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i)
395         rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i];
396
397     DWORD numFonts = 0;
398     HANDLE fontHandle = AddFontMemResourceEx(data, originalDataSize + nameTableSize, 0, &numFonts);
399
400     if (fontHandle && numFonts != 1) {
401         RemoveFontMemResourceEx(fontHandle);
402         return 0;
403     }
404
405     return fontHandle;
406 }
407
408 }