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