16c3c002ad03c7b5f9a8200b3c91c8eb681ab4fa
[WebKit-https.git] / WebCore / platform / graphics / opentype / OpenTypeUtilities.cpp
1 /*
2  * Copyright (C) 2008 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 static void appendBigEndianStringToEOTHeader(Vector<uint8_t, 512>& eotHeader, const BigEndianUShort* string, unsigned short length)
165 {
166     size_t size = eotHeader.size();
167     eotHeader.resize(size + length + 2 * sizeof(unsigned short));
168     UChar* dst = reinterpret_cast<UChar*>(eotHeader.data() + size);
169     unsigned i = 0;
170     dst[i++] = length;
171     unsigned numCharacters = length / 2;
172     for (unsigned j = 0; j < numCharacters; j++)
173         dst[i++] = string[j];
174     dst[i] = 0;
175 }
176
177 bool getEOTHeader(SharedBuffer* fontData, Vector<uint8_t, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength)
178 {
179     overlayDst = 0;
180     overlaySrc = 0;
181     overlayLength = 0;
182
183     size_t dataLength = fontData->size();
184     const char* data = fontData->data();
185
186     eotHeader.resize(sizeof(EOTPrefix));
187     EOTPrefix* prefix = reinterpret_cast<EOTPrefix*>(eotHeader.data());
188
189     prefix->fontDataSize = dataLength;
190     prefix->version = 0x00020001;
191     prefix->flags = 0;
192
193     if (dataLength < offsetof(sfntHeader, tables))
194         return false;
195
196     const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data);
197
198     if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry))
199         return false;
200
201     bool haveOS2 = false;
202     bool haveHead = false;
203     bool haveName = false;
204
205     const BigEndianUShort* familyName = 0;
206     unsigned short familyNameLength = 0;
207     const BigEndianUShort* subfamilyName = 0;
208     unsigned short subfamilyNameLength = 0;
209     const BigEndianUShort* fullName = 0;
210     unsigned short fullNameLength = 0;
211     const BigEndianUShort* versionString = 0;
212     unsigned short versionStringLength = 0;
213
214     for (unsigned i = 0; i < sfnt->numTables; i++) {
215         unsigned tableOffset = sfnt->tables[i].offset;
216         unsigned tableLength = sfnt->tables[i].length;
217
218         if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength)
219             return false;
220
221         unsigned tableTag = sfnt->tables[i].tag;
222         switch (tableTag) {
223             case 'OS/2':
224                 {
225                     if (dataLength < tableOffset + sizeof(OS2Table))
226                         return false;
227
228                     haveOS2 = true;
229                     const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data + tableOffset);
230                     for (unsigned j = 0; j < 10; j++)
231                         prefix->fontPANOSE[j] = OS2->panose[j];
232                     prefix->italic = OS2->fsSelection & 0x01;
233                     prefix->weight = OS2->weightClass;
234                     // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value.
235                     // Since ATS does not enforce this on Mac OS X, we do not enforce it either.
236                     prefix->fsType = 0;            
237                     for (unsigned j = 0; j < 4; j++)
238                         prefix->unicodeRange[j] = OS2->unicodeRange[j];
239                     for (unsigned j = 0; j < 2; j++)
240                         prefix->codePageRange[j] = OS2->codePageRange[j];
241                     break;
242                 }
243             case 'head':
244                 {
245                     if (dataLength < tableOffset + sizeof(headTable))
246                         return false;
247
248                     haveHead = true;
249                     const headTable* head = reinterpret_cast<const headTable*>(data + tableOffset);
250                     prefix->checkSumAdjustment = head->checkSumAdjustment;
251                     break;
252                 }
253             case 'name':
254                 {
255                     if (dataLength < tableOffset + offsetof(nameTable, nameRecords))
256                         return false;
257
258                     haveName = true;
259                     const nameTable* name = reinterpret_cast<const nameTable*>(data + tableOffset);
260                     for (int j = 0; j < name->count; j++) {
261                         if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord))
262                             return false;
263                         if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) {
264                             if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length)
265                                 return false;
266
267                             unsigned short nameLength = name->nameRecords[j].length;
268                             const BigEndianUShort* nameString = reinterpret_cast<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRecords[j].offset);
269                             
270                             switch (name->nameRecords[j].nameID) {
271                                 case 1:
272                                     familyNameLength = nameLength;
273                                     familyName = nameString;
274                                     break;
275                                 case 2:
276                                     subfamilyNameLength = nameLength;
277                                     subfamilyName = nameString;
278                                     break;
279                                 case 4:
280                                     fullNameLength = nameLength;
281                                     fullName = nameString;
282                                     break;
283                                 case 5:
284                                     versionStringLength = nameLength;
285                                     versionString = nameString;
286                                     break;
287                                 default:
288                                     break;
289                             }
290                         }
291                     }
292                     break;
293                 }
294             default:
295                 break;
296         }
297         if (haveOS2 && haveHead && haveName)
298             break;
299     }
300
301     prefix->charset = DEFAULT_CHARSET;
302     prefix->magicNumber = 0x504c;
303     prefix->reserved[0] = 0;
304     prefix->reserved[1] = 0;
305     prefix->reserved[2] = 0;
306     prefix->reserved[3] = 0;
307     prefix->padding1 = 0;
308
309     appendBigEndianStringToEOTHeader(eotHeader, familyName, familyNameLength);
310     appendBigEndianStringToEOTHeader(eotHeader, subfamilyName, subfamilyNameLength);
311     appendBigEndianStringToEOTHeader(eotHeader, versionString, versionStringLength);
312
313     // If possible, ensure that the family name is a prefix of the full name.
314     if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) {
315         overlaySrc = reinterpret_cast<const char*>(fullName) - data;
316         overlayDst = reinterpret_cast<const char*>(familyName) - data;
317         overlayLength = familyNameLength;
318     }
319
320     appendBigEndianStringToEOTHeader(eotHeader, fullName, fullNameLength);
321
322     unsigned short padding = 0;
323     eotHeader.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding));
324
325     prefix->eotSize = eotHeader.size() + fontData->size();
326
327     return true;
328 }
329
330 HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName)
331 {
332     size_t originalDataSize = fontData->size();
333     const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data());
334
335     unsigned t;
336     for (t = 0; t < sfnt->numTables; ++t) {
337         if (sfnt->tables[t].tag == 'name')
338             break;
339     }
340     if (t == sfnt->numTables)
341         return 0;
342
343     const int nameRecordCount = 5;
344
345     // Rounded up to a multiple of 4 to simplify the checksum calculation.
346     size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4;
347
348     Vector<char> rewrittenFontData(fontData->size() + nameTableSize);
349     char* data = rewrittenFontData.data();
350     memcpy(data, fontData->data(), originalDataSize);
351
352     // Make the table directory entry point to the new 'name' table.
353     sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data);
354     rewrittenSfnt->tables[t].length = nameTableSize;
355     rewrittenSfnt->tables[t].offset = originalDataSize;
356
357     // Write the new 'name' table after the original font data.
358     nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize);
359     name->format = 0;
360     name->count = nameRecordCount;
361     name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord);
362     for (unsigned i = 0; i < nameRecordCount; ++i) {
363         name->nameRecords[i].platformID = 3;
364         name->nameRecords[i].encodingID = 1;
365         name->nameRecords[i].languageID = 0x0409;
366         name->nameRecords[i].offset = 0;
367         name->nameRecords[i].length = fontName.length() * sizeof(UChar);
368     }
369
370     // The required 'name' record types: Family, Style, Unique, Full and PostScript.
371     name->nameRecords[0].nameID = 1;
372     name->nameRecords[1].nameID = 2;
373     name->nameRecords[2].nameID = 3;
374     name->nameRecords[3].nameID = 4;
375     name->nameRecords[4].nameID = 6;
376
377     for (unsigned i = 0; i < fontName.length(); ++i)
378         reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i];
379
380     // Update the table checksum in the directory entry.
381     rewrittenSfnt->tables[t].checkSum = 0;
382     for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i)
383         rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i];
384
385     DWORD numFonts = 0;
386     HANDLE fontHandle = AddFontMemResourceEx(data, originalDataSize + nameTableSize, 0, &numFonts);
387
388     if (fontHandle && numFonts != 1) {
389         RemoveFontMemResourceEx(fontHandle);
390         return 0;
391     }
392
393     return fontHandle;
394 }
395
396 }