1 /***************************************************************************
2 *
3 * © 2016 and later: Unicode, Inc. and others.
4 * License & terms of use: http://www.unicode.org/copyright.html
5 *
6 ****************************************************************************/
7 /***************************************************************************
8 *
9 * Copyright (C) 1998-2013, International Business Machines
10 * Corporation and others. All Rights Reserved.
11 *
12 ************************************************************************/
13
14 #include <stdio.h>
15
16 #include "layout/LETypes.h"
17 #include "FontObject.h"
18 #include "layout/LESwaps.h"
19
20 using icu::LESwaps;
21
FontObject(char * fileName)22 FontObject::FontObject(char *fileName)
23 : directory(nullptr), numTables(0), searchRange(0),entrySelector(0),
24 cmapTable(nullptr), cmSegCount(0), cmSearchRange(0), cmEntrySelector(0),
25 cmEndCodes(nullptr), cmStartCodes(nullptr), cmIdDelta(nullptr), cmIdRangeOffset(nullptr),
26 headTable(nullptr), hmtxTable(nullptr), numGlyphs(0), numOfLongHorMetrics(0), file(nullptr)
27 {
28 file = fopen(fileName, "rb");
29
30 if (file == nullptr) {
31 printf("?? Couldn't open %s", fileName);
32 return;
33 }
34
35 SFNTDirectory tempDir;
36
37 fread(&tempDir, sizeof tempDir, 1, file);
38
39 numTables = SWAPW(tempDir.numTables);
40 searchRange = SWAPW(tempDir.searchRange) >> 4;
41 entrySelector = SWAPW(tempDir.entrySelector);
42 rangeShift = SWAPW(tempDir.rangeShift) >> 4;
43
44 int dirSize = sizeof tempDir + ((numTables - ANY_NUMBER) * sizeof(DirectoryEntry));
45
46 directory = reinterpret_cast<SFNTDirectory*>(new char[dirSize]);
47
48 fseek(file, 0L, SEEK_SET);
49 fread(directory, sizeof(char), dirSize, file);
50
51 initUnicodeCMAP();
52 }
53
~FontObject()54 FontObject::~FontObject()
55 {
56 fclose(file);
57 delete[] directory;
58 delete[] cmapTable;
59 delete[] headTable;
60 delete[] hmtxTable;
61 }
62
deleteTable(void * table)63 void FontObject::deleteTable(void *table)
64 {
65 delete[] static_cast<char*>(table);
66 }
67
findTable(LETag tag)68 DirectoryEntry *FontObject::findTable(LETag tag)
69 {
70 le_uint16 table = 0;
71 le_uint16 probe = 1 << entrySelector;
72
73 if (SWAPL(directory->tableDirectory[rangeShift].tag) <= tag) {
74 table = rangeShift;
75 }
76
77 while (probe > (1 << 0)) {
78 probe >>= 1;
79
80 if (SWAPL(directory->tableDirectory[table + probe].tag) <= tag) {
81 table += probe;
82 }
83 }
84
85 if (SWAPL(directory->tableDirectory[table].tag) == tag) {
86 return &directory->tableDirectory[table];
87 }
88
89 return nullptr;
90 }
91
readTable(LETag tag,le_uint32 * length)92 void *FontObject::readTable(LETag tag, le_uint32 *length)
93 {
94 DirectoryEntry *entry = findTable(tag);
95
96 if (entry == nullptr) {
97 *length = 0;
98 return nullptr;
99 }
100
101 *length = SWAPL(entry->length);
102
103 void *table = new char[*length];
104
105 fseek(file, SWAPL(entry->offset), SEEK_SET);
106 fread(table, sizeof(char), *length, file);
107
108 return table;
109 }
110
findCMAP(le_uint16 platformID,le_uint16 platformSpecificID)111 CMAPEncodingSubtable *FontObject::findCMAP(le_uint16 platformID, le_uint16 platformSpecificID)
112 {
113 LETag cmapTag = 0x636D6170; // 'cmap'
114
115 if (cmapTable == nullptr) {
116 le_uint32 length;
117
118 cmapTable = static_cast<CMAPTable*>(readTable(cmapTag, &length));
119 }
120
121 if (cmapTable != nullptr) {
122 le_uint16 i;
123 le_uint16 nSubtables = SWAPW(cmapTable->numberSubtables);
124
125
126 for (i = 0; i < nSubtables; i += 1) {
127 CMAPEncodingSubtableHeader *esh = &cmapTable->encodingSubtableHeaders[i];
128
129 if (SWAPW(esh->platformID) == platformID &&
130 SWAPW(esh->platformSpecificID) == platformSpecificID) {
131 return reinterpret_cast<CMAPEncodingSubtable*>(reinterpret_cast<char*>(cmapTable) + SWAPL(esh->encodingOffset));
132 }
133 }
134 }
135
136 return nullptr;
137 }
138
initUnicodeCMAP()139 void FontObject::initUnicodeCMAP()
140 {
141 CMAPEncodingSubtable *encodingSubtable = findCMAP(3, 1);
142
143 if (encodingSubtable == nullptr ||
144 SWAPW(encodingSubtable->format) != 4) {
145 printf("Can't find unicode 'cmap'");
146 return;
147 }
148
149 CMAPFormat4Encoding *header = (CMAPFormat4Encoding *) encodingSubtable;
150
151 cmSegCount = SWAPW(header->segCountX2) / 2;
152 cmSearchRange = SWAPW(header->searchRange);
153 cmEntrySelector = SWAPW(header->entrySelector);
154 cmRangeShift = SWAPW(header->rangeShift) / 2;
155 cmEndCodes = &header->endCodes[0];
156 cmStartCodes = &header->endCodes[cmSegCount + 1]; // + 1 for reservedPad...
157 cmIdDelta = &cmStartCodes[cmSegCount];
158 cmIdRangeOffset = &cmIdDelta[cmSegCount];
159 }
160
unicodeToGlyph(LEUnicode32 unicode32)161 LEGlyphID FontObject::unicodeToGlyph(LEUnicode32 unicode32)
162 {
163 if (unicode32 >= 0x10000) {
164 return 0;
165 }
166
167 LEUnicode16 unicode = static_cast<LEUnicode16>(unicode32);
168 le_uint16 index = 0;
169 le_uint16 probe = 1 << cmEntrySelector;
170 LEGlyphID result = 0;
171
172 if (SWAPW(cmStartCodes[cmRangeShift]) <= unicode) {
173 index = cmRangeShift;
174 }
175
176 while (probe > (1 << 0)) {
177 probe >>= 1;
178
179 if (SWAPW(cmStartCodes[index + probe]) <= unicode) {
180 index += probe;
181 }
182 }
183
184 if (unicode >= SWAPW(cmStartCodes[index]) && unicode <= SWAPW(cmEndCodes[index])) {
185 if (cmIdRangeOffset[index] == 0) {
186 result = static_cast<LEGlyphID>(unicode);
187 } else {
188 le_uint16 offset = unicode - SWAPW(cmStartCodes[index]);
189 le_uint16 rangeOffset = SWAPW(cmIdRangeOffset[index]);
190 le_uint16* glyphIndexTable = reinterpret_cast<le_uint16*>(reinterpret_cast<char*>(&cmIdRangeOffset[index]) + rangeOffset);
191
192 result = SWAPW(glyphIndexTable[offset]);
193 }
194
195 result += SWAPW(cmIdDelta[index]);
196 } else {
197 result = 0;
198 }
199
200 return result;
201 }
202
getUnitsPerEM()203 le_uint16 FontObject::getUnitsPerEM()
204 {
205 if (headTable == nullptr) {
206 LETag headTag = 0x68656164; // 'head'
207 le_uint32 length;
208
209 headTable = static_cast<HEADTable*>(readTable(headTag, &length));
210 }
211
212 return SWAPW(headTable->unitsPerEm);
213 }
214
getGlyphAdvance(LEGlyphID glyph)215 le_uint16 FontObject::getGlyphAdvance(LEGlyphID glyph)
216 {
217 if (hmtxTable == nullptr) {
218 LETag maxpTag = 0x6D617870; // 'maxp'
219 LETag hheaTag = 0x68686561; // 'hhea'
220 LETag hmtxTag = 0x686D7478; // 'hmtx'
221 le_uint32 length;
222 HHEATable *hheaTable;
223 MAXPTable* maxpTable = static_cast<MAXPTable*>(readTable(maxpTag, &length));
224
225 numGlyphs = SWAPW(maxpTable->numGlyphs);
226 deleteTable(maxpTable);
227
228 hheaTable = static_cast<HHEATable*>(readTable(hheaTag, &length));
229 numOfLongHorMetrics = SWAPW(hheaTable->numOfLongHorMetrics);
230 deleteTable(hheaTable);
231
232 hmtxTable = static_cast<HMTXTable*>(readTable(hmtxTag, &length));
233 }
234
235 le_uint16 index = glyph;
236
237 if (glyph >= numGlyphs) {
238 return 0;
239 }
240
241 if (glyph >= numOfLongHorMetrics) {
242 index = numOfLongHorMetrics - 1;
243 }
244
245 return SWAPW(hmtxTable->hMetrics[index].advanceWidth);
246 }
247
248
249