OLD | NEW |
| (Empty) |
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 & 0
xff0000) >> 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 fontPANOSE[10]; | |
53 UInt8 charset; | |
54 UInt8 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 struct sfntHeader { | |
73 Fixed version; | |
74 BigEndianUShort numTables; | |
75 BigEndianUShort searchRange; | |
76 BigEndianUShort entrySelector; | |
77 BigEndianUShort rangeShift; | |
78 TableDirectoryEntry tables[1]; | |
79 }; | |
80 | |
81 struct OS2Table { | |
82 BigEndianUShort version; | |
83 BigEndianUShort avgCharWidth; | |
84 BigEndianUShort weightClass; | |
85 BigEndianUShort widthClass; | |
86 BigEndianUShort fsType; | |
87 BigEndianUShort subscriptXSize; | |
88 BigEndianUShort subscriptYSize; | |
89 BigEndianUShort subscriptXOffset; | |
90 BigEndianUShort subscriptYOffset; | |
91 BigEndianUShort superscriptXSize; | |
92 BigEndianUShort superscriptYSize; | |
93 BigEndianUShort superscriptXOffset; | |
94 BigEndianUShort superscriptYOffset; | |
95 BigEndianUShort strikeoutSize; | |
96 BigEndianUShort strikeoutPosition; | |
97 BigEndianUShort familyClass; | |
98 UInt8 panose[10]; | |
99 BigEndianULong unicodeRange[4]; | |
100 UInt8 vendID[4]; | |
101 BigEndianUShort fsSelection; | |
102 BigEndianUShort firstCharIndex; | |
103 BigEndianUShort lastCharIndex; | |
104 BigEndianUShort typoAscender; | |
105 BigEndianUShort typoDescender; | |
106 BigEndianUShort typoLineGap; | |
107 BigEndianUShort winAscent; | |
108 BigEndianUShort winDescent; | |
109 BigEndianULong codePageRange[2]; | |
110 BigEndianUShort xHeight; | |
111 BigEndianUShort capHeight; | |
112 BigEndianUShort defaultChar; | |
113 BigEndianUShort breakChar; | |
114 BigEndianUShort maxContext; | |
115 }; | |
116 | |
117 struct headTable { | |
118 Fixed version; | |
119 Fixed fontRevision; | |
120 BigEndianULong checkSumAdjustment; | |
121 BigEndianULong magicNumber; | |
122 BigEndianUShort flags; | |
123 BigEndianUShort unitsPerEm; | |
124 long long created; | |
125 long long modified; | |
126 BigEndianUShort xMin; | |
127 BigEndianUShort xMax; | |
128 BigEndianUShort yMin; | |
129 BigEndianUShort yMax; | |
130 BigEndianUShort macStyle; | |
131 BigEndianUShort lowestRectPPEM; | |
132 BigEndianUShort fontDirectionHint; | |
133 BigEndianUShort indexToLocFormat; | |
134 BigEndianUShort glyphDataFormat; | |
135 }; | |
136 | |
137 struct nameRecord { | |
138 BigEndianUShort platformID; | |
139 BigEndianUShort encodingID; | |
140 BigEndianUShort languageID; | |
141 BigEndianUShort nameID; | |
142 BigEndianUShort length; | |
143 BigEndianUShort offset; | |
144 }; | |
145 | |
146 struct nameTable { | |
147 BigEndianUShort format; | |
148 BigEndianUShort count; | |
149 BigEndianUShort stringOffset; | |
150 nameRecord nameRecords[1]; | |
151 }; | |
152 | |
153 #pragma pack() | |
154 | |
155 static void appendBigEndianStringToEOTHeader(Vector<UInt8, 512>& eotHeader, cons
t BigEndianUShort* string, unsigned short length) | |
156 { | |
157 size_t size = eotHeader.size(); | |
158 eotHeader.resize(size + length + 2 * sizeof(unsigned short)); | |
159 UChar* dst = reinterpret_cast<UChar*>(eotHeader.data() + size); | |
160 unsigned i = 0; | |
161 dst[i++] = length; | |
162 unsigned numCharacters = length / 2; | |
163 for (unsigned j = 0; j < numCharacters; j++) | |
164 dst[i++] = string[j]; | |
165 dst[i] = 0; | |
166 } | |
167 | |
168 bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t&
overlayDst, size_t& overlaySrc, size_t& overlayLength) | |
169 { | |
170 overlayDst = 0; | |
171 overlaySrc = 0; | |
172 overlayLength = 0; | |
173 | |
174 size_t dataLength = fontData->size(); | |
175 const char* data = fontData->data(); | |
176 | |
177 eotHeader.resize(sizeof(EOTPrefix)); | |
178 EOTPrefix* prefix = reinterpret_cast<EOTPrefix*>(eotHeader.data()); | |
179 | |
180 prefix->fontDataSize = dataLength; | |
181 prefix->version = 0x00020001; | |
182 prefix->flags = 0; | |
183 | |
184 if (dataLength < offsetof(sfntHeader, tables)) | |
185 return false; | |
186 | |
187 const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(data); | |
188 | |
189 if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(Tab
leDirectoryEntry)) | |
190 return false; | |
191 | |
192 bool haveOS2 = false; | |
193 bool haveHead = false; | |
194 bool haveName = false; | |
195 | |
196 const BigEndianUShort* familyName = 0; | |
197 unsigned short familyNameLength = 0; | |
198 const BigEndianUShort* subfamilyName = 0; | |
199 unsigned short subfamilyNameLength = 0; | |
200 const BigEndianUShort* fullName = 0; | |
201 unsigned short fullNameLength = 0; | |
202 const BigEndianUShort* versionString = 0; | |
203 unsigned short versionStringLength = 0; | |
204 | |
205 for (unsigned i = 0; i < sfnt->numTables; i++) { | |
206 unsigned tableOffset = sfnt->tables[i].offset; | |
207 unsigned tableLength = sfnt->tables[i].length; | |
208 | |
209 if (dataLength < tableOffset || dataLength < tableLength || dataLength <
tableOffset + tableLength) | |
210 return false; | |
211 | |
212 unsigned tableTag = sfnt->tables[i].tag; | |
213 switch (tableTag) { | |
214 case 'OS/2': | |
215 { | |
216 if (dataLength < tableOffset + sizeof(OS2Table)) | |
217 return false; | |
218 | |
219 haveOS2 = true; | |
220 const OS2Table* OS2 = reinterpret_cast<const OS2Table*>(data
+ tableOffset); | |
221 for (unsigned j = 0; j < 10; j++) | |
222 prefix->fontPANOSE[j] = OS2->panose[j]; | |
223 prefix->italic = OS2->fsSelection & 0x01; | |
224 prefix->weight = OS2->weightClass; | |
225 // FIXME: Should use OS2->fsType, but some TrueType fonts se
t it to an over-restrictive value. | |
226 // Since ATS does not enforce this on Mac OS X, we do not en
force it either. | |
227 prefix->fsType = 0; | |
228 for (unsigned j = 0; j < 4; j++) | |
229 prefix->unicodeRange[j] = OS2->unicodeRange[j]; | |
230 for (unsigned j = 0; j < 2; j++) | |
231 prefix->codePageRange[j] = OS2->codePageRange[j]; | |
232 break; | |
233 } | |
234 case 'head': | |
235 { | |
236 if (dataLength < tableOffset + sizeof(headTable)) | |
237 return false; | |
238 | |
239 haveHead = true; | |
240 const headTable* head = reinterpret_cast<const headTable*>(d
ata + tableOffset); | |
241 prefix->checkSumAdjustment = head->checkSumAdjustment; | |
242 break; | |
243 } | |
244 case 'name': | |
245 { | |
246 if (dataLength < tableOffset + offsetof(nameTable, nameRecor
ds)) | |
247 return false; | |
248 | |
249 haveName = true; | |
250 const nameTable* name = reinterpret_cast<const nameTable*>(d
ata + tableOffset); | |
251 for (int j = 0; j < name->count; j++) { | |
252 if (dataLength < tableOffset + offsetof(nameTable, nameR
ecords) + (j + 1) * sizeof(nameRecord)) | |
253 return false; | |
254 if (name->nameRecords[j].platformID == 3 && name->nameRe
cords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) { | |
255 if (dataLength < tableOffset + name->stringOffset +
name->nameRecords[j].offset + name->nameRecords[j].length) | |
256 return false; | |
257 | |
258 unsigned short nameLength = name->nameRecords[j].len
gth; | |
259 const BigEndianUShort* nameString = reinterpret_cast
<const BigEndianUShort*>(data + tableOffset + name->stringOffset + name->nameRec
ords[j].offset); | |
260 | |
261 switch (name->nameRecords[j].nameID) { | |
262 case 1: | |
263 familyNameLength = nameLength; | |
264 familyName = nameString; | |
265 break; | |
266 case 2: | |
267 subfamilyNameLength = nameLength; | |
268 subfamilyName = nameString; | |
269 break; | |
270 case 4: | |
271 fullNameLength = nameLength; | |
272 fullName = nameString; | |
273 break; | |
274 case 5: | |
275 versionStringLength = nameLength; | |
276 versionString = nameString; | |
277 break; | |
278 default: | |
279 break; | |
280 } | |
281 } | |
282 } | |
283 break; | |
284 } | |
285 default: | |
286 break; | |
287 } | |
288 if (haveOS2 && haveHead && haveName) | |
289 break; | |
290 } | |
291 | |
292 prefix->charset = DEFAULT_CHARSET; | |
293 prefix->magicNumber = 0x504c; | |
294 prefix->reserved[0] = 0; | |
295 prefix->reserved[1] = 0; | |
296 prefix->reserved[2] = 0; | |
297 prefix->reserved[3] = 0; | |
298 prefix->padding1 = 0; | |
299 | |
300 appendBigEndianStringToEOTHeader(eotHeader, familyName, familyNameLength); | |
301 appendBigEndianStringToEOTHeader(eotHeader, subfamilyName, subfamilyNameLeng
th); | |
302 appendBigEndianStringToEOTHeader(eotHeader, versionString, versionStringLeng
th); | |
303 | |
304 // If possible, ensure that the family name is a prefix of the full name. | |
305 if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, famil
yNameLength)) { | |
306 overlaySrc = reinterpret_cast<const char*>(fullName) - data; | |
307 overlayDst = reinterpret_cast<const char*>(familyName) - data; | |
308 overlayLength = familyNameLength; | |
309 } | |
310 | |
311 appendBigEndianStringToEOTHeader(eotHeader, fullName, fullNameLength); | |
312 | |
313 unsigned short padding = 0; | |
314 eotHeader.append(reinterpret_cast<UInt8*>(&padding), sizeof(padding)); | |
315 | |
316 prefix->eotSize = eotHeader.size() + fontData->size(); | |
317 | |
318 return true; | |
319 } | |
320 | |
321 HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName) | |
322 { | |
323 size_t originalDataSize = fontData->size(); | |
324 const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data(
)); | |
325 | |
326 unsigned t; | |
327 for (t = 0; t < sfnt->numTables; ++t) { | |
328 if (sfnt->tables[t].tag == 'name') | |
329 break; | |
330 } | |
331 if (t == sfnt->numTables) | |
332 return 0; | |
333 | |
334 const int nameRecordCount = 5; | |
335 | |
336 // Rounded up to a multiple of 4 to simplify the checksum calculation. | |
337 size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount
* sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4; | |
338 | |
339 Vector<char> rewrittenFontData(fontData->size() + nameTableSize); | |
340 char* data = rewrittenFontData.data(); | |
341 memcpy(data, fontData->data(), originalDataSize); | |
342 | |
343 // Make the table directory entry point to the new 'name' table. | |
344 sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data); | |
345 rewrittenSfnt->tables[t].length = nameTableSize; | |
346 rewrittenSfnt->tables[t].offset = originalDataSize; | |
347 | |
348 // Write the new 'name' table after the original font data. | |
349 nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize); | |
350 name->format = 0; | |
351 name->count = nameRecordCount; | |
352 name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * si
zeof(nameRecord); | |
353 for (unsigned i = 0; i < nameRecordCount; ++i) { | |
354 name->nameRecords[i].platformID = 3; | |
355 name->nameRecords[i].encodingID = 1; | |
356 name->nameRecords[i].languageID = 0x0409; | |
357 name->nameRecords[i].offset = 0; | |
358 name->nameRecords[i].length = fontName.length() * sizeof(UChar); | |
359 } | |
360 | |
361 // The required 'name' record types: Family, Style, Unique, Full and PostScr
ipt. | |
362 name->nameRecords[0].nameID = 1; | |
363 name->nameRecords[1].nameID = 2; | |
364 name->nameRecords[2].nameID = 3; | |
365 name->nameRecords[3].nameID = 4; | |
366 name->nameRecords[4].nameID = 6; | |
367 | |
368 for (unsigned i = 0; i < fontName.length(); ++i) | |
369 reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->strin
gOffset)[i] = fontName[i]; | |
370 | |
371 // Update the table checksum in the directory entry. | |
372 rewrittenSfnt->tables[t].checkSum = 0; | |
373 for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i) | |
374 rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum +
reinterpret_cast<BigEndianULong*>(name)[i]; | |
375 | |
376 DWORD numFonts = 0; | |
377 HANDLE fontHandle = AddFontMemResourceEx(data, originalDataSize + nameTableS
ize, 0, &numFonts); | |
378 | |
379 if (fontHandle && numFonts != 1) { | |
380 RemoveFontMemResourceEx(fontHandle); | |
381 return 0; | |
382 } | |
383 | |
384 return fontHandle; | |
385 } | |
386 | |
387 } | |
OLD | NEW |