michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkData.h" michael@0: #include "SkEndian.h" michael@0: #include "SkSFNTHeader.h" michael@0: #include "SkStream.h" michael@0: #include "SkOTTable_head.h" michael@0: #include "SkOTTable_name.h" michael@0: #include "SkOTTableTypes.h" michael@0: #include "SkOTUtils.h" michael@0: michael@0: extern const uint8_t SK_OT_GlyphData_NoOutline[] = { michael@0: 0x0,0x0, //SkOTTableGlyphData::numberOfContours michael@0: 0x0,0x0, //SkOTTableGlyphData::xMin michael@0: 0x0,0x0, //SkOTTableGlyphData::yMin michael@0: 0x0,0x0, //SkOTTableGlyphData::xMax michael@0: 0x0,0x0, //SkOTTableGlyphData::yMax michael@0: michael@0: 0x0,0x0, //SkOTTableGlyphDataInstructions::length michael@0: }; michael@0: michael@0: uint32_t SkOTUtils::CalcTableChecksum(SK_OT_ULONG *data, size_t length) { michael@0: uint32_t sum = 0; michael@0: SK_OT_ULONG *dataEnd = data + ((length + 3) & ~3) / sizeof(SK_OT_ULONG); michael@0: for (; data < dataEnd; ++data) { michael@0: sum += SkEndian_SwapBE32(*data); michael@0: } michael@0: return sum; michael@0: } michael@0: michael@0: SkData* SkOTUtils::RenameFont(SkStream* fontData, const char* fontName, int fontNameLen) { michael@0: michael@0: // Get the sfnt header. michael@0: SkSFNTHeader sfntHeader; michael@0: if (fontData->read(&sfntHeader, sizeof(sfntHeader)) < sizeof(sfntHeader)) { michael@0: return NULL; michael@0: } michael@0: michael@0: // Find the existing 'name' table. michael@0: int tableIndex; michael@0: SkSFNTHeader::TableDirectoryEntry tableEntry; michael@0: int numTables = SkEndian_SwapBE16(sfntHeader.numTables); michael@0: for (tableIndex = 0; tableIndex < numTables; ++tableIndex) { michael@0: if (fontData->read(&tableEntry, sizeof(tableEntry)) < sizeof(tableEntry)) { michael@0: return NULL; michael@0: } michael@0: if (SkOTTableName::TAG == tableEntry.tag) { michael@0: break; michael@0: } michael@0: } michael@0: if (tableIndex == numTables) { michael@0: return NULL; michael@0: } michael@0: michael@0: if (!fontData->rewind()) { michael@0: return NULL; michael@0: } michael@0: michael@0: // The required 'name' record types: Family, Style, Unique, Full and PostScript. michael@0: const SkOTTableName::Record::NameID::Predefined::Value namesToCreate[] = { michael@0: SkOTTableName::Record::NameID::Predefined::FontFamilyName, michael@0: SkOTTableName::Record::NameID::Predefined::FontSubfamilyName, michael@0: SkOTTableName::Record::NameID::Predefined::UniqueFontIdentifier, michael@0: SkOTTableName::Record::NameID::Predefined::FullFontName, michael@0: SkOTTableName::Record::NameID::Predefined::PostscriptName, michael@0: }; michael@0: const int namesCount = SK_ARRAY_COUNT(namesToCreate); michael@0: michael@0: // Copy the data, leaving out the old name table. michael@0: // In theory, we could also remove the DSIG table if it exists. michael@0: size_t nameTableLogicalSize = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableName::Record)) + (fontNameLen * sizeof(wchar_t)); michael@0: size_t nameTablePhysicalSize = (nameTableLogicalSize + 3) & ~3; // Rounded up to a multiple of 4. michael@0: michael@0: size_t oldNameTablePhysicalSize = (SkEndian_SwapBE32(tableEntry.logicalLength) + 3) & ~3; // Rounded up to a multiple of 4. michael@0: size_t oldNameTableOffset = SkEndian_SwapBE32(tableEntry.offset); michael@0: michael@0: //originalDataSize is the size of the original data without the name table. michael@0: size_t originalDataSize = fontData->getLength() - oldNameTablePhysicalSize; michael@0: size_t newDataSize = originalDataSize + nameTablePhysicalSize; michael@0: michael@0: SK_OT_BYTE* data = static_cast(sk_malloc_throw(newDataSize)); michael@0: SkAutoTUnref rewrittenFontData(SkData::NewFromMalloc(data, newDataSize)); michael@0: michael@0: if (fontData->read(data, oldNameTableOffset) < oldNameTableOffset) { michael@0: return NULL; michael@0: } michael@0: if (fontData->skip(oldNameTablePhysicalSize) < oldNameTablePhysicalSize) { michael@0: return NULL; michael@0: } michael@0: if (fontData->read(data + oldNameTableOffset, originalDataSize - oldNameTableOffset) < originalDataSize - oldNameTableOffset) { michael@0: return NULL; michael@0: } michael@0: michael@0: //Fix up the offsets of the directory entries after the old 'name' table entry. michael@0: SkSFNTHeader::TableDirectoryEntry* currentEntry = reinterpret_cast(data + sizeof(SkSFNTHeader)); michael@0: SkSFNTHeader::TableDirectoryEntry* endEntry = currentEntry + numTables; michael@0: SkSFNTHeader::TableDirectoryEntry* headTableEntry = NULL; michael@0: for (; currentEntry < endEntry; ++currentEntry) { michael@0: uint32_t oldOffset = SkEndian_SwapBE32(currentEntry->offset); michael@0: if (oldOffset > oldNameTableOffset) { michael@0: currentEntry->offset = SkEndian_SwapBE32(oldOffset - oldNameTablePhysicalSize); michael@0: } michael@0: if (SkOTTableHead::TAG == currentEntry->tag) { michael@0: headTableEntry = currentEntry; michael@0: } michael@0: } michael@0: michael@0: // Make the table directory entry point to the new 'name' table. michael@0: SkSFNTHeader::TableDirectoryEntry* nameTableEntry = reinterpret_cast(data + sizeof(SkSFNTHeader)) + tableIndex; michael@0: nameTableEntry->logicalLength = SkEndian_SwapBE32(nameTableLogicalSize); michael@0: nameTableEntry->offset = SkEndian_SwapBE32(originalDataSize); michael@0: michael@0: // Write the new 'name' table after the original font data. michael@0: SkOTTableName* nameTable = reinterpret_cast(data + originalDataSize); michael@0: unsigned short stringOffset = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableName::Record)); michael@0: nameTable->format = SkOTTableName::format_0; michael@0: nameTable->count = SkEndian_SwapBE16(namesCount); michael@0: nameTable->stringOffset = SkEndian_SwapBE16(stringOffset); michael@0: michael@0: SkOTTableName::Record* nameRecords = reinterpret_cast(data + originalDataSize + sizeof(SkOTTableName)); michael@0: for (int i = 0; i < namesCount; ++i) { michael@0: nameRecords[i].platformID.value = SkOTTableName::Record::PlatformID::Windows; michael@0: nameRecords[i].encodingID.windows.value = SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2; michael@0: nameRecords[i].languageID.windows.value = SkOTTableName::Record::LanguageID::Windows::English_UnitedStates; michael@0: nameRecords[i].nameID.predefined.value = namesToCreate[i]; michael@0: nameRecords[i].offset = SkEndian_SwapBE16(0); michael@0: nameRecords[i].length = SkEndian_SwapBE16(fontNameLen * sizeof(wchar_t)); michael@0: } michael@0: michael@0: SK_OT_USHORT* nameString = reinterpret_cast(data + originalDataSize + stringOffset); michael@0: for (int i = 0; i < fontNameLen; ++i) { michael@0: nameString[i] = SkEndian_SwapBE16(fontName[i]); michael@0: } michael@0: michael@0: unsigned char* logical = data + originalDataSize + nameTableLogicalSize; michael@0: unsigned char* physical = data + originalDataSize + nameTablePhysicalSize; michael@0: for (; logical < physical; ++logical) { michael@0: *logical = 0; michael@0: } michael@0: michael@0: // Update the table checksum in the directory entry. michael@0: nameTableEntry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum(reinterpret_cast(nameTable), nameTableLogicalSize)); michael@0: michael@0: // Update the checksum adjustment in the head table. michael@0: if (headTableEntry) { michael@0: size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset); michael@0: if (headTableOffset + sizeof(SkOTTableHead) < originalDataSize) { michael@0: SkOTTableHead* headTable = reinterpret_cast(data + headTableOffset); michael@0: headTable->checksumAdjustment = SkEndian_SwapBE32(0); michael@0: uint32_t unadjustedFontChecksum = SkOTUtils::CalcTableChecksum(reinterpret_cast(data), originalDataSize + nameTablePhysicalSize); michael@0: headTable->checksumAdjustment = SkEndian_SwapBE32(SkOTTableHead::fontChecksum - unadjustedFontChecksum); michael@0: } michael@0: } michael@0: michael@0: return rewrittenFontData.detach(); michael@0: } michael@0: michael@0: michael@0: SkOTUtils::LocalizedStrings_NameTable* michael@0: SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(const SkTypeface& typeface) { michael@0: static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e'); michael@0: size_t nameTableSize = typeface.getTableSize(nameTag); michael@0: if (0 == nameTableSize) { michael@0: return NULL; michael@0: } michael@0: SkAutoTDeleteArray nameTableData(new uint8_t[nameTableSize]); michael@0: size_t copied = typeface.getTableData(nameTag, 0, nameTableSize, nameTableData.get()); michael@0: if (copied != nameTableSize) { michael@0: return NULL; michael@0: } michael@0: michael@0: return new SkOTUtils::LocalizedStrings_NameTable((SkOTTableName*)nameTableData.detach(), michael@0: SkOTUtils::LocalizedStrings_NameTable::familyNameTypes, michael@0: SK_ARRAY_COUNT(SkOTUtils::LocalizedStrings_NameTable::familyNameTypes)); michael@0: } michael@0: michael@0: bool SkOTUtils::LocalizedStrings_NameTable::next(SkTypeface::LocalizedString* localizedString) { michael@0: do { michael@0: SkOTTableName::Iterator::Record record; michael@0: if (fFamilyNameIter.next(record)) { michael@0: localizedString->fString = record.name; michael@0: localizedString->fLanguage = record.language; michael@0: return true; michael@0: } michael@0: if (fTypesCount == fTypesIndex + 1) { michael@0: return false; michael@0: } michael@0: ++fTypesIndex; michael@0: fFamilyNameIter.reset(fTypes[fTypesIndex]); michael@0: } while (true); michael@0: } michael@0: michael@0: SkOTTableName::Record::NameID::Predefined::Value michael@0: SkOTUtils::LocalizedStrings_NameTable::familyNameTypes[3] = { michael@0: SkOTTableName::Record::NameID::Predefined::FontFamilyName, michael@0: SkOTTableName::Record::NameID::Predefined::PreferredFamily, michael@0: SkOTTableName::Record::NameID::Predefined::WWSFamilyName, michael@0: };