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(SkToU32(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(SkToU32(nameTableLogicalSize)); 116 nameTableEntry->offset = SkEndian_SwapBE32(SkToU32(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 }; 204