SkOTUtils.cpp revision a550199c6f37e1b05a386ea57eee4c40cc91d84d
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 17uint32_t SkOTUtils::CalcTableChecksum(SK_OT_ULONG *data, size_t length) { 18 uint32_t sum = 0; 19 SK_OT_ULONG *dataEnd = data + ((length + 3) & ~3) / sizeof(SK_OT_ULONG); 20 for (; data < dataEnd; ++data) { 21 sum += SkEndian_SwapBE32(*data); 22 } 23 return sum; 24} 25 26SkData* SkOTUtils::RenameFont(SkStream* fontData, 27 const char* fontName, int fontNameLen) { 28 29 // Get the sfnt header. 30 SkSFNTHeader sfntHeader; 31 if (fontData->read(&sfntHeader, sizeof(sfntHeader)) < sizeof(sfntHeader)) { 32 return NULL; 33 } 34 35 // Find the existing 'name' table. 36 int tableIndex; 37 SkSFNTTableDirectoryEntry tableEntry; 38 int numTables = SkEndian_SwapBE16(sfntHeader.numTables); 39 for (tableIndex = 0; tableIndex < numTables; ++tableIndex) { 40 if (fontData->read(&tableEntry, sizeof(tableEntry)) < sizeof(tableEntry)) { 41 return NULL; 42 } 43 if ('name' == SkEndian_SwapBE32(tableEntry.tag)) { 44 break; 45 } 46 } 47 if (tableIndex == numTables) { 48 return NULL; 49 } 50 51 if (!fontData->rewind()) { 52 return NULL; 53 } 54 55 // The required 'name' record types: Family, Style, Unique, Full and PostScript. 56 const SkOTTableNameRecord::NameID::Predefined::Value namesToCreate[] = { 57 SkOTTableNameRecord::NameID::Predefined::FontFamilyName, 58 SkOTTableNameRecord::NameID::Predefined::FontSubfamilyName, 59 SkOTTableNameRecord::NameID::Predefined::UniqueFontIdentifier, 60 SkOTTableNameRecord::NameID::Predefined::FullFontName, 61 SkOTTableNameRecord::NameID::Predefined::PostscriptName, 62 }; 63 const int namesCount = SK_ARRAY_COUNT(namesToCreate); 64 65 // Copy the data, leaving out the old name table. 66 // In theory, we could also remove the DSIG table if it exists. 67 size_t nameTableLogicalSize = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableNameRecord)) + (fontNameLen * sizeof(wchar_t)); 68 size_t nameTablePhysicalSize = (nameTableLogicalSize + 3) & ~3; // Rounded up to a multiple of 4. 69 70 size_t oldNameTablePhysicalSize = (SkEndian_SwapBE32(tableEntry.logicalLength) + 3) & ~3; // Rounded up to a multiple of 4. 71 size_t oldNameTableOffset = SkEndian_SwapBE32(tableEntry.offset); 72 73 //originalDataSize is the size of the original data without the name table. 74 size_t originalDataSize = fontData->getLength() - oldNameTablePhysicalSize; 75 size_t newDataSize = originalDataSize + nameTablePhysicalSize; 76 77 SK_OT_BYTE* data = static_cast<SK_OT_BYTE*>(sk_malloc_throw(newDataSize)); 78 SkAutoTUnref<SkData> rewrittenFontData = SkAutoTUnref<SkData>(SkData::NewFromMalloc(data, newDataSize)); 79 80 if (fontData->read(data, oldNameTableOffset) < oldNameTableOffset) { 81 return NULL; 82 } 83 if (fontData->skip(oldNameTablePhysicalSize) < oldNameTablePhysicalSize) { 84 return NULL; 85 } 86 if (fontData->read(data + oldNameTableOffset, originalDataSize - oldNameTableOffset) < originalDataSize - oldNameTableOffset) { 87 return NULL; 88 } 89 90 //Fix up the offsets of the directory entries after the old 'name' table entry. 91 SkSFNTTableDirectoryEntry* currentEntry = reinterpret_cast<SkSFNTTableDirectoryEntry*>(data + sizeof(SkSFNTHeader)); 92 SkSFNTTableDirectoryEntry* endEntry = currentEntry + numTables; 93 SkSFNTTableDirectoryEntry* headTableEntry = NULL; 94 for (; currentEntry < endEntry; ++currentEntry) { 95 uint32_t oldOffset = SkEndian_SwapBE32(currentEntry->offset); 96 if (oldOffset > oldNameTableOffset) { 97 currentEntry->offset = SkEndian_SwapBE32(oldOffset - oldNameTablePhysicalSize); 98 } 99 if ('head' == SkEndian_SwapBE32(tableEntry.tag)) { 100 headTableEntry = currentEntry; 101 } 102 } 103 104 // Make the table directory entry point to the new 'name' table. 105 SkSFNTTableDirectoryEntry* nameTableEntry = reinterpret_cast<SkSFNTTableDirectoryEntry*>(data + sizeof(SkSFNTHeader)) + tableIndex; 106 nameTableEntry->logicalLength = SkEndian_SwapBE32(nameTableLogicalSize); 107 nameTableEntry->offset = SkEndian_SwapBE32(originalDataSize); 108 109 // Write the new 'name' table after the original font data. 110 SkOTTableName* nameTable = reinterpret_cast<SkOTTableName*>(data + originalDataSize); 111 unsigned short stringOffset = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableNameRecord)); 112 nameTable->format = SkOTTableName::format_0; 113 nameTable->count = SkEndian_SwapBE16(namesCount); 114 nameTable->stringOffset = SkEndian_SwapBE16(stringOffset); 115 116 SkOTTableNameRecord* nameRecords = reinterpret_cast<SkOTTableNameRecord*>(data + originalDataSize + sizeof(SkOTTableName)); 117 for (int i = 0; i < namesCount; ++i) { 118 nameRecords[i].platformID.value = SkOTTableNameRecord::PlatformID::Windows; 119 nameRecords[i].encodingID.windows.value = SkOTTableNameRecord::EncodingID::Windows::UnicodeBMPUCS2; 120 nameRecords[i].languageID.windows.value = SkOTTableNameRecord::LanguageID::Windows::English_UnitedStates; 121 nameRecords[i].nameID.predefined.value = namesToCreate[i]; 122 nameRecords[i].offset = SkEndian_SwapBE16(0); 123 nameRecords[i].length = SkEndian_SwapBE16(fontNameLen * sizeof(wchar_t)); 124 } 125 126 SK_OT_USHORT* nameString = reinterpret_cast<SK_OT_USHORT*>(data + originalDataSize + stringOffset); 127 for (int i = 0; i < fontNameLen; ++i) { 128 nameString[i] = SkEndian_SwapBE16(fontName[i]); 129 } 130 131 unsigned char* logical = data + originalDataSize + nameTableLogicalSize; 132 unsigned char* physical = data + originalDataSize + nameTablePhysicalSize; 133 for (; logical < physical; ++logical) { 134 *logical = 0; 135 } 136 137 // Update the table checksum in the directory entry. 138 nameTableEntry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(nameTable), nameTableLogicalSize)); 139 140 // Update the checksum adjustment in the head table. 141 if (headTableEntry) { 142 size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset); 143 if (headTableOffset + sizeof(SkOTTableHead) < originalDataSize) { 144 SkOTTableHead* headTable = reinterpret_cast<SkOTTableHead*>(data + headTableOffset); 145 headTable->checksumAdjustment = SkEndian_SwapBE32(0); 146 uint32_t unadjustedFontChecksum = SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(data), originalDataSize + nameTablePhysicalSize); 147 headTable->checksumAdjustment = SkEndian_SwapBE32(SkOTTableHead::fontChecksum - unadjustedFontChecksum); 148 } 149 } 150 151 return rewrittenFontData.detach(); 152} 153