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 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 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
32namespace WebCore {
33
34struct 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
40struct 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
48struct 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
66struct 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.
79typedef int32_t Fixed;
80#endif
81
82struct sfntHeader {
83    Fixed version;
84    BigEndianUShort numTables;
85    BigEndianUShort searchRange;
86    BigEndianUShort entrySelector;
87    BigEndianUShort rangeShift;
88    TableDirectoryEntry tables[1];
89};
90
91struct 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
127struct 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
147struct nameRecord {
148    BigEndianUShort platformID;
149    BigEndianUShort encodingID;
150    BigEndianUShort languageID;
151    BigEndianUShort nameID;
152    BigEndianUShort length;
153    BigEndianUShort offset;
154};
155
156struct nameTable {
157    BigEndianUShort format;
158    BigEndianUShort count;
159    BigEndianUShort stringOffset;
160    nameRecord nameRecords[1];
161};
162
163#pragma pack()
164
165EOTHeader::EOTHeader()
166{
167    m_buffer.resize(sizeof(EOTPrefix));
168}
169
170void EOTHeader::updateEOTSize(size_t fontDataSize)
171{
172    prefix()->eotSize = m_buffer.size() + fontDataSize;
173}
174
175void 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
188void EOTHeader::appendPaddingShort()
189{
190    unsigned short padding = 0;
191    m_buffer.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding));
192}
193
194bool 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// adds fontName to the font table in fontData, and writes the new font table to rewrittenFontTable
344// returns the size of the name table (which is used by renameAndActivateFont), or 0 on early abort
345bool renameFont(const SharedBuffer& fontData, const String& fontName, Vector<char>& rewrittenFontData)
346{
347    size_t originalDataSize = fontData.size();
348    const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData.data());
349
350    // Abort if the data is too small to be a font header with a "tables" entry.
351    if (originalDataSize < offsetof(sfntHeader, tables))
352        return false;
353
354    // Abort if the data is too small to hold all the tables specified in the header.
355    if (originalDataSize < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry))
356        return false;
357
358    unsigned t;
359    for (t = 0; t < sfnt->numTables; ++t) {
360        if (sfnt->tables[t].tag == 'name')
361            break;
362    }
363    if (t == sfnt->numTables)
364        return false;
365
366    const int nameRecordCount = 5;
367
368    // Rounded up to a multiple of 4 to simplify the checksum calculation.
369    size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4;
370
371    rewrittenFontData.resize(fontData.size() + nameTableSize);
372    char* data = rewrittenFontData.data();
373    memcpy(data, fontData.data(), originalDataSize);
374
375    // Make the table directory entry point to the new 'name' table.
376    sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data);
377    rewrittenSfnt->tables[t].length = nameTableSize;
378    rewrittenSfnt->tables[t].offset = originalDataSize;
379
380    // Write the new 'name' table after the original font data.
381    nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize);
382    name->format = 0;
383    name->count = nameRecordCount;
384    name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord);
385    for (unsigned i = 0; i < nameRecordCount; ++i) {
386        name->nameRecords[i].platformID = 3;
387        name->nameRecords[i].encodingID = 1;
388        name->nameRecords[i].languageID = 0x0409;
389        name->nameRecords[i].offset = 0;
390        name->nameRecords[i].length = fontName.length() * sizeof(UChar);
391    }
392
393    // The required 'name' record types: Family, Style, Unique, Full and PostScript.
394    name->nameRecords[0].nameID = 1;
395    name->nameRecords[1].nameID = 2;
396    name->nameRecords[2].nameID = 3;
397    name->nameRecords[3].nameID = 4;
398    name->nameRecords[4].nameID = 6;
399
400    for (unsigned i = 0; i < fontName.length(); ++i)
401        reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i];
402
403    // Update the table checksum in the directory entry.
404    rewrittenSfnt->tables[t].checkSum = 0;
405    for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i)
406        rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i];
407
408    return true;
409}
410
411#if !OS(WINCE)
412// Rename the font and install the new font data into the system
413HANDLE renameAndActivateFont(const SharedBuffer& fontData, const String& fontName)
414{
415    Vector<char> rewrittenFontData;
416    if (!renameFont(fontData, fontName, rewrittenFontData))
417        return 0;
418
419    DWORD numFonts = 0;
420    HANDLE fontHandle = AddFontMemResourceEx(rewrittenFontData.data(), rewrittenFontData.size(), 0, &numFonts);
421
422    if (fontHandle && numFonts < 1) {
423        RemoveFontMemResourceEx(fontHandle);
424        return 0;
425    }
426
427    return fontHandle;
428}
429#endif
430
431}
432