SkOTUtils.cpp revision 363e546ed626b6dbbc42f5db87b3594bc0b5944b
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
17extern 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
27uint32_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
36SkData* 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 SkOTTableNameRecord::NameID::Predefined::Value namesToCreate[] = {
66        SkOTTableNameRecord::NameID::Predefined::FontFamilyName,
67        SkOTTableNameRecord::NameID::Predefined::FontSubfamilyName,
68        SkOTTableNameRecord::NameID::Predefined::UniqueFontIdentifier,
69        SkOTTableNameRecord::NameID::Predefined::FullFontName,
70        SkOTTableNameRecord::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(SkOTTableNameRecord)) + (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(SkOTTableNameRecord));
121    nameTable->format = SkOTTableName::format_0;
122    nameTable->count = SkEndian_SwapBE16(namesCount);
123    nameTable->stringOffset = SkEndian_SwapBE16(stringOffset);
124
125    SkOTTableNameRecord* nameRecords = reinterpret_cast<SkOTTableNameRecord*>(data + originalDataSize + sizeof(SkOTTableName));
126    for (int i = 0; i < namesCount; ++i) {
127        nameRecords[i].platformID.value = SkOTTableNameRecord::PlatformID::Windows;
128        nameRecords[i].encodingID.windows.value = SkOTTableNameRecord::EncodingID::Windows::UnicodeBMPUCS2;
129        nameRecords[i].languageID.windows.value = SkOTTableNameRecord::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