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
20FontObject::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
52FontObject::~FontObject()
53{
54    fclose(file);
55    delete[] directory;
56    delete[] cmapTable;
57    delete[] headTable;
58    delete[] hmtxTable;
59}
60
61void FontObject::deleteTable(void *table)
62{
63    delete[] (char *) table;
64}
65
66DirectoryEntry *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
90void *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
109CMAPEncodingSubtable *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
137void 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
159LEGlyphID 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
201le_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
213le_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