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 "platform/fonts/opentype/OpenTypeUtilities.h"
29
30#include "platform/SharedBuffer.h"
31
32namespace blink {
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 TableDirectoryEntry {
49    BigEndianULong tag;
50    BigEndianULong checkSum;
51    BigEndianULong offset;
52    BigEndianULong length;
53};
54
55// Fixed type is not defined on non-CG and Windows platforms. |version| in sfntHeader
56// and headTable and |fontRevision| in headTable are of Fixed, but they're
57// not actually refered to anywhere. Therefore, we just have to match
58// the size (4 bytes). For the definition of Fixed type, see
59// http://developer.apple.com/documentation/mac/Legacy/GXEnvironment/GXEnvironment-356.html#HEADING356-6.
60typedef int32_t Fixed;
61
62struct sfntHeader {
63    Fixed version;
64    BigEndianUShort numTables;
65    BigEndianUShort searchRange;
66    BigEndianUShort entrySelector;
67    BigEndianUShort rangeShift;
68    TableDirectoryEntry tables[1];
69};
70
71struct OS2Table {
72    BigEndianUShort version;
73    BigEndianUShort avgCharWidth;
74    BigEndianUShort weightClass;
75    BigEndianUShort widthClass;
76    BigEndianUShort fsType;
77    BigEndianUShort subscriptXSize;
78    BigEndianUShort subscriptYSize;
79    BigEndianUShort subscriptXOffset;
80    BigEndianUShort subscriptYOffset;
81    BigEndianUShort superscriptXSize;
82    BigEndianUShort superscriptYSize;
83    BigEndianUShort superscriptXOffset;
84    BigEndianUShort superscriptYOffset;
85    BigEndianUShort strikeoutSize;
86    BigEndianUShort strikeoutPosition;
87    BigEndianUShort familyClass;
88    uint8_t panose[10];
89    BigEndianULong unicodeRange[4];
90    uint8_t vendID[4];
91    BigEndianUShort fsSelection;
92    BigEndianUShort firstCharIndex;
93    BigEndianUShort lastCharIndex;
94    BigEndianUShort typoAscender;
95    BigEndianUShort typoDescender;
96    BigEndianUShort typoLineGap;
97    BigEndianUShort winAscent;
98    BigEndianUShort winDescent;
99    BigEndianULong codePageRange[2];
100    BigEndianUShort xHeight;
101    BigEndianUShort capHeight;
102    BigEndianUShort defaultChar;
103    BigEndianUShort breakChar;
104    BigEndianUShort maxContext;
105};
106
107struct headTable {
108    Fixed version;
109    Fixed fontRevision;
110    BigEndianULong checkSumAdjustment;
111    BigEndianULong magicNumber;
112    BigEndianUShort flags;
113    BigEndianUShort unitsPerEm;
114    long long created;
115    long long modified;
116    BigEndianUShort xMin;
117    BigEndianUShort xMax;
118    BigEndianUShort yMin;
119    BigEndianUShort yMax;
120    BigEndianUShort macStyle;
121    BigEndianUShort lowestRectPPEM;
122    BigEndianUShort fontDirectionHint;
123    BigEndianUShort indexToLocFormat;
124    BigEndianUShort glyphDataFormat;
125};
126
127struct nameRecord {
128    BigEndianUShort platformID;
129    BigEndianUShort encodingID;
130    BigEndianUShort languageID;
131    BigEndianUShort nameID;
132    BigEndianUShort length;
133    BigEndianUShort offset;
134};
135
136struct nameTable {
137    BigEndianUShort format;
138    BigEndianUShort count;
139    BigEndianUShort stringOffset;
140    nameRecord nameRecords[1];
141};
142
143#pragma pack()
144
145// adds fontName to the font table in fontData, and writes the new font table to rewrittenFontTable
146// returns the size of the name table (which is used by renameAndActivateFont), or 0 on early abort
147static size_t renameFont(SharedBuffer* fontData, const String& fontName, Vector<char> &rewrittenFontData)
148{
149    size_t originalDataSize = fontData->size();
150    const sfntHeader* sfnt = reinterpret_cast<const sfntHeader*>(fontData->data());
151
152    unsigned t;
153    for (t = 0; t < sfnt->numTables; ++t) {
154        if (sfnt->tables[t].tag == 'name')
155            break;
156    }
157    if (t == sfnt->numTables)
158        return 0;
159
160    const int nameRecordCount = 5;
161
162    // Rounded up to a multiple of 4 to simplify the checksum calculation.
163    size_t nameTableSize = ((offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord) + fontName.length() * sizeof(UChar)) & ~3) + 4;
164
165    rewrittenFontData.resize(fontData->size() + nameTableSize);
166    char* data = rewrittenFontData.data();
167    memcpy(data, fontData->data(), originalDataSize);
168
169    // Make the table directory entry point to the new 'name' table.
170    sfntHeader* rewrittenSfnt = reinterpret_cast<sfntHeader*>(data);
171    rewrittenSfnt->tables[t].length = nameTableSize;
172    rewrittenSfnt->tables[t].offset = originalDataSize;
173
174    // Write the new 'name' table after the original font data.
175    nameTable* name = reinterpret_cast<nameTable*>(data + originalDataSize);
176    name->format = 0;
177    name->count = nameRecordCount;
178    name->stringOffset = offsetof(nameTable, nameRecords) + nameRecordCount * sizeof(nameRecord);
179    for (unsigned i = 0; i < nameRecordCount; ++i) {
180        name->nameRecords[i].platformID = 3;
181        name->nameRecords[i].encodingID = 1;
182        name->nameRecords[i].languageID = 0x0409;
183        name->nameRecords[i].offset = 0;
184        name->nameRecords[i].length = fontName.length() * sizeof(UChar);
185    }
186
187    // The required 'name' record types: Family, Style, Unique, Full and PostScript.
188    name->nameRecords[0].nameID = 1;
189    name->nameRecords[1].nameID = 2;
190    name->nameRecords[2].nameID = 3;
191    name->nameRecords[3].nameID = 4;
192    name->nameRecords[4].nameID = 6;
193
194    for (unsigned i = 0; i < fontName.length(); ++i)
195        reinterpret_cast<BigEndianUShort*>(data + originalDataSize + name->stringOffset)[i] = fontName[i];
196
197    // Update the table checksum in the directory entry.
198    rewrittenSfnt->tables[t].checkSum = 0;
199    for (unsigned i = 0; i * sizeof(BigEndianULong) < nameTableSize; ++i)
200        rewrittenSfnt->tables[t].checkSum = rewrittenSfnt->tables[t].checkSum + reinterpret_cast<BigEndianULong*>(name)[i];
201
202    return nameTableSize;
203}
204
205// Rename the font and install the new font data into the system
206HANDLE renameAndActivateFont(SharedBuffer* fontData, const String& fontName)
207{
208    Vector<char> rewrittenFontData;
209    size_t nameTableSize = renameFont(fontData, fontName, rewrittenFontData);
210    if (!nameTableSize)
211        return 0;
212
213    DWORD numFonts = 0;
214    HANDLE fontHandle = AddFontMemResourceEx(rewrittenFontData.data(), fontData->size() + nameTableSize, 0, &numFonts);
215
216    if (fontHandle && numFonts < 1) {
217        RemoveFontMemResourceEx(fontHandle);
218        return 0;
219    }
220
221    return fontHandle;
222}
223
224} // namespace blink
225