blob: 8d57f853bbdfe6e91bc1f42622171ff86abcf3b2 [file] [log] [blame]
bungeman@google.com8ec99562012-02-07 21:30:21 +00001/*
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
bungeman@google.coma5501992012-05-18 19:06:41 +00008#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"
bungeman@google.com8ec99562012-02-07 21:30:21 +000014#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}
bungeman@google.coma5501992012-05-18 19:06:41 +000025
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));
bungeman@google.come9678a22012-05-18 19:12:21 +000078 SkAutoTUnref<SkData> rewrittenFontData(SkData::NewFromMalloc(data, newDataSize));
bungeman@google.coma5501992012-05-18 19:06:41 +000079
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}