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