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
14FontObject::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
46FontObject::~FontObject()
47{
48    fclose(file);
49    delete[] directory;
50    delete[] cmapTable;
51    delete[] headTable;
52    delete[] hmtxTable;
53}
54
55void FontObject::deleteTable(void *table)
56{
57    delete[] (char *) table;
58}
59
60DirectoryEntry *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
84void *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
103CMAPEncodingSubtable *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
131void 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
153LEGlyphID 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
195le_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
207le_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