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