// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /*************************************************************************** * * Copyright (C) 1998-2002, International Business Machines * Corporation and others. All Rights Reserved. * ************************************************************************/ #include #include "LETypes.h" #include "FontObject.h" #include "LESwaps.h" FontObject::FontObject(char *fileName) : directory(NULL), numTables(0), searchRange(0),entrySelector(0), cmapTable(NULL), cmSegCount(0), cmSearchRange(0), cmEntrySelector(0), cmEndCodes(NULL), cmStartCodes(NULL), cmIdDelta(0), cmIdRangeOffset(0), headTable(NULL), hmtxTable(NULL), numGlyphs(0), numOfLongHorMetrics(0), file(NULL) { file = fopen(fileName, "rb"); if (file == NULL) { printf("?? Couldn't open %s", fileName); return; } SFNTDirectory tempDir; fread(&tempDir, sizeof tempDir, 1, file); numTables = SWAPW(tempDir.numTables); searchRange = SWAPW(tempDir.searchRange) >> 4; entrySelector = SWAPW(tempDir.entrySelector); rangeShift = SWAPW(tempDir.rangeShift) >> 4; int dirSize = sizeof tempDir + ((numTables - ANY_NUMBER) * sizeof(DirectoryEntry)); directory = (SFNTDirectory *) new char[dirSize]; fseek(file, 0L, SEEK_SET); fread(directory, sizeof(char), dirSize, file); initUnicodeCMAP(); } FontObject::~FontObject() { fclose(file); delete[] directory; delete[] cmapTable; delete[] headTable; delete[] hmtxTable; } void FontObject::deleteTable(void *table) { delete[] (char *) table; } DirectoryEntry *FontObject::findTable(LETag tag) { le_uint16 table = 0; le_uint16 probe = 1 << entrySelector; if (SWAPL(directory->tableDirectory[rangeShift].tag) <= tag) { table = rangeShift; } while (probe > (1 << 0)) { probe >>= 1; if (SWAPL(directory->tableDirectory[table + probe].tag) <= tag) { table += probe; } } if (SWAPL(directory->tableDirectory[table].tag) == tag) { return &directory->tableDirectory[table]; } return NULL; } void *FontObject::readTable(LETag tag, le_uint32 *length) { DirectoryEntry *entry = findTable(tag); if (entry == NULL) { *length = 0; return NULL; } *length = SWAPL(entry->length); void *table = new char[*length]; fseek(file, SWAPL(entry->offset), SEEK_SET); fread(table, sizeof(char), *length, file); return table; } CMAPEncodingSubtable *FontObject::findCMAP(le_uint16 platformID, le_uint16 platformSpecificID) { LETag cmapTag = 0x636D6170; // 'cmap' if (cmapTable == NULL) { le_uint32 length; cmapTable = (CMAPTable *) readTable(cmapTag, &length); } if (cmapTable != NULL) { le_uint16 i; le_uint16 nSubtables = SWAPW(cmapTable->numberSubtables); for (i = 0; i < nSubtables; i += 1) { CMAPEncodingSubtableHeader *esh = &cmapTable->encodingSubtableHeaders[i]; if (SWAPW(esh->platformID) == platformID && SWAPW(esh->platformSpecificID) == platformSpecificID) { return (CMAPEncodingSubtable *) ((char *) cmapTable + SWAPL(esh->encodingOffset)); } } } return NULL; } void FontObject::initUnicodeCMAP() { CMAPEncodingSubtable *encodingSubtable = findCMAP(3, 1); if (encodingSubtable == 0 || SWAPW(encodingSubtable->format) != 4) { printf("Can't find unicode 'cmap'"); return; } CMAPFormat4Encoding *header = (CMAPFormat4Encoding *) encodingSubtable; cmSegCount = SWAPW(header->segCountX2) / 2; cmSearchRange = SWAPW(header->searchRange); cmEntrySelector = SWAPW(header->entrySelector); cmRangeShift = SWAPW(header->rangeShift) / 2; cmEndCodes = &header->endCodes[0]; cmStartCodes = &header->endCodes[cmSegCount + 1]; // + 1 for reservedPad... cmIdDelta = &cmStartCodes[cmSegCount]; cmIdRangeOffset = &cmIdDelta[cmSegCount]; } LEGlyphID FontObject::unicodeToGlyph(LEUnicode32 unicode32) { if (unicode32 >= 0x10000) { return 0; } LEUnicode16 unicode = (LEUnicode16) unicode32; le_uint16 index = 0; le_uint16 probe = 1 << cmEntrySelector; LEGlyphID result = 0; if (SWAPW(cmStartCodes[cmRangeShift]) <= unicode) { index = cmRangeShift; } while (probe > (1 << 0)) { probe >>= 1; if (SWAPW(cmStartCodes[index + probe]) <= unicode) { index += probe; } } if (unicode >= SWAPW(cmStartCodes[index]) && unicode <= SWAPW(cmEndCodes[index])) { if (cmIdRangeOffset[index] == 0) { result = (LEGlyphID) unicode; } else { le_uint16 offset = unicode - SWAPW(cmStartCodes[index]); le_uint16 rangeOffset = SWAPW(cmIdRangeOffset[index]); le_uint16 *glyphIndexTable = (le_uint16 *) ((char *) &cmIdRangeOffset[index] + rangeOffset); result = SWAPW(glyphIndexTable[offset]); } result += SWAPW(cmIdDelta[index]); } else { result = 0; } return result; } le_uint16 FontObject::getUnitsPerEM() { if (headTable == NULL) { LETag headTag = 0x68656164; // 'head' le_uint32 length; headTable = (HEADTable *) readTable(headTag, &length); } return SWAPW(headTable->unitsPerEm); } le_uint16 FontObject::getGlyphAdvance(LEGlyphID glyph) { if (hmtxTable == NULL) { LETag maxpTag = 0x6D617870; // 'maxp' LETag hheaTag = 0x68686561; // 'hhea' LETag hmtxTag = 0x686D7478; // 'hmtx' le_uint32 length; HHEATable *hheaTable; MAXPTable *maxpTable = (MAXPTable *) readTable(maxpTag, &length); numGlyphs = SWAPW(maxpTable->numGlyphs); deleteTable(maxpTable); hheaTable = (HHEATable *) readTable(hheaTag, &length); numOfLongHorMetrics = SWAPW(hheaTable->numOfLongHorMetrics); deleteTable(hheaTable); hmtxTable = (HMTXTable *) readTable(hmtxTag, &length); } le_uint16 index = glyph; if (glyph >= numGlyphs) { return 0; } if (glyph >= numOfLongHorMetrics) { index = numOfLongHorMetrics - 1; } return SWAPW(hmtxTable->hMetrics[index].advanceWidth); }