|
1 /* |
|
2 * Copyright 2012 Google Inc. |
|
3 * |
|
4 * Use of this source code is governed by a BSD-style license that can be |
|
5 * found in the LICENSE file. |
|
6 */ |
|
7 |
|
8 #include "SkData.h" |
|
9 #include "SkEndian.h" |
|
10 #include "SkSFNTHeader.h" |
|
11 #include "SkStream.h" |
|
12 #include "SkOTTable_head.h" |
|
13 #include "SkOTTable_name.h" |
|
14 #include "SkOTTableTypes.h" |
|
15 #include "SkOTUtils.h" |
|
16 |
|
17 extern const uint8_t SK_OT_GlyphData_NoOutline[] = { |
|
18 0x0,0x0, //SkOTTableGlyphData::numberOfContours |
|
19 0x0,0x0, //SkOTTableGlyphData::xMin |
|
20 0x0,0x0, //SkOTTableGlyphData::yMin |
|
21 0x0,0x0, //SkOTTableGlyphData::xMax |
|
22 0x0,0x0, //SkOTTableGlyphData::yMax |
|
23 |
|
24 0x0,0x0, //SkOTTableGlyphDataInstructions::length |
|
25 }; |
|
26 |
|
27 uint32_t SkOTUtils::CalcTableChecksum(SK_OT_ULONG *data, size_t length) { |
|
28 uint32_t sum = 0; |
|
29 SK_OT_ULONG *dataEnd = data + ((length + 3) & ~3) / sizeof(SK_OT_ULONG); |
|
30 for (; data < dataEnd; ++data) { |
|
31 sum += SkEndian_SwapBE32(*data); |
|
32 } |
|
33 return sum; |
|
34 } |
|
35 |
|
36 SkData* SkOTUtils::RenameFont(SkStream* fontData, const char* fontName, int fontNameLen) { |
|
37 |
|
38 // Get the sfnt header. |
|
39 SkSFNTHeader sfntHeader; |
|
40 if (fontData->read(&sfntHeader, sizeof(sfntHeader)) < sizeof(sfntHeader)) { |
|
41 return NULL; |
|
42 } |
|
43 |
|
44 // Find the existing 'name' table. |
|
45 int tableIndex; |
|
46 SkSFNTHeader::TableDirectoryEntry tableEntry; |
|
47 int numTables = SkEndian_SwapBE16(sfntHeader.numTables); |
|
48 for (tableIndex = 0; tableIndex < numTables; ++tableIndex) { |
|
49 if (fontData->read(&tableEntry, sizeof(tableEntry)) < sizeof(tableEntry)) { |
|
50 return NULL; |
|
51 } |
|
52 if (SkOTTableName::TAG == tableEntry.tag) { |
|
53 break; |
|
54 } |
|
55 } |
|
56 if (tableIndex == numTables) { |
|
57 return NULL; |
|
58 } |
|
59 |
|
60 if (!fontData->rewind()) { |
|
61 return NULL; |
|
62 } |
|
63 |
|
64 // The required 'name' record types: Family, Style, Unique, Full and PostScript. |
|
65 const SkOTTableName::Record::NameID::Predefined::Value namesToCreate[] = { |
|
66 SkOTTableName::Record::NameID::Predefined::FontFamilyName, |
|
67 SkOTTableName::Record::NameID::Predefined::FontSubfamilyName, |
|
68 SkOTTableName::Record::NameID::Predefined::UniqueFontIdentifier, |
|
69 SkOTTableName::Record::NameID::Predefined::FullFontName, |
|
70 SkOTTableName::Record::NameID::Predefined::PostscriptName, |
|
71 }; |
|
72 const int namesCount = SK_ARRAY_COUNT(namesToCreate); |
|
73 |
|
74 // Copy the data, leaving out the old name table. |
|
75 // In theory, we could also remove the DSIG table if it exists. |
|
76 size_t nameTableLogicalSize = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableName::Record)) + (fontNameLen * sizeof(wchar_t)); |
|
77 size_t nameTablePhysicalSize = (nameTableLogicalSize + 3) & ~3; // Rounded up to a multiple of 4. |
|
78 |
|
79 size_t oldNameTablePhysicalSize = (SkEndian_SwapBE32(tableEntry.logicalLength) + 3) & ~3; // Rounded up to a multiple of 4. |
|
80 size_t oldNameTableOffset = SkEndian_SwapBE32(tableEntry.offset); |
|
81 |
|
82 //originalDataSize is the size of the original data without the name table. |
|
83 size_t originalDataSize = fontData->getLength() - oldNameTablePhysicalSize; |
|
84 size_t newDataSize = originalDataSize + nameTablePhysicalSize; |
|
85 |
|
86 SK_OT_BYTE* data = static_cast<SK_OT_BYTE*>(sk_malloc_throw(newDataSize)); |
|
87 SkAutoTUnref<SkData> rewrittenFontData(SkData::NewFromMalloc(data, newDataSize)); |
|
88 |
|
89 if (fontData->read(data, oldNameTableOffset) < oldNameTableOffset) { |
|
90 return NULL; |
|
91 } |
|
92 if (fontData->skip(oldNameTablePhysicalSize) < oldNameTablePhysicalSize) { |
|
93 return NULL; |
|
94 } |
|
95 if (fontData->read(data + oldNameTableOffset, originalDataSize - oldNameTableOffset) < originalDataSize - oldNameTableOffset) { |
|
96 return NULL; |
|
97 } |
|
98 |
|
99 //Fix up the offsets of the directory entries after the old 'name' table entry. |
|
100 SkSFNTHeader::TableDirectoryEntry* currentEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader)); |
|
101 SkSFNTHeader::TableDirectoryEntry* endEntry = currentEntry + numTables; |
|
102 SkSFNTHeader::TableDirectoryEntry* headTableEntry = NULL; |
|
103 for (; currentEntry < endEntry; ++currentEntry) { |
|
104 uint32_t oldOffset = SkEndian_SwapBE32(currentEntry->offset); |
|
105 if (oldOffset > oldNameTableOffset) { |
|
106 currentEntry->offset = SkEndian_SwapBE32(oldOffset - oldNameTablePhysicalSize); |
|
107 } |
|
108 if (SkOTTableHead::TAG == currentEntry->tag) { |
|
109 headTableEntry = currentEntry; |
|
110 } |
|
111 } |
|
112 |
|
113 // Make the table directory entry point to the new 'name' table. |
|
114 SkSFNTHeader::TableDirectoryEntry* nameTableEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader)) + tableIndex; |
|
115 nameTableEntry->logicalLength = SkEndian_SwapBE32(nameTableLogicalSize); |
|
116 nameTableEntry->offset = SkEndian_SwapBE32(originalDataSize); |
|
117 |
|
118 // Write the new 'name' table after the original font data. |
|
119 SkOTTableName* nameTable = reinterpret_cast<SkOTTableName*>(data + originalDataSize); |
|
120 unsigned short stringOffset = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableName::Record)); |
|
121 nameTable->format = SkOTTableName::format_0; |
|
122 nameTable->count = SkEndian_SwapBE16(namesCount); |
|
123 nameTable->stringOffset = SkEndian_SwapBE16(stringOffset); |
|
124 |
|
125 SkOTTableName::Record* nameRecords = reinterpret_cast<SkOTTableName::Record*>(data + originalDataSize + sizeof(SkOTTableName)); |
|
126 for (int i = 0; i < namesCount; ++i) { |
|
127 nameRecords[i].platformID.value = SkOTTableName::Record::PlatformID::Windows; |
|
128 nameRecords[i].encodingID.windows.value = SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2; |
|
129 nameRecords[i].languageID.windows.value = SkOTTableName::Record::LanguageID::Windows::English_UnitedStates; |
|
130 nameRecords[i].nameID.predefined.value = namesToCreate[i]; |
|
131 nameRecords[i].offset = SkEndian_SwapBE16(0); |
|
132 nameRecords[i].length = SkEndian_SwapBE16(fontNameLen * sizeof(wchar_t)); |
|
133 } |
|
134 |
|
135 SK_OT_USHORT* nameString = reinterpret_cast<SK_OT_USHORT*>(data + originalDataSize + stringOffset); |
|
136 for (int i = 0; i < fontNameLen; ++i) { |
|
137 nameString[i] = SkEndian_SwapBE16(fontName[i]); |
|
138 } |
|
139 |
|
140 unsigned char* logical = data + originalDataSize + nameTableLogicalSize; |
|
141 unsigned char* physical = data + originalDataSize + nameTablePhysicalSize; |
|
142 for (; logical < physical; ++logical) { |
|
143 *logical = 0; |
|
144 } |
|
145 |
|
146 // Update the table checksum in the directory entry. |
|
147 nameTableEntry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(nameTable), nameTableLogicalSize)); |
|
148 |
|
149 // Update the checksum adjustment in the head table. |
|
150 if (headTableEntry) { |
|
151 size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset); |
|
152 if (headTableOffset + sizeof(SkOTTableHead) < originalDataSize) { |
|
153 SkOTTableHead* headTable = reinterpret_cast<SkOTTableHead*>(data + headTableOffset); |
|
154 headTable->checksumAdjustment = SkEndian_SwapBE32(0); |
|
155 uint32_t unadjustedFontChecksum = SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(data), originalDataSize + nameTablePhysicalSize); |
|
156 headTable->checksumAdjustment = SkEndian_SwapBE32(SkOTTableHead::fontChecksum - unadjustedFontChecksum); |
|
157 } |
|
158 } |
|
159 |
|
160 return rewrittenFontData.detach(); |
|
161 } |
|
162 |
|
163 |
|
164 SkOTUtils::LocalizedStrings_NameTable* |
|
165 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(const SkTypeface& typeface) { |
|
166 static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e'); |
|
167 size_t nameTableSize = typeface.getTableSize(nameTag); |
|
168 if (0 == nameTableSize) { |
|
169 return NULL; |
|
170 } |
|
171 SkAutoTDeleteArray<uint8_t> nameTableData(new uint8_t[nameTableSize]); |
|
172 size_t copied = typeface.getTableData(nameTag, 0, nameTableSize, nameTableData.get()); |
|
173 if (copied != nameTableSize) { |
|
174 return NULL; |
|
175 } |
|
176 |
|
177 return new SkOTUtils::LocalizedStrings_NameTable((SkOTTableName*)nameTableData.detach(), |
|
178 SkOTUtils::LocalizedStrings_NameTable::familyNameTypes, |
|
179 SK_ARRAY_COUNT(SkOTUtils::LocalizedStrings_NameTable::familyNameTypes)); |
|
180 } |
|
181 |
|
182 bool SkOTUtils::LocalizedStrings_NameTable::next(SkTypeface::LocalizedString* localizedString) { |
|
183 do { |
|
184 SkOTTableName::Iterator::Record record; |
|
185 if (fFamilyNameIter.next(record)) { |
|
186 localizedString->fString = record.name; |
|
187 localizedString->fLanguage = record.language; |
|
188 return true; |
|
189 } |
|
190 if (fTypesCount == fTypesIndex + 1) { |
|
191 return false; |
|
192 } |
|
193 ++fTypesIndex; |
|
194 fFamilyNameIter.reset(fTypes[fTypesIndex]); |
|
195 } while (true); |
|
196 } |
|
197 |
|
198 SkOTTableName::Record::NameID::Predefined::Value |
|
199 SkOTUtils::LocalizedStrings_NameTable::familyNameTypes[3] = { |
|
200 SkOTTableName::Record::NameID::Predefined::FontFamilyName, |
|
201 SkOTTableName::Record::NameID::Predefined::PreferredFamily, |
|
202 SkOTTableName::Record::NameID::Predefined::WWSFamilyName, |
|
203 }; |