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 { 35 operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; } 36 BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { } 37 unsigned short v; 38 }; 39 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) { } 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 165 EOTHeader::EOTHeader() 166 { 167 m_buffer.resize(sizeof(EOTPrefix)); 168 } 169 170 void EOTHeader::updateEOTSize(size_t fontDataSize) 171 { 172 prefix()->eotSize = m_buffer.size() + fontDataSize; 173 } 174 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 188 void EOTHeader::appendPaddingShort() 189 { 190 unsigned short padding = 0; 191 m_buffer.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding)); 192 } 193 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 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 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 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