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