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