Home | History | Annotate | Download | only in opentype
      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 !PLATFORM(CG)
     74 // Fixed type is not defined on non-CG 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