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