Path: blob/master/thirdparty/graphite/src/TtfUtil.cpp
9902 views
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later1// Copyright 2010, SIL International, All rights reserved.23/*4Responsibility: Alan Ward5Last reviewed: Not yet.67Description8Implements the methods for TtfUtil class. This file should remain portable to any C++9environment by only using standard C++ and the TTF structurs defined in Tt.h.10*/111213/***********************************************************************************************14Include files15***********************************************************************************************/16// Language headers17//#include <algorithm>18#include <cassert>19#include <cstddef>20#include <cstring>21#include <climits>22#include <cwchar>23//#include <stdexcept>24// Platform headers25// Module headers26#include "inc/TtfUtil.h"27#include "inc/TtfTypes.h"28#include "inc/Endian.h"2930/***********************************************************************************************31Forward declarations32***********************************************************************************************/3334/***********************************************************************************************35Local Constants and static variables36***********************************************************************************************/37namespace38{39#ifdef ALL_TTFUTILS40// max number of components allowed in composite glyphs41const int kMaxGlyphComponents = 8;42#endif4344template <int R, typename T>45inline float fixed_to_float(const T f) {46return float(f)/float(2^R);47}4849/*----------------------------------------------------------------------------------------------50Table of standard Postscript glyph names. From Martin Hosken. Disagress with ttfdump.exe51---------------------------------------------------------------------------------------------*/52#ifdef ALL_TTFUTILS53const int kcPostNames = 258;5455const char * rgPostName[kcPostNames] = {56".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign",57"dollar", "percent", "ampersand", "quotesingle", "parenleft",58"parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash",59"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",60"nine", "colon", "semicolon", "less", "equal", "greater", "question",61"at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",62"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",63"bracketleft", "backslash", "bracketright", "asciicircum",64"underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i",65"j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",66"x", "y", "z", "braceleft", "bar", "braceright", "asciitilde",67"Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis",68"Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde",69"aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis",70"iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute",71"ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave",72"ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling",73"section", "bullet", "paragraph", "germandbls", "registered",74"copyright", "trademark", "acute", "dieresis", "notequal", "AE",75"Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen",76"mu", "partialdiff", "summation", "product", "pi", "integral",77"ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown",78"exclamdown", "logicalnot", "radical", "florin", "approxequal",79"Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace",80"Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash",81"quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide",82"lozenge", "ydieresis", "Ydieresis", "fraction", "currency",83"guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered",84"quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",85"Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute",86"Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex",87"apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi",88"circumflex", "tilde", "macron", "breve", "dotaccent", "ring",89"cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash",90"Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth",91"Yacute", "yacute", "Thorn", "thorn", "minus", "multiply",92"onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter",93"threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla",94"scedilla", "Cacute", "cacute", "Ccaron", "ccaron",95"dcroat" };96#endif9798} // end of namespace99100/***********************************************************************************************101Methods102***********************************************************************************************/103104/* Note on error processing: The code guards against bad glyph ids being used to look up data105in open ended tables (loca, hmtx). If the glyph id comes from a cmap this shouldn't happen106but it seems prudent to check for user errors here. The code does assume that data obtained107from the TTF file is valid otherwise (though the CheckTable method seeks to check for108obvious problems that might accompany a change in table versions). For example an invalid109offset in the loca table which could exceed the size of the glyf table is NOT trapped.110Likewise if numberOf_LongHorMetrics in the hhea table is wrong, this will NOT be trapped,111which could cause a lookup in the hmtx table to exceed the table length. Of course, TTF tables112that are completely corrupt will cause unpredictable results. */113114/* Note on composite glyphs: Glyphs that have components that are themselves composites115are not supported. IsDeepComposite can be used to test for this. False is returned from many116of the methods in this cases. It is unclear how to build composite glyphs in some cases,117so this code represents my best guess until test cases can be found. See notes on the high-118level GlyfPoints method. */119namespace graphite2120{121namespace TtfUtil122{123124125/*----------------------------------------------------------------------------------------------126Get offset and size of the offset table needed to find table directory.127Return true if success, false otherwise.128lSize excludes any table directory entries.129----------------------------------------------------------------------------------------------*/130bool GetHeaderInfo(size_t & lOffset, size_t & lSize)131{132lOffset = 0;133lSize = offsetof(Sfnt::OffsetSubTable, table_directory);134assert(sizeof(uint32) + 4*sizeof (uint16) == lSize);135return true;136}137138/*----------------------------------------------------------------------------------------------139Check the offset table for expected data.140Return true if success, false otherwise.141----------------------------------------------------------------------------------------------*/142bool CheckHeader(const void * pHdr)143{144const Sfnt::OffsetSubTable * pOffsetTable145= reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr);146147return pHdr && be::swap(pOffsetTable->scaler_type) == Sfnt::OffsetSubTable::TrueTypeWin;148}149150/*----------------------------------------------------------------------------------------------151Get offset and size of the table directory.152Return true if successful, false otherwise.153----------------------------------------------------------------------------------------------*/154bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize)155{156const Sfnt::OffsetSubTable * pOffsetTable157= reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr);158159lOffset = offsetof(Sfnt::OffsetSubTable, table_directory);160lSize = be::swap(pOffsetTable->num_tables)161* sizeof(Sfnt::OffsetSubTable::Entry);162163return true;164}165166167/*----------------------------------------------------------------------------------------------168Get offset and size of the specified table.169Return true if successful, false otherwise. On false, offset and size will be 0.170----------------------------------------------------------------------------------------------*/171bool GetTableInfo(const Tag TableTag, const void * pHdr, const void * pTableDir,172size_t & lOffset, size_t & lSize)173{174const Sfnt::OffsetSubTable * pOffsetTable175= reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr);176const size_t num_tables = be::swap(pOffsetTable->num_tables);177const Sfnt::OffsetSubTable::Entry178* entry_itr = reinterpret_cast<const Sfnt::OffsetSubTable::Entry *>(179pTableDir),180* const dir_end = entry_itr + num_tables;181182if (num_tables > 40)183return false;184185for (;entry_itr != dir_end; ++entry_itr) // 40 - safe guard186{187if (be::swap(entry_itr->tag) == TableTag)188{189lOffset = be::swap(entry_itr->offset);190lSize = be::swap(entry_itr->length);191return true;192}193}194195return false;196}197198/*----------------------------------------------------------------------------------------------199Check the specified table. Tests depend on the table type.200Return true if successful, false otherwise.201----------------------------------------------------------------------------------------------*/202bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)203{204using namespace Sfnt;205206if (pTable == 0 || lTableSize < 4) return false;207208switch(TableId)209{210case Tag::cmap: // cmap211{212const Sfnt::CharacterCodeMap * const pCmap213= reinterpret_cast<const Sfnt::CharacterCodeMap *>(pTable);214if (lTableSize < sizeof(Sfnt::CharacterCodeMap))215return false;216return be::swap(pCmap->version) == 0;217}218219case Tag::head: // head220{221const Sfnt::FontHeader * const pHead222= reinterpret_cast<const Sfnt::FontHeader *>(pTable);223if (lTableSize < sizeof(Sfnt::FontHeader))224return false;225bool r = be::swap(pHead->version) == OneFix226&& be::swap(pHead->magic_number) == FontHeader::MagicNumber227&& be::swap(pHead->glyph_data_format)228== FontHeader::GlypDataFormat229&& (be::swap(pHead->index_to_loc_format)230== FontHeader::ShortIndexLocFormat231|| be::swap(pHead->index_to_loc_format)232== FontHeader::LongIndexLocFormat)233&& sizeof(FontHeader) <= lTableSize;234return r;235}236237case Tag::post: // post238{239const Sfnt::PostScriptGlyphName * const pPost240= reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pTable);241if (lTableSize < sizeof(Sfnt::PostScriptGlyphName))242return false;243const fixed format = be::swap(pPost->format);244bool r = format == PostScriptGlyphName::Format1245|| format == PostScriptGlyphName::Format2246|| format == PostScriptGlyphName::Format3247|| format == PostScriptGlyphName::Format25;248return r;249}250251case Tag::hhea: // hhea252{253const Sfnt::HorizontalHeader * pHhea =254reinterpret_cast<const Sfnt::HorizontalHeader *>(pTable);255if (lTableSize < sizeof(Sfnt::HorizontalHeader))256return false;257bool r = be::swap(pHhea->version) == OneFix258&& be::swap(pHhea->metric_data_format) == 0259&& sizeof (Sfnt::HorizontalHeader) <= lTableSize;260return r;261}262263case Tag::maxp: // maxp264{265const Sfnt::MaximumProfile * pMaxp =266reinterpret_cast<const Sfnt::MaximumProfile *>(pTable);267if (lTableSize < sizeof(Sfnt::MaximumProfile))268return false;269bool r = be::swap(pMaxp->version) == OneFix270&& sizeof(Sfnt::MaximumProfile) <= lTableSize;271return r;272}273274case Tag::OS_2: // OS/2275{276const Sfnt::Compatibility * pOs2277= reinterpret_cast<const Sfnt::Compatibility *>(pTable);278if (be::swap(pOs2->version) == 0)279{ // OS/2 table version 1 size280// if (sizeof(Sfnt::Compatibility)281// - sizeof(uint32)*2 - sizeof(int16)*2282// - sizeof(uint16)*3 <= lTableSize)283if (sizeof(Sfnt::Compatibility0) <= lTableSize)284return true;285}286else if (be::swap(pOs2->version) == 1)287{ // OS/2 table version 2 size288// if (sizeof(Sfnt::Compatibility)289// - sizeof(int16) *2290// - sizeof(uint16)*3 <= lTableSize)291if (sizeof(Sfnt::Compatibility1) <= lTableSize)292return true;293}294else if (be::swap(pOs2->version) == 2)295{ // OS/2 table version 3 size296if (sizeof(Sfnt::Compatibility2) <= lTableSize)297return true;298}299else if (be::swap(pOs2->version) == 3 || be::swap(pOs2->version) == 4)300{ // OS/2 table version 4 size - version 4 changed the meaning of some fields which we don't use301if (sizeof(Sfnt::Compatibility3) <= lTableSize)302return true;303}304else305return false;306break;307}308309case Tag::name:310{311const Sfnt::FontNames * pName312= reinterpret_cast<const Sfnt::FontNames *>(pTable);313if (lTableSize < sizeof(Sfnt::FontNames))314return false;315return be::swap(pName->format) == 0;316}317318case Tag::glyf:319{320return (lTableSize >= sizeof(Sfnt::Glyph));321}322323default:324break;325}326327return true;328}329330/*----------------------------------------------------------------------------------------------331Return the number of glyphs in the font. Should never be less than zero.332333Note: this method is not currently used by the Graphite engine.334----------------------------------------------------------------------------------------------*/335size_t GlyphCount(const void * pMaxp)336{337const Sfnt::MaximumProfile * pTable =338reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp);339return be::swap(pTable->num_glyphs);340}341342#ifdef ALL_TTFUTILS343/*----------------------------------------------------------------------------------------------344Return the maximum number of components for any composite glyph in the font.345346Note: this method is not currently used by the Graphite engine.347----------------------------------------------------------------------------------------------*/348size_t MaxCompositeComponentCount(const void * pMaxp)349{350const Sfnt::MaximumProfile * pTable =351reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp);352return be::swap(pTable->max_component_elements);353}354355/*----------------------------------------------------------------------------------------------356Composite glyphs can be composed of glyphs that are themselves composites.357This method returns the maximum number of levels like this for any glyph in the font.358A non-composite glyph has a level of 1.359360Note: this method is not currently used by the Graphite engine.361----------------------------------------------------------------------------------------------*/362size_t MaxCompositeLevelCount(const void * pMaxp)363{364const Sfnt::MaximumProfile * pTable =365reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp);366return be::swap(pTable->max_component_depth);367}368369/*----------------------------------------------------------------------------------------------370Return the number of glyphs in the font according to a differt source.371Should never be less than zero. Return -1 on failure.372373Note: this method is not currently used by the Graphite engine.374----------------------------------------------------------------------------------------------*/375size_t LocaGlyphCount(size_t lLocaSize, const void * pHead) //throw(std::domain_error)376{377378const Sfnt::FontHeader * pTable379= reinterpret_cast<const Sfnt::FontHeader *>(pHead);380381if (be::swap(pTable->index_to_loc_format)382== Sfnt::FontHeader::ShortIndexLocFormat)383// loca entries are two bytes and have been divided by two384return (lLocaSize >> 1) - 1;385386if (be::swap(pTable->index_to_loc_format)387== Sfnt::FontHeader::LongIndexLocFormat)388// loca entries are four bytes389return (lLocaSize >> 2) - 1;390391return -1;392//throw std::domain_error("head table in inconsistent state. The font may be corrupted");393}394#endif395396/*----------------------------------------------------------------------------------------------397Return the design units the font is designed with398----------------------------------------------------------------------------------------------*/399int DesignUnits(const void * pHead)400{401const Sfnt::FontHeader * pTable =402reinterpret_cast<const Sfnt::FontHeader *>(pHead);403404return be::swap(pTable->units_per_em);405}406407#ifdef ALL_TTFUTILS408/*----------------------------------------------------------------------------------------------409Return the checksum from the head table, which serves as a unique identifer for the font.410----------------------------------------------------------------------------------------------*/411int HeadTableCheckSum(const void * pHead)412{413const Sfnt::FontHeader * pTable =414reinterpret_cast<const Sfnt::FontHeader *>(pHead);415416return be::swap(pTable->check_sum_adjustment);417}418419/*----------------------------------------------------------------------------------------------420Return the create time from the head table. This consists of a 64-bit integer, which421we return here as two 32-bit integers.422423Note: this method is not currently used by the Graphite engine.424----------------------------------------------------------------------------------------------*/425void HeadTableCreateTime(const void * pHead,426unsigned int * pnDateBC, unsigned int * pnDateAD)427{428const Sfnt::FontHeader * pTable =429reinterpret_cast<const Sfnt::FontHeader *>(pHead);430431*pnDateBC = be::swap(pTable->created[0]);432*pnDateAD = be::swap(pTable->created[1]);433}434435/*----------------------------------------------------------------------------------------------436Return the modify time from the head table.This consists of a 64-bit integer, which437we return here as two 32-bit integers.438439Note: this method is not currently used by the Graphite engine.440----------------------------------------------------------------------------------------------*/441void HeadTableModifyTime(const void * pHead,442unsigned int * pnDateBC, unsigned int *pnDateAD)443{444const Sfnt::FontHeader * pTable =445reinterpret_cast<const Sfnt::FontHeader *>(pHead);446;447*pnDateBC = be::swap(pTable->modified[0]);448*pnDateAD = be::swap(pTable->modified[1]);449}450451/*----------------------------------------------------------------------------------------------452Return true if the font is italic.453----------------------------------------------------------------------------------------------*/454bool IsItalic(const void * pHead)455{456const Sfnt::FontHeader * pTable =457reinterpret_cast<const Sfnt::FontHeader *>(pHead);458459return ((be::swap(pTable->mac_style) & 0x00000002) != 0);460}461462/*----------------------------------------------------------------------------------------------463Return the ascent for the font464----------------------------------------------------------------------------------------------*/465int FontAscent(const void * pOs2)466{467const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2);468469return be::swap(pTable->win_ascent);470}471472/*----------------------------------------------------------------------------------------------473Return the descent for the font474----------------------------------------------------------------------------------------------*/475int FontDescent(const void * pOs2)476{477const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2);478479return be::swap(pTable->win_descent);480}481482/*----------------------------------------------------------------------------------------------483Get the bold and italic style bits.484Return true if successful. false otherwise.485In addition to checking the OS/2 table, one could also check486the head table's macStyle field (overridden by the OS/2 table on Win)487the sub-family name in the name table (though this can contain oblique, dark, etc too)488----------------------------------------------------------------------------------------------*/489bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic)490{491const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2);492493fBold = (be::swap(pTable->fs_selection) & Sfnt::Compatibility::Bold) != 0;494fItalic = (be::swap(pTable->fs_selection) & Sfnt::Compatibility::Italic) != 0;495496return true;497}498#endif499500/*----------------------------------------------------------------------------------------------501Method for searching name table.502----------------------------------------------------------------------------------------------*/503bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId,504int nLangId, int nNameId, size_t & lOffset, size_t & lSize)505{506lOffset = 0;507lSize = 0;508509const Sfnt::FontNames * pTable = reinterpret_cast<const Sfnt::FontNames *>(pName);510uint16 cRecord = be::swap(pTable->count);511uint16 nRecordOffset = be::swap(pTable->string_offset);512const Sfnt::NameRecord * pRecord = reinterpret_cast<const Sfnt::NameRecord *>(pTable + 1);513514for (int i = 0; i < cRecord; ++i)515{516if (be::swap(pRecord->platform_id) == nPlatformId &&517be::swap(pRecord->platform_specific_id) == nEncodingId &&518be::swap(pRecord->language_id) == nLangId &&519be::swap(pRecord->name_id) == nNameId)520{521lOffset = be::swap(pRecord->offset) + nRecordOffset;522lSize = be::swap(pRecord->length);523return true;524}525pRecord++;526}527528return false;529}530531#ifdef ALL_TTFUTILS532/*----------------------------------------------------------------------------------------------533Return all the lang-IDs that have data for the given name-IDs. Assume that there is room534in the return array (langIdList) for 128 items. The purpose of this method is to return535a list of all possible lang-IDs.536----------------------------------------------------------------------------------------------*/537int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId,538int * nameIdList, int cNameIds, short * langIdList)539{540const Sfnt::FontNames * pTable = reinterpret_cast<const Sfnt::FontNames *>(pName);541int cLangIds = 0;542uint16 cRecord = be::swap(pTable->count);543if (cRecord > 127) return cLangIds;544//uint16 nRecordOffset = swapw(pTable->stringOffset);545const Sfnt::NameRecord * pRecord = reinterpret_cast<const Sfnt::NameRecord *>(pTable + 1);546547for (int i = 0; i < cRecord; ++i)548{549if (be::swap(pRecord->platform_id) == nPlatformId &&550be::swap(pRecord->platform_specific_id) == nEncodingId)551{552bool fNameFound = false;553int nLangId = be::swap(pRecord->language_id);554int nNameId = be::swap(pRecord->name_id);555for (int j = 0; j < cNameIds; j++)556{557if (nNameId == nameIdList[j])558{559fNameFound = true;560break;561}562}563if (fNameFound)564{565// Add it if it's not there.566int ilang;567for (ilang = 0; ilang < cLangIds; ilang++)568if (langIdList[ilang] == nLangId)569break;570if (ilang >= cLangIds)571{572langIdList[cLangIds] = short(nLangId);573cLangIds++;574}575if (cLangIds == 128)576return cLangIds;577}578}579pRecord++;580}581582return cLangIds;583}584585/*----------------------------------------------------------------------------------------------586Get the offset and size of the font family name in English for the MS Platform with Unicode587writing system. The offset is within the pName data. The string is double byte with MSB588first.589----------------------------------------------------------------------------------------------*/590bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize)591{592return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033,593Sfnt::NameRecord::Family, lOffset, lSize);594}595596/*----------------------------------------------------------------------------------------------597Get the offset and size of the full font name in English for the MS Platform with Unicode598writing system. The offset is within the pName data. The string is double byte with MSB599first.600601Note: this method is not currently used by the Graphite engine.602----------------------------------------------------------------------------------------------*/603bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize)604{605return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033,606Sfnt::NameRecord::Fullname, lOffset, lSize);607}608609/*----------------------------------------------------------------------------------------------610Get the offset and size of the font family name in English for the MS Platform with Symbol611writing system. The offset is within the pName data. The string is double byte with MSB612first.613----------------------------------------------------------------------------------------------*/614bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize)615{616return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033,617Sfnt::NameRecord::Family, lOffset, lSize);618}619620/*----------------------------------------------------------------------------------------------621Get the offset and size of the full font name in English for the MS Platform with Symbol622writing system. The offset is within the pName data. The string is double byte with MSB623first.624625Note: this method is not currently used by the Graphite engine.626----------------------------------------------------------------------------------------------*/627bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize)628{629return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033,630Sfnt::NameRecord::Fullname, lOffset, lSize);631}632633/*----------------------------------------------------------------------------------------------634Return the Glyph ID for a given Postscript name. This method finds the first glyph which635matches the requested Postscript name. Ideally every glyph should have a unique Postscript636name (except for special names such as .notdef), but this is not always true.637On failure return value less than zero.638-1 - table search failed639-2 - format 3 table (no Postscript glyph info)640-3 - other failures641642Note: this method is not currently used by the Graphite engine.643----------------------------------------------------------------------------------------------*/644int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp,645const char * pPostName)646{647using namespace Sfnt;648649const Sfnt::PostScriptGlyphName * pTable650= reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pPost);651fixed format = be::swap(pTable->format);652653if (format == PostScriptGlyphName::Format3)654{ // format 3 - no Postscript glyph info in font655return -2;656}657658// search for given Postscript name among the standard names659int iPostName = -1; // index in standard names660for (int i = 0; i < kcPostNames; i++)661{662if (!strcmp(pPostName, rgPostName[i]))663{664iPostName = i;665break;666}667}668669if (format == PostScriptGlyphName::Format1)670{ // format 1 - use standard Postscript names671return iPostName;672}673674if (format == PostScriptGlyphName::Format25)675{676if (iPostName == -1)677return -1;678679const PostScriptGlyphName25 * pTable25680= static_cast<const PostScriptGlyphName25 *>(pTable);681int cnGlyphs = GlyphCount(pMaxp);682for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs && nGlyphId < kcPostNames;683nGlyphId++)684{ // glyph_name_index25 contains bytes so no byte swapping needed685// search for first glyph id that uses the standard name686if (nGlyphId + pTable25->offset[nGlyphId] == iPostName)687return nGlyphId;688}689}690691if (format == PostScriptGlyphName::Format2)692{ // format 2693const PostScriptGlyphName2 * pTable2694= static_cast<const PostScriptGlyphName2 *>(pTable);695696int cnGlyphs = be::swap(pTable2->number_of_glyphs);697698if (iPostName != -1)699{ // did match a standard name, look for first glyph id mapped to that name700for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++)701{702if (be::swap(pTable2->glyph_name_index[nGlyphId]) == iPostName)703return nGlyphId;704}705}706707{ // did not match a standard name, search font specific names708size_t nStrSizeGoal = strlen(pPostName);709const char * pFirstGlyphName = reinterpret_cast<const char *>(710&pTable2->glyph_name_index[0] + cnGlyphs);711const char * pGlyphName = pFirstGlyphName;712int iInNames = 0; // index in font specific names713bool fFound = false;714const char * const endOfTable715= reinterpret_cast<const char *>(pTable2) + lPostSize;716while (pGlyphName < endOfTable && !fFound)717{ // search Pascal strings for first matching name718size_t nStringSize = size_t(*pGlyphName);719if (nStrSizeGoal != nStringSize ||720strncmp(pGlyphName + 1, pPostName, nStringSize))721{ // did not match722++iInNames;723pGlyphName += nStringSize + 1;724}725else726{ // did match727fFound = true;728}729}730if (!fFound)731return -1; // no font specific name matches request732733iInNames += kcPostNames;734for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++)735{ // search for first glyph id that maps to the found string index736if (be::swap(pTable2->glyph_name_index[nGlyphId]) == iInNames)737return nGlyphId;738}739return -1; // no glyph mapped to this index (very strange)740}741}742743return -3;744}745746/*----------------------------------------------------------------------------------------------747Convert a Unicode character string from big endian (MSB first, Motorola) format to little748endian (LSB first, Intel) format.749nSize is the number of Unicode characters in the string. It should not include any750terminating null. If nSize is 0, it is assumed the string is null terminated. nSize751defaults to 0.752Return true if successful, false otherwise.753----------------------------------------------------------------------------------------------*/754void SwapWString(void * pWStr, size_t nSize /* = 0 */) //throw (std::invalid_argument)755{756if (pWStr == 0)757{758// throw std::invalid_argument("null pointer given");759return;760}761762uint16 * pStr = reinterpret_cast<uint16 *>(pWStr);763uint16 * const pStrEnd = pStr + (nSize == 0 ? wcslen((const wchar_t*)pStr) : nSize);764765for (; pStr != pStrEnd; ++pStr)766*pStr = be::swap(*pStr);767// std::transform(pStr, pStrEnd, pStr, read<uint16>);768769// for (int i = 0; i < nSize; i++)770// { // swap the wide characters in the string771// pStr[i] = utf16(be::swap(uint16(pStr[i])));772// }773}774#endif775776/*----------------------------------------------------------------------------------------------777Get the left-side bearing and and advance width based on the given tables and Glyph ID778Return true if successful, false otherwise. On false, one or both value could be INT_MIN779----------------------------------------------------------------------------------------------*/780bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, const void * pHhea,781int & nLsb, unsigned int & nAdvWid)782{783const Sfnt::HorizontalMetric * phmtx =784reinterpret_cast<const Sfnt::HorizontalMetric *>(pHmtx);785786const Sfnt::HorizontalHeader * phhea =787reinterpret_cast<const Sfnt::HorizontalHeader *>(pHhea);788789size_t cLongHorMetrics = be::swap(phhea->num_long_hor_metrics);790if (nGlyphId < cLongHorMetrics)791{ // glyph id is acceptable792if ((nGlyphId + 1) * sizeof(Sfnt::HorizontalMetric) > lHmtxSize) return false;793nAdvWid = be::swap(phmtx[nGlyphId].advance_width);794nLsb = be::swap(phmtx[nGlyphId].left_side_bearing);795}796else797{798// guard against bad glyph id799size_t lLsbOffset = sizeof(Sfnt::HorizontalMetric) * cLongHorMetrics +800sizeof(int16) * (nGlyphId - cLongHorMetrics); // offset in bytes801// We test like this as LsbOffset is an offset not a length.802if (lLsbOffset >= lHmtxSize - sizeof(int16) || cLongHorMetrics == 0)803{804nLsb = 0;805return false;806}807nAdvWid = be::swap(phmtx[cLongHorMetrics - 1].advance_width);808nLsb = be::peek<int16>(reinterpret_cast<const byte *>(phmtx) + lLsbOffset);809}810811return true;812}813814/*----------------------------------------------------------------------------------------------815Return a pointer to the requested cmap subtable. By default find the Microsoft Unicode816subtable. Pass nEncoding as -1 to find first table that matches only nPlatformId.817Return NULL if the subtable cannot be found.818----------------------------------------------------------------------------------------------*/819const void * FindCmapSubtable(const void * pCmap, int nPlatformId, /* =3 */ int nEncodingId, /* = 1 */ size_t length)820{821const Sfnt::CharacterCodeMap * pTable = reinterpret_cast<const Sfnt::CharacterCodeMap *>(pCmap);822uint16 csuPlatforms = be::swap(pTable->num_subtables);823if (length && (sizeof(Sfnt::CharacterCodeMap) + 8 * (csuPlatforms - 1) > length))824return NULL;825for (int i = 0; i < csuPlatforms; i++)826{827if (be::swap(pTable->encoding[i].platform_id) == nPlatformId &&828(nEncodingId == -1 || be::swap(pTable->encoding[i].platform_specific_id) == nEncodingId))829{830uint32 offset = be::swap(pTable->encoding[i].offset);831const uint8 * pRtn = reinterpret_cast<const uint8 *>(pCmap) + offset;832if (length)833{834if (offset > length - 2) return NULL;835uint16 format = be::read<uint16>(pRtn);836if (format == 4)837{838if (offset > length - 4) return NULL;839uint16 subTableLength = be::peek<uint16>(pRtn);840if (i + 1 == csuPlatforms)841{842if (subTableLength > length - offset)843return NULL;844}845else if (subTableLength > be::swap(pTable->encoding[i+1].offset))846return NULL;847}848if (format == 12)849{850if (offset > length - 6) return NULL;851uint32 subTableLength = be::peek<uint32>(pRtn);852if (i + 1 == csuPlatforms)853{854if (subTableLength > length - offset)855return NULL;856}857else if (subTableLength > be::swap(pTable->encoding[i+1].offset))858return NULL;859}860}861return reinterpret_cast<const uint8 *>(pCmap) + offset;862}863}864865return 0;866}867868/*----------------------------------------------------------------------------------------------869Check the Microsoft Unicode subtable for expected values870----------------------------------------------------------------------------------------------*/871bool CheckCmapSubtable4(const void * pCmapSubtable4, const void * pCmapEnd /*, unsigned int maxgid*/)872{873size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable4;874if (!pCmapSubtable4) return false;875const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable4);876// Bob H say some freeware TT fonts have version 1 (eg, CALIGULA.TTF)877// so don't check subtable version. 21 Mar 2002 spec changes version to language.878if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 4) return false;879const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtable4);880if (table_len < sizeof(*pTable4))881return false;882uint16 length = be::swap(pTable4->length);883if (length > table_len)884return false;885if (length < sizeof(Sfnt::CmapSubTableFormat4))886return false;887uint16 nRanges = be::swap(pTable4->seg_count_x2) >> 1;888if (!nRanges || length < sizeof(Sfnt::CmapSubTableFormat4) + 4 * nRanges * sizeof(uint16))889return false;890// check last range is properly terminated891uint16 chEnd = be::peek<uint16>(pTable4->end_code + nRanges - 1);892if (chEnd != 0xFFFF)893return false;894#if 0895int lastend = -1;896for (int i = 0; i < nRanges; ++i)897{898uint16 end = be::peek<uint16>(pTable4->end_code + i);899uint16 start = be::peek<uint16>(pTable4->end_code + nRanges + 1 + i);900int16 delta = be::peek<int16>(pTable4->end_code + 2*nRanges + 1 + i);901uint16 offset = be::peek<uint16>(pTable4->end_code + 3*nRanges + 1 + i);902if (lastend >= end || lastend >= start)903return false;904if (offset)905{906const uint16 *gstart = pTable4->end_code + 3*nRanges + 1 + i + (offset >> 1);907const uint16 *gend = gstart + end - start;908if ((char *)gend >= (char *)pCmapSubtable4 + length)909return false;910while (gstart <= gend)911{912uint16 g = be::peek<uint16>(gstart++);913if (g && ((g + delta) & 0xFFFF) > maxgid)914return false;915}916}917else if (((delta + end) & 0xFFFF) > maxgid)918return false;919lastend = end;920}921#endif922return true;923}924925/*----------------------------------------------------------------------------------------------926Return the Glyph ID for the given Unicode ID in the Microsoft Unicode subtable.927(Actually this code only depends on subtable being format 4.)928Return 0 if the Unicode ID is not in the subtable.929----------------------------------------------------------------------------------------------*/930gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey)931{932const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtabel4);933934uint16 nSeg = be::swap(pTable->seg_count_x2) >> 1;935936uint16 n;937const uint16 * pLeft, * pMid;938uint16 cMid, chStart, chEnd;939940if (rangeKey)941{942pMid = &(pTable->end_code[rangeKey]);943chEnd = be::peek<uint16>(pMid);944}945else946{947// Binary search of the endCode[] array948pLeft = &(pTable->end_code[0]);949n = nSeg;950while (n > 0)951{952cMid = n >> 1; // Pick an element in the middle953pMid = pLeft + cMid;954chEnd = be::peek<uint16>(pMid);955if (nUnicodeId <= chEnd)956{957if (cMid == 0 || nUnicodeId > be::peek<uint16>(pMid -1))958break; // Must be this seg or none!959n = cMid; // Continue on left side, omitting mid point960}961else962{963pLeft = pMid + 1; // Continue on right side, omitting mid point964n -= (cMid + 1);965}966}967968if (!n)969return 0;970}971972// Ok, we're down to one segment and pMid points to the endCode element973// Either this is it or none is.974975chStart = be::peek<uint16>(pMid += nSeg + 1);976if (chEnd >= nUnicodeId && nUnicodeId >= chStart)977{978// Found correct segment. Find Glyph Id979int16 idDelta = be::peek<uint16>(pMid += nSeg);980uint16 idRangeOffset = be::peek<uint16>(pMid += nSeg);981982if (idRangeOffset == 0)983return (uint16)(idDelta + nUnicodeId); // must use modulus 2^16984985// Look up value in glyphIdArray986const ptrdiff_t offset = (nUnicodeId - chStart) + (idRangeOffset >> 1) +987(pMid - reinterpret_cast<const uint16 *>(pTable));988if (offset * 2 + 1 >= be::swap<uint16>(pTable->length))989return 0;990gid16 nGlyphId = be::peek<uint16>(reinterpret_cast<const uint16 *>(pTable)+offset);991// If this value is 0, return 0. Else add the idDelta992return nGlyphId ? nGlyphId + idDelta : 0;993}994995return 0;996}997998/*----------------------------------------------------------------------------------------------999Return the next Unicode value in the cmap. Pass 0 to obtain the first item.1000Returns 0xFFFF as the last item.1001pRangeKey is an optional key that is used to optimize the search; its value is the range1002in which the character is found.1003----------------------------------------------------------------------------------------------*/1004unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId, int * pRangeKey)1005{1006const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmap31);10071008uint16 nRange = be::swap(pTable->seg_count_x2) >> 1;10091010uint32 nUnicodePrev = (uint32)nUnicodeId;10111012const uint16 * pStartCode = &(pTable->end_code[0])1013+ nRange // length of end code array1014+ 1; // reserved word10151016if (nUnicodePrev == 0)1017{1018// return the first codepoint.1019if (pRangeKey)1020*pRangeKey = 0;1021return be::peek<uint16>(pStartCode);1022}1023else if (nUnicodePrev >= 0xFFFF)1024{1025if (pRangeKey)1026*pRangeKey = nRange - 1;1027return 0xFFFF;1028}10291030int iRange = (pRangeKey) ? *pRangeKey : 0;1031// Just in case we have a bad key:1032while (iRange > 0 && be::peek<uint16>(pStartCode + iRange) > nUnicodePrev)1033iRange--;1034while (iRange < nRange - 1 && be::peek<uint16>(pTable->end_code + iRange) < nUnicodePrev)1035iRange++;10361037// Now iRange is the range containing nUnicodePrev.1038unsigned int nStartCode = be::peek<uint16>(pStartCode + iRange);1039unsigned int nEndCode = be::peek<uint16>(pTable->end_code + iRange);10401041if (nStartCode > nUnicodePrev)1042// Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable1043// answer this time around.1044nUnicodePrev = nStartCode - 1;10451046if (nEndCode > nUnicodePrev)1047{1048// Next is in the same range; it is the next successive codepoint.1049if (pRangeKey)1050*pRangeKey = iRange;1051return nUnicodePrev + 1;1052}10531054// Otherwise the next codepoint is the first one in the next range.1055// There is guaranteed to be a next range because there must be one that1056// ends with 0xFFFF.1057if (pRangeKey)1058*pRangeKey = iRange + 1;1059return (iRange + 1 >= nRange) ? 0xFFFF : be::peek<uint16>(pStartCode + iRange + 1);1060}10611062/*----------------------------------------------------------------------------------------------1063Check the Microsoft UCS-4 subtable for expected values.1064----------------------------------------------------------------------------------------------*/1065bool CheckCmapSubtable12(const void *pCmapSubtable12, const void *pCmapEnd /*, unsigned int maxgid*/)1066{1067size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable12;1068if (!pCmapSubtable12) return false;1069const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable12);1070if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 12)1071return false;1072const Sfnt::CmapSubTableFormat12 * pTable12 = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmapSubtable12);1073if (table_len < sizeof(*pTable12))1074return false;1075uint32 length = be::swap(pTable12->length);1076if (length > table_len)1077return false;1078if (length < sizeof(Sfnt::CmapSubTableFormat12))1079return false;1080uint32 num_groups = be::swap(pTable12->num_groups);1081if (num_groups > 0x10000000 || length != (sizeof(Sfnt::CmapSubTableFormat12) + (num_groups - 1) * sizeof(uint32) * 3))1082return false;1083#if 01084for (unsigned int i = 0; i < num_groups; ++i)1085{1086if (be::swap(pTable12->group[i].end_char_code) - be::swap(pTable12->group[i].start_char_code) + be::swap(pTable12->group[i].start_glyph_id) > maxgid)1087return false;1088if (i > 0 && be::swap(pTable12->group[i].start_char_code) <= be::swap(pTable12->group[i-1].end_char_code))1089return false;1090}1091#endif1092return true;1093}10941095/*----------------------------------------------------------------------------------------------1096Return the Glyph ID for the given Unicode ID in the Microsoft UCS-4 subtable.1097(Actually this code only depends on subtable being format 12.)1098Return 0 if the Unicode ID is not in the subtable.1099----------------------------------------------------------------------------------------------*/1100gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey)1101{1102const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmap310);11031104//uint32 uLength = be::swap(pTable->length); //could use to test for premature end of table1105uint32 ucGroups = be::swap(pTable->num_groups);11061107for (unsigned int i = rangeKey; i < ucGroups; i++)1108{1109uint32 uStartCode = be::swap(pTable->group[i].start_char_code);1110uint32 uEndCode = be::swap(pTable->group[i].end_char_code);1111if (uUnicodeId >= uStartCode && uUnicodeId <= uEndCode)1112{1113uint32 uDiff = uUnicodeId - uStartCode;1114uint32 uStartGid = be::swap(pTable->group[i].start_glyph_id);1115return static_cast<gid16>(uStartGid + uDiff);1116}1117}11181119return 0;1120}11211122/*----------------------------------------------------------------------------------------------1123Return the next Unicode value in the cmap. Pass 0 to obtain the first item.1124Returns 0x10FFFF as the last item.1125pRangeKey is an optional key that is used to optimize the search; its value is the range1126in which the character is found.1127----------------------------------------------------------------------------------------------*/1128unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId, int * pRangeKey)1129{1130const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmap310);11311132int nRange = be::swap(pTable->num_groups);11331134uint32 nUnicodePrev = (uint32)nUnicodeId;11351136if (nUnicodePrev == 0)1137{1138// return the first codepoint.1139if (pRangeKey)1140*pRangeKey = 0;1141return be::swap(pTable->group[0].start_char_code);1142}1143else if (nUnicodePrev >= 0x10FFFF)1144{1145if (pRangeKey)1146*pRangeKey = nRange;1147return 0x10FFFF;1148}11491150int iRange = (pRangeKey) ? *pRangeKey : 0;1151// Just in case we have a bad key:1152while (iRange > 0 && be::swap(pTable->group[iRange].start_char_code) > nUnicodePrev)1153iRange--;1154while (iRange < nRange - 1 && be::swap(pTable->group[iRange].end_char_code) < nUnicodePrev)1155iRange++;11561157// Now iRange is the range containing nUnicodePrev.11581159unsigned int nStartCode = be::swap(pTable->group[iRange].start_char_code);1160unsigned int nEndCode = be::swap(pTable->group[iRange].end_char_code);11611162if (nStartCode > nUnicodePrev)1163// Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable1164// answer this time around.1165nUnicodePrev = nStartCode - 1;11661167if (nEndCode > nUnicodePrev)1168{1169// Next is in the same range; it is the next successive codepoint.1170if (pRangeKey)1171*pRangeKey = iRange;1172return nUnicodePrev + 1;1173}11741175// Otherwise the next codepoint is the first one in the next range, or 10FFFF if we're done.1176if (pRangeKey)1177*pRangeKey = iRange + 1;1178return (iRange + 1 >= nRange) ? 0x10FFFF : be::swap(pTable->group[iRange + 1].start_char_code);1179}11801181/*----------------------------------------------------------------------------------------------1182Return the offset stored in the loca table for the given Glyph ID.1183(This offset is into the glyf table.)1184Return -1 if the lookup failed.1185Technically this method should return an unsigned long but it is unlikely the offset will1186exceed 2^31.1187----------------------------------------------------------------------------------------------*/1188size_t LocaLookup(gid16 nGlyphId,1189const void * pLoca, size_t lLocaSize,1190const void * pHead) // throw (std::out_of_range)1191{1192const Sfnt::FontHeader * pTable = reinterpret_cast<const Sfnt::FontHeader *>(pHead);1193size_t res = -2;11941195// CheckTable verifies the index_to_loc_format is valid1196if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat)1197{ // loca entries are two bytes and have been divided by two1198if (lLocaSize > 1 && nGlyphId + 1u < lLocaSize >> 1) // allow sentinel value to be accessed1199{1200const uint16 * pShortTable = reinterpret_cast<const uint16 *>(pLoca);1201res = be::peek<uint16>(pShortTable + nGlyphId) << 1;1202if (res == static_cast<size_t>(be::peek<uint16>(pShortTable + nGlyphId + 1) << 1))1203return -1;1204}1205}1206else if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)1207{ // loca entries are four bytes1208if (lLocaSize > 3 && nGlyphId + 1u < lLocaSize >> 2)1209{1210const uint32 * pLongTable = reinterpret_cast<const uint32 *>(pLoca);1211res = be::peek<uint32>(pLongTable + nGlyphId);1212if (res == static_cast<size_t>(be::peek<uint32>(pLongTable + nGlyphId + 1)))1213return -1;1214}1215}12161217// only get here if glyph id was bad1218return res;1219//throw std::out_of_range("glyph id out of range for font");1220}12211222/*----------------------------------------------------------------------------------------------1223Return a pointer into the glyf table based on the given offset (from LocaLookup).1224Return NULL on error.1225----------------------------------------------------------------------------------------------*/1226void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset, size_t nTableLen)1227{1228const uint8 * pByte = reinterpret_cast<const uint8 *>(pGlyf);1229if (OVERFLOW_OFFSET_CHECK(pByte, nGlyfOffset) || nGlyfOffset >= nTableLen - sizeof(Sfnt::Glyph))1230return NULL;1231return const_cast<uint8 *>(pByte + nGlyfOffset);1232}12331234/*----------------------------------------------------------------------------------------------1235Get the bounding box coordinates for a simple glyf entry (non-composite).1236Return true if successful, false otherwise.1237----------------------------------------------------------------------------------------------*/1238bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin,1239int & xMax, int & yMax)1240{1241const Sfnt::Glyph * pGlyph = reinterpret_cast<const Sfnt::Glyph *>(pSimpleGlyf);12421243xMin = be::swap(pGlyph->x_min);1244yMin = be::swap(pGlyph->y_min);1245xMax = be::swap(pGlyph->x_max);1246yMax = be::swap(pGlyph->y_max);12471248return true;1249}12501251#ifdef ALL_TTFUTILS1252/*----------------------------------------------------------------------------------------------1253Return the number of contours for a simple glyf entry (non-composite)1254Returning -1 means this is a composite glyph1255----------------------------------------------------------------------------------------------*/1256int GlyfContourCount(const void * pSimpleGlyf)1257{1258const Sfnt::Glyph * pGlyph = reinterpret_cast<const Sfnt::Glyph *>(pSimpleGlyf);1259return be::swap(pGlyph->number_of_contours); // -1 means composite glyph1260}12611262/*----------------------------------------------------------------------------------------------1263Get the point numbers for the end points of the glyph contours for a simple1264glyf entry (non-composite).1265cnPointsTotal - count of contours from GlyfContourCount(); (same as number of end points)1266prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers1267cnPoints - count of points placed in above range1268Return true if successful, false otherwise.1269False could indicate a multi-level composite glyphs.1270----------------------------------------------------------------------------------------------*/1271bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint,1272int cnPointsTotal, int & cnPoints)1273{1274const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);12751276int cContours = be::swap(pGlyph->number_of_contours);1277if (cContours < 0)1278return false; // this method isn't supposed handle composite glyphs12791280for (int i = 0; i < cContours && i < cnPointsTotal; i++)1281{1282prgnContourEndPoint[i] = be::swap(pGlyph->end_pts_of_contours[i]);1283}12841285cnPoints = cContours;1286return true;1287}12881289/*----------------------------------------------------------------------------------------------1290Get the points for a simple glyf entry (non-composite)1291cnPointsTotal - count of points from largest end point obtained from GlyfContourEndPoints1292prgnX & prgnY - should point to buffers large enough to hold cnPointsTotal integers1293The ranges are parallel so that coordinates for point(n) are found at offset n in both1294ranges. This is raw point data with relative coordinates.1295prgbFlag - should point to a buffer a large enough to hold cnPointsTotal bytes1296This range is parallel to the prgnX & prgnY1297cnPoints - count of points placed in above ranges1298Return true if successful, false otherwise.1299False could indicate a composite glyph1300----------------------------------------------------------------------------------------------*/1301bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY,1302char * prgbFlag, int cnPointsTotal, int & cnPoints)1303{1304using namespace Sfnt;13051306const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);1307int cContours = be::swap(pGlyph->number_of_contours);1308// return false for composite glyph1309if (cContours <= 0)1310return false;1311int cPts = be::swap(pGlyph->end_pts_of_contours[cContours - 1]) + 1;1312if (cPts > cnPointsTotal)1313return false;13141315// skip over bounding box data & point to byte count of instructions (hints)1316const uint8 * pbGlyph = reinterpret_cast<const uint8 *>1317(&pGlyph->end_pts_of_contours[cContours]);13181319// skip over hints & point to first flag1320int cbHints = be::swap(*(uint16 *)pbGlyph);1321pbGlyph += sizeof(uint16);1322pbGlyph += cbHints;13231324// load flags & point to first x coordinate1325int iFlag = 0;1326while (iFlag < cPts)1327{1328if (!(*pbGlyph & SimpleGlyph::Repeat))1329{ // flag isn't repeated1330prgbFlag[iFlag] = (char)*pbGlyph;1331pbGlyph++;1332iFlag++;1333}1334else1335{ // flag is repeated; count specified by next byte1336char chFlag = (char)*pbGlyph;1337pbGlyph++;1338int cFlags = (int)*pbGlyph;1339pbGlyph++;1340prgbFlag[iFlag] = chFlag;1341iFlag++;1342for (int i = 0; i < cFlags; i++)1343{1344prgbFlag[iFlag + i] = chFlag;1345}1346iFlag += cFlags;1347}1348}1349if (iFlag != cPts)1350return false;13511352// load x coordinates1353iFlag = 0;1354while (iFlag < cPts)1355{1356if (prgbFlag[iFlag] & SimpleGlyph::XShort)1357{1358prgnX[iFlag] = *pbGlyph;1359if (!(prgbFlag[iFlag] & SimpleGlyph::XIsPos))1360{1361prgnX[iFlag] = -prgnX[iFlag];1362}1363pbGlyph++;1364}1365else1366{1367if (prgbFlag[iFlag] & SimpleGlyph::XIsSame)1368{1369prgnX[iFlag] = 0;1370// do NOT increment pbGlyph1371}1372else1373{1374prgnX[iFlag] = be::swap(*(int16 *)pbGlyph);1375pbGlyph += sizeof(int16);1376}1377}1378iFlag++;1379}13801381// load y coordinates1382iFlag = 0;1383while (iFlag < cPts)1384{1385if (prgbFlag[iFlag] & SimpleGlyph::YShort)1386{1387prgnY[iFlag] = *pbGlyph;1388if (!(prgbFlag[iFlag] & SimpleGlyph::YIsPos))1389{1390prgnY[iFlag] = -prgnY[iFlag];1391}1392pbGlyph++;1393}1394else1395{1396if (prgbFlag[iFlag] & SimpleGlyph::YIsSame)1397{1398prgnY[iFlag] = 0;1399// do NOT increment pbGlyph1400}1401else1402{1403prgnY[iFlag] = be::swap(*(int16 *)pbGlyph);1404pbGlyph += sizeof(int16);1405}1406}1407iFlag++;1408}14091410cnPoints = cPts;1411return true;1412}14131414/*----------------------------------------------------------------------------------------------1415Fill prgnCompId with the component Glyph IDs from pSimpleGlyf.1416Client must allocate space before calling.1417pSimpleGlyf - assumed to point to a composite glyph1418cCompIdTotal - the number of elements in prgnCompId1419cCompId - the total number of Glyph IDs stored in prgnCompId1420Return true if successful, false otherwise1421False could indicate a non-composite glyph or the input array was not big enough1422----------------------------------------------------------------------------------------------*/1423bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId,1424size_t cnCompIdTotal, size_t & cnCompId)1425{1426using namespace Sfnt;14271428if (GlyfContourCount(pSimpleGlyf) >= 0)1429return false;14301431const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);1432// for a composite glyph, the special data begins here1433const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]);14341435uint16 GlyphFlags;1436size_t iCurrentComp = 0;1437do1438{1439GlyphFlags = be::swap(*((uint16 *)pbGlyph));1440pbGlyph += sizeof(uint16);1441prgnCompId[iCurrentComp++] = be::swap(*((uint16 *)pbGlyph));1442pbGlyph += sizeof(uint16);1443if (iCurrentComp >= cnCompIdTotal)1444return false;1445int nOffset = 0;1446nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2;1447nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0;1448nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0;1449nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0;1450pbGlyph += nOffset;1451} while (GlyphFlags & CompoundGlyph::MoreComponents);14521453cnCompId = iCurrentComp;14541455return true;1456}14571458/*----------------------------------------------------------------------------------------------1459Return info on how a component glyph is to be placed1460pSimpleGlyph - assumed to point to a composite glyph1461nCompId - glyph id for component of interest1462bOffset - if true, a & b are the x & y offsets for this component1463if false, b is the point on this component that is attaching to point a on the1464preceding glyph1465Return true if successful, false otherwise1466False could indicate a non-composite glyph or that component wasn't found1467----------------------------------------------------------------------------------------------*/1468bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId,1469bool fOffset, int & a, int & b)1470{1471using namespace Sfnt;14721473if (GlyfContourCount(pSimpleGlyf) >= 0)1474return false;14751476const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);1477// for a composite glyph, the special data begins here1478const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]);14791480uint16 GlyphFlags;1481do1482{1483GlyphFlags = be::swap(*((uint16 *)pbGlyph));1484pbGlyph += sizeof(uint16);1485if (be::swap(*((uint16 *)pbGlyph)) == nCompId)1486{1487pbGlyph += sizeof(uint16); // skip over glyph id of component1488fOffset = (GlyphFlags & CompoundGlyph::ArgsAreXYValues) == CompoundGlyph::ArgsAreXYValues;14891490if (GlyphFlags & CompoundGlyph::Arg1Arg2Words )1491{1492a = be::swap(*(int16 *)pbGlyph);1493pbGlyph += sizeof(int16);1494b = be::swap(*(int16 *)pbGlyph);1495pbGlyph += sizeof(int16);1496}1497else1498{ // args are signed bytes1499a = *pbGlyph++;1500b = *pbGlyph++;1501}1502return true;1503}1504pbGlyph += sizeof(uint16); // skip over glyph id of component1505int nOffset = 0;1506nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2;1507nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0;1508nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0;1509nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0;1510pbGlyph += nOffset;1511} while (GlyphFlags & CompoundGlyph::MoreComponents);15121513// didn't find requested component1514fOffset = true;1515a = 0;1516b = 0;1517return false;1518}15191520/*----------------------------------------------------------------------------------------------1521Return info on how a component glyph is to be transformed1522pSimpleGlyph - assumed to point to a composite glyph1523nCompId - glyph id for component of interest1524flt11, flt11, flt11, flt11 - a 2x2 matrix giving the transform1525bTransOffset - whether to transform the offset from above method1526The spec is unclear about the meaning of this flag1527Currently - initialize to true for MS rasterizer and false for Mac rasterizer, then1528on return it will indicate whether transform should apply to offset (MSDN CD 10/99)1529Return true if successful, false otherwise1530False could indicate a non-composite glyph or that component wasn't found1531----------------------------------------------------------------------------------------------*/1532bool GetComponentTransform(const void * pSimpleGlyf, int nCompId,1533float & flt11, float & flt12, float & flt21, float & flt22,1534bool & fTransOffset)1535{1536using namespace Sfnt;15371538if (GlyfContourCount(pSimpleGlyf) >= 0)1539return false;15401541const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);1542// for a composite glyph, the special data begins here1543const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]);15441545uint16 GlyphFlags;1546do1547{1548GlyphFlags = be::swap(*((uint16 *)pbGlyph));1549pbGlyph += sizeof(uint16);1550if (be::swap(*((uint16 *)pbGlyph)) == nCompId)1551{1552pbGlyph += sizeof(uint16); // skip over glyph id of component1553pbGlyph += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2; // skip over placement data15541555if (fTransOffset) // MS rasterizer1556fTransOffset = !(GlyphFlags & CompoundGlyph::UnscaledOffset);1557else // Apple rasterizer1558fTransOffset = (GlyphFlags & CompoundGlyph::ScaledOffset) != 0;15591560if (GlyphFlags & CompoundGlyph::HaveScale)1561{1562flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));1563pbGlyph += sizeof(uint16);1564flt12 = 0;1565flt21 = 0;1566flt22 = flt11;1567}1568else if (GlyphFlags & CompoundGlyph::HaveXAndYScale)1569{1570flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));1571pbGlyph += sizeof(uint16);1572flt12 = 0;1573flt21 = 0;1574flt22 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));1575pbGlyph += sizeof(uint16);1576}1577else if (GlyphFlags & CompoundGlyph::HaveTwoByTwo)1578{1579flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));1580pbGlyph += sizeof(uint16);1581flt12 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));1582pbGlyph += sizeof(uint16);1583flt21 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));1584pbGlyph += sizeof(uint16);1585flt22 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));1586pbGlyph += sizeof(uint16);1587}1588else1589{ // identity transform1590flt11 = 1.0;1591flt12 = 0.0;1592flt21 = 0.0;1593flt22 = 1.0;1594}1595return true;1596}1597pbGlyph += sizeof(uint16); // skip over glyph id of component1598int nOffset = 0;1599nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2;1600nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0;1601nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale ? 4 : 0;1602nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo ? 8 : 0;1603pbGlyph += nOffset;1604} while (GlyphFlags & CompoundGlyph::MoreComponents);16051606// didn't find requested component1607fTransOffset = false;1608flt11 = 1;1609flt12 = 0;1610flt21 = 0;1611flt22 = 1;1612return false;1613}1614#endif16151616/*----------------------------------------------------------------------------------------------1617Return a pointer into the glyf table based on the given tables and Glyph ID1618Since this method doesn't check for spaces, it is good to call IsSpace before using it.1619Return NULL on error.1620----------------------------------------------------------------------------------------------*/1621void * GlyfLookup(gid16 nGlyphId, const void * pGlyf, const void * pLoca,1622size_t lGlyfSize, size_t lLocaSize, const void * pHead)1623{1624// test for valid glyph id1625// CheckTable verifies the index_to_loc_format is valid16261627const Sfnt::FontHeader * pTable1628= reinterpret_cast<const Sfnt::FontHeader *>(pHead);16291630if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat)1631{ // loca entries are two bytes (and have been divided by two)1632if (nGlyphId >= (lLocaSize >> 1) - 1) // don't allow nGlyphId to access sentinel1633{1634// throw std::out_of_range("glyph id out of range for font");1635return NULL;1636}1637}1638if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)1639{ // loca entries are four bytes1640if (nGlyphId >= (lLocaSize >> 2) - 1)1641{1642// throw std::out_of_range("glyph id out of range for font");1643return NULL;1644}1645}16461647size_t lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead);1648void * pSimpleGlyf = GlyfLookup(pGlyf, lGlyfOffset, lGlyfSize); // invalid loca offset returns null1649return pSimpleGlyf;1650}16511652#ifdef ALL_TTFUTILS1653/*----------------------------------------------------------------------------------------------1654Determine if a particular Glyph ID has any data in the glyf table. If it is white space,1655there will be no glyf data, though there will be metric data in hmtx, etc.1656----------------------------------------------------------------------------------------------*/1657bool IsSpace(gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead)1658{1659size_t lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead);16601661// the +1 should always work because there is a sentinel value at the end of the loca table1662size_t lNextGlyfOffset = LocaLookup(nGlyphId + 1, pLoca, lLocaSize, pHead);16631664return (lNextGlyfOffset - lGlyfOffset) == 0;1665}16661667/*----------------------------------------------------------------------------------------------1668Determine if a particular Glyph ID is a multi-level composite.1669----------------------------------------------------------------------------------------------*/1670bool IsDeepComposite(gid16 nGlyphId, const void * pGlyf, const void * pLoca,1671size_t lGlyfSize, long lLocaSize, const void * pHead)1672{1673if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}16741675void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1676if (pSimpleGlyf == NULL)1677return false; // no way to really indicate an error occured here16781679if (GlyfContourCount(pSimpleGlyf) >= 0)1680return false;16811682int rgnCompId[kMaxGlyphComponents]; // assumes only a limited number of glyph components1683size_t cCompIdTotal = kMaxGlyphComponents;1684size_t cCompId = 0;16851686if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))1687return false;16881689for (size_t i = 0; i < cCompId; i++)1690{1691pSimpleGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]),1692pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1693if (pSimpleGlyf == NULL) {return false;}16941695if (GlyfContourCount(pSimpleGlyf) < 0)1696return true;1697}16981699return false;1700}17011702/*----------------------------------------------------------------------------------------------1703Get the bounding box coordinates based on the given tables and Glyph ID1704Handles both simple and composite glyphs.1705Return true if successful, false otherwise. On false, all point values will be INT_MIN1706False may indicate a white space glyph1707----------------------------------------------------------------------------------------------*/1708bool GlyfBox(gid16 nGlyphId, const void * pGlyf, const void * pLoca,1709size_t lGlyfSize, size_t lLocaSize, const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax)1710{1711xMin = yMin = xMax = yMax = INT_MIN;17121713if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}17141715void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1716if (pSimpleGlyf == NULL) {return false;}17171718return GlyfBox(pSimpleGlyf, xMin, yMin, xMax, yMax);1719}17201721/*----------------------------------------------------------------------------------------------1722Get the number of contours based on the given tables and Glyph ID1723Handles both simple and composite glyphs.1724Return true if successful, false otherwise. On false, cnContours will be INT_MIN1725False may indicate a white space glyph or a multi-level composite glyph.1726----------------------------------------------------------------------------------------------*/1727bool GlyfContourCount(gid16 nGlyphId, const void * pGlyf, const void * pLoca,1728size_t lGlyfSize, size_t lLocaSize, const void * pHead, size_t & cnContours)1729{1730cnContours = static_cast<size_t>(INT_MIN);17311732if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}17331734void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1735if (pSimpleGlyf == NULL) {return false;}17361737int cRtnContours = GlyfContourCount(pSimpleGlyf);1738if (cRtnContours >= 0)1739{1740cnContours = size_t(cRtnContours);1741return true;1742}17431744//handle composite glyphs17451746int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components1747size_t cCompIdTotal = kMaxGlyphComponents;1748size_t cCompId = 0;17491750if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))1751return false;17521753cRtnContours = 0;1754int cTmp = 0;1755for (size_t i = 0; i < cCompId; i++)1756{1757if (IsSpace(static_cast<gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;}1758pSimpleGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]),1759pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1760if (pSimpleGlyf == 0) {return false;}1761// return false on multi-level composite1762if ((cTmp = GlyfContourCount(pSimpleGlyf)) < 0)1763return false;1764cRtnContours += cTmp;1765}17661767cnContours = size_t(cRtnContours);1768return true;1769}17701771/*----------------------------------------------------------------------------------------------1772Get the point numbers for the end points of the glyph contours based on the given tables1773and Glyph ID1774Handles both simple and composite glyphs.1775cnPoints - count of contours from GlyfContourCount (same as number of end points)1776prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers1777Return true if successful, false otherwise. On false, all end points are INT_MIN1778False may indicate a white space glyph or a multi-level composite glyph.1779----------------------------------------------------------------------------------------------*/1780bool GlyfContourEndPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca,1781size_t lGlyfSize, size_t lLocaSize, const void * pHead,1782int * prgnContourEndPoint, size_t cnPoints)1783{1784memset(prgnContourEndPoint, 0xFF, cnPoints * sizeof(int));1785// std::fill_n(prgnContourEndPoint, cnPoints, INT_MIN);17861787if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}17881789void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1790if (pSimpleGlyf == NULL) {return false;}17911792int cContours = GlyfContourCount(pSimpleGlyf);1793int cActualPts = 0;1794if (cContours > 0)1795return GlyfContourEndPoints(pSimpleGlyf, prgnContourEndPoint, cnPoints, cActualPts);17961797// handle composite glyphs17981799int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components1800size_t cCompIdTotal = kMaxGlyphComponents;1801size_t cCompId = 0;18021803if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))1804return false;18051806int * prgnCurrentEndPoint = prgnContourEndPoint;1807int cCurrentPoints = cnPoints;1808int nPrevPt = 0;1809for (size_t i = 0; i < cCompId; i++)1810{1811if (IsSpace(static_cast<gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;}1812pSimpleGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]), pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1813if (pSimpleGlyf == NULL) {return false;}1814// returns false on multi-level composite1815if (!GlyfContourEndPoints(pSimpleGlyf, prgnCurrentEndPoint, cCurrentPoints, cActualPts))1816return false;1817// points in composite are numbered sequentially as components are added1818// must adjust end point numbers for new point numbers1819for (int j = 0; j < cActualPts; j++)1820prgnCurrentEndPoint[j] += nPrevPt;1821nPrevPt = prgnCurrentEndPoint[cActualPts - 1] + 1;18221823prgnCurrentEndPoint += cActualPts;1824cCurrentPoints -= cActualPts;1825}18261827return true;1828}18291830/*----------------------------------------------------------------------------------------------1831Get the points for a glyph based on the given tables and Glyph ID1832Handles both simple and composite glyphs.1833cnPoints - count of points from largest end point obtained from GlyfContourEndPoints1834prgnX & prgnY - should point to buffers large enough to hold cnPoints integers1835The ranges are parallel so that coordinates for point(n) are found at offset n in1836both ranges. These points are in absolute coordinates.1837prgfOnCurve - should point to a buffer a large enough to hold cnPoints bytes (bool)1838This range is parallel to the prgnX & prgnY1839Return true if successful, false otherwise. On false, all points may be INT_MIN1840False may indicate a white space glyph, a multi-level composite, or a corrupt font1841It's not clear from the TTF spec when the transforms should be applied. Should the1842transform be done before or after attachment point calcs? (current code - before)1843Should the transform be applied to other offsets? (currently - no; however commented1844out code is in place so that if CompoundGlyph::UnscaledOffset on the MS rasterizer is1845clear (typical) then yes, and if CompoundGlyph::ScaledOffset on the Apple rasterizer is1846clear (typical?) then no). See GetComponentTransform.1847It's also unclear where point numbering with attachment poinst starts1848(currently - first point number is relative to whole glyph, second point number is1849relative to current glyph).1850----------------------------------------------------------------------------------------------*/1851bool GlyfPoints(gid16 nGlyphId, const void * pGlyf,1852const void * pLoca, size_t lGlyfSize, size_t lLocaSize, const void * pHead,1853const int * /*prgnContourEndPoint*/, size_t /*cnEndPoints*/,1854int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints)1855{1856memset(prgnX, 0x7F, cnPoints * sizeof(int));1857memset(prgnY, 0x7F, cnPoints * sizeof(int));18581859if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead))1860return false;18611862void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1863if (pSimpleGlyf == NULL)1864return false;18651866int cContours = GlyfContourCount(pSimpleGlyf);1867int cActualPts;1868if (cContours > 0)1869{1870if (!GlyfPoints(pSimpleGlyf, prgnX, prgnY, (char *)prgfOnCurve, cnPoints, cActualPts))1871return false;1872CalcAbsolutePoints(prgnX, prgnY, cnPoints);1873SimplifyFlags((char *)prgfOnCurve, cnPoints);1874return true;1875}18761877// handle composite glyphs1878int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components1879size_t cCompIdTotal = kMaxGlyphComponents;1880size_t cCompId = 0;18811882// this will fail if there are more components than there is room for1883if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))1884return false;18851886int * prgnCurrentX = prgnX;1887int * prgnCurrentY = prgnY;1888char * prgbCurrentFlag = (char *)prgfOnCurve; // converting bool to char should be safe1889int cCurrentPoints = cnPoints;1890bool fOffset = true, fTransOff = true;1891int a, b;1892float flt11, flt12, flt21, flt22;1893// int * prgnPrevX = prgnX; // in case first att pt number relative to preceding glyph1894// int * prgnPrevY = prgnY;1895for (size_t i = 0; i < cCompId; i++)1896{1897if (IsSpace(static_cast<gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;}1898void * pCompGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]), pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);1899if (pCompGlyf == NULL) {return false;}1900// returns false on multi-level composite1901if (!GlyfPoints(pCompGlyf, prgnCurrentX, prgnCurrentY, prgbCurrentFlag,1902cCurrentPoints, cActualPts))1903return false;1904if (!GetComponentPlacement(pSimpleGlyf, rgnCompId[i], fOffset, a, b))1905return false;1906if (!GetComponentTransform(pSimpleGlyf, rgnCompId[i],1907flt11, flt12, flt21, flt22, fTransOff))1908return false;1909bool fIdTrans = flt11 == 1.0 && flt12 == 0.0 && flt21 == 0.0 && flt22 == 1.0;19101911// convert points to absolute coordinates1912// do before transform and attachment point placement are applied1913CalcAbsolutePoints(prgnCurrentX, prgnCurrentY, cActualPts);19141915// apply transform - see main method note above1916// do before attachment point calcs1917if (!fIdTrans)1918for (int j = 0; j < cActualPts; j++)1919{1920int x = prgnCurrentX[j]; // store before transform applied1921int y = prgnCurrentY[j];1922prgnCurrentX[j] = (int)(x * flt11 + y * flt12);1923prgnCurrentY[j] = (int)(x * flt21 + y * flt22);1924}19251926// apply placement - see main method note above1927int nXOff, nYOff;1928if (fOffset) // explicit x & y offsets1929{1930/* ignore fTransOff for now1931if (fTransOff && !fIdTrans)1932{ // transform x & y offsets1933nXOff = (int)(a * flt11 + b * flt12);1934nYOff = (int)(a * flt21 + b * flt22);1935}1936else */1937{ // don't transform offset1938nXOff = a;1939nYOff = b;1940}1941}1942else // attachment points1943{ // in case first point is relative to preceding glyph and second relative to current1944// nXOff = prgnPrevX[a] - prgnCurrentX[b];1945// nYOff = prgnPrevY[a] - prgnCurrentY[b];1946// first point number relative to whole composite, second relative to current glyph1947nXOff = prgnX[a] - prgnCurrentX[b];1948nYOff = prgnY[a] - prgnCurrentY[b];1949}1950for (int j = 0; j < cActualPts; j++)1951{1952prgnCurrentX[j] += nXOff;1953prgnCurrentY[j] += nYOff;1954}19551956// prgnPrevX = prgnCurrentX;1957// prgnPrevY = prgnCurrentY;1958prgnCurrentX += cActualPts;1959prgnCurrentY += cActualPts;1960prgbCurrentFlag += cActualPts;1961cCurrentPoints -= cActualPts;1962}19631964SimplifyFlags((char *)prgfOnCurve, cnPoints);19651966return true;1967}19681969/*----------------------------------------------------------------------------------------------1970Simplify the meaning of flags to just indicate whether point is on-curve or off-curve.1971---------------------------------------------------------------------------------------------*/1972bool SimplifyFlags(char * prgbFlags, int cnPoints)1973{1974for (int i = 0; i < cnPoints; i++)1975prgbFlags[i] = static_cast<char>(prgbFlags[i] & Sfnt::SimpleGlyph::OnCurve);1976return true;1977}19781979/*----------------------------------------------------------------------------------------------1980Convert relative point coordinates to absolute coordinates1981Points are stored in the font such that they are offsets from one another except for the1982first point of a glyph.1983---------------------------------------------------------------------------------------------*/1984bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints)1985{1986int nX = prgnX[0];1987int nY = prgnY[0];1988for (int i = 1; i < cnPoints; i++)1989{1990prgnX[i] += nX;1991nX = prgnX[i];1992prgnY[i] += nY;1993nY = prgnY[i];1994}19951996return true;1997}1998#endif19992000/*----------------------------------------------------------------------------------------------2001Return the length of the 'name' table in bytes.2002Currently used.2003---------------------------------------------------------------------------------------------*/2004#if 02005size_t NameTableLength(const byte * pTable)2006{2007byte * pb = (const_cast<byte *>(pTable)) + 2; // skip format2008size_t cRecords = *pb++ << 8; cRecords += *pb++;2009int dbStringOffset0 = (*pb++) << 8; dbStringOffset0 += *pb++;2010int dbMaxStringOffset = 0;2011for (size_t irec = 0; irec < cRecords; irec++)2012{2013int nPlatform = (*pb++) << 8; nPlatform += *pb++;2014int nEncoding = (*pb++) << 8; nEncoding += *pb++;2015int nLanguage = (*pb++) << 8; nLanguage += *pb++;2016int nName = (*pb++) << 8; nName += *pb++;2017int cbStringLen = (*pb++) << 8; cbStringLen += *pb++;2018int dbStringOffset = (*pb++) << 8; dbStringOffset += *pb++;2019if (dbMaxStringOffset < dbStringOffset + cbStringLen)2020dbMaxStringOffset = dbStringOffset + cbStringLen;2021}2022return dbStringOffset0 + dbMaxStringOffset;2023}2024#endif20252026} // end of namespace TtfUtil2027} // end of namespace graphite202820292030