1 /*************************************************************************** 2 * 3 * 2016 and later: Unicode, Inc. and others. 4 * License & terms of use: http://www.unicode.org/copyright.html#License 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 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 52 FontObject::~FontObject() 53 { 54 fclose(file); 55 delete[] directory; 56 delete[] cmapTable; 57 delete[] headTable; 58 delete[] hmtxTable; 59 } 60 61 void FontObject::deleteTable(void *table) 62 { 63 delete[] (char *) table; 64 } 65 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 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 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 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 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 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 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