/* * Adobe Universal Font Library * * Copyright (c) 1996 Adobe Systems Inc. * All Rights Reserved * * GOODNAME.C * * * $Header: */ /*===============================================================================* * Include files used by this interface * *===============================================================================*/ #include "UFLPriv.h" #include "UFLMem.h" #include "UFLMath.h" #include "UFLStd.h" #include "UFLErr.h" #include "UFLPS.h" #include "ParseTT.h" #include "UFLVm.h" #include "ufot42.h" #include "goodname.h" #include "ttformat.h" /* ------------------------------------------------------------- */ static void GetTTcmap2Stuff( void *pTTcmap, TTcmap2Stuff *p2 ) { if (pTTcmap == NULL) return; p2->pByte = (unsigned char *) pTTcmap; /* subHeaderKeys[256] starts at fourth WORD */ p2->subHeaderKeys = (unsigned short *) (p2->pByte + 6); p2->subHeaders = (PTTcmap2SH)(p2->pByte + 6 + 2 * 256); } static void GetTTcmap4Stuff( void *pTTcmap, TTcmap4Stuff *p4 ) { unsigned short *pWord; /* a pointer in WORD format */ if (pTTcmap == NULL) return; /* a convenient pointer */ pWord = (unsigned short *)pTTcmap; /* fourth WORD is segCount X 2 */ p4->segCount = (MOTOROLAINT(pWord[3]))/2; p4->endCode = pWord + 7; p4->startCode = pWord + 7 + p4->segCount * 1 + 1; p4->idDelta = pWord + 7 + p4->segCount * 2 + 1; p4->idRangeOffset = pWord + 7 + p4->segCount * 3 + 1; p4->glyphIdArray = pWord + 7 + p4->segCount * 4 + 1; } static void GetTTmortStuff( void *pTTmort, /* mort table data */ TTmortStuff *p ) { unsigned short *pWord; /* a pointer in WORD format */ if (pTTmort == NULL) return; /* a convenient pointer */ pWord = (unsigned short *)pTTmort; /* 34th Word is the second Unit16 in the BinSrchHeader */ p->nEntries = MOTOROLAINT(pWord[34]); /* LookupSingle starts at 77th byte - 38th Word */ p->pGlyphSet = pWord + 38 ; } /* ------------------------------------------------------------- */ static void GetTTGSUBStuff( void *pTTGSUB, /* GSUB table data */ TTGSUBStuff *p ) { unsigned short *pWord; /* a pointer in WORD format */ unsigned short offSet; if (pTTGSUB == NULL) return; /* a convenient pointer */ pWord = (unsigned short *)pTTGSUB; /* fourth WORD is offset to LooupList */ offSet = MOTOROLAINT(pWord[4]); p->pLookupList = (unsigned short *)((unsigned char *)pTTGSUB + offSet); p->lookupCount = MOTOROLAINT(p->pLookupList[0] ); } /* ------------------------------------------------------------- */ /* This function is responsible for cmap, mort and GSUB */ unsigned short GetTablesFromTTFont( UFOStruct *pUFObj ) { unsigned short retVal = 0; unsigned long dwSize, dwOffset, dwcmapSize; unsigned short wData[4]; unsigned short numSubTables, index; PSubTableEntry pTableEntry = NULL; unsigned long cmapOffset; UFLBool foundUnicodeCmap = 0; unsigned short platformID, encodingID, format; AFontStruct *pAFont; unsigned long length; if (pUFObj == NULL) return 0; pAFont = pUFObj->pAFont; if (pAFont == NULL) return 0; /* Check if the cmap/mort/GSUB data is already in pTTFData */ if (pAFont->gotTables) return 1; /* setup booleans - so we don't have to look into data for correctness */ pAFont->hascmap = 0; pAFont->hasmort = 0; pAFont->hasGSUB = 0; pAFont->gsubTBSize = 0; pAFont->cmapTBSize = 0; pAFont->mortTBSize = 0; pAFont->gotTables = 1; /* Get cmap table */ dwSize= GETTTFONTDATA(pUFObj, CMAP_TABLE, 0, (void *) wData, 4, pUFObj->pFData->fontIndex); if (dwSize==0 || dwSize==0xFFFFFFFFL) { goto exit0; // no SubHeader !!! } /* usually 2 or 3 subTables */ numSubTables = MOTOROLAINT(wData[1]); pTableEntry = UFLNewPtr(pUFObj->pMem, numSubTables * sizeof(SubTableEntry)); if (pTableEntry == NULL) goto exit0; /**********************/ /* Get cmap subtables */ /**********************/ dwSize= GETTTFONTDATA(pUFObj, CMAP_TABLE, 4, // skip header (void *) pTableEntry, numSubTables * sizeof(SubTableEntry), pUFObj->pFData->fontIndex); if (dwSize==0 || dwSize==0xFFFFFFFFL) { goto exit0; // no SubHeader !!! } /* We Prefer Unicode Encoding: PlatForm=3, Encoding = 1 * Since the subtable entries are sorted by PlatformID and then EncodingID, * our searching is reallyg this order: * Mac:J->CT->K->CS, Win:Uni->J->CS->CT->K and we will stop if found Win:Uni * or the last one found (in the list) will be used. */ foundUnicodeCmap = 0; cmapOffset = 0; for (index = 0; index < numSubTables && !foundUnicodeCmap; index++) { platformID = MOTOROLAINT((pTableEntry + index)->platformID); encodingID = MOTOROLAINT((pTableEntry + index)->encodingID); dwOffset = MOTOROLALONG((pTableEntry + index)->offset); if (platformID != 3) continue; /* Get cmap subtable's format - first USHORT at Table->offset */ dwSize= GETTTFONTDATA(pUFObj, CMAP_TABLE, dwOffset, (void *) &(wData[0]), 4, pUFObj->pFData->fontIndex); if (dwSize == 0 || dwSize == 0xFFFFFFFF) continue; format = MOTOROLAINT(wData[0]); length = MOTOROLAINT(wData[1]); /* we only parse format 2 or 4 for now */ if (format != 2 && format !=4) continue; switch(encodingID) { case 1: if (format == 2) pAFont->cmapFormat = DTT_Win_UNICODE_cmap2; else /* must be 4 */ pAFont->cmapFormat = DTT_Win_UNICODE_cmap4; cmapOffset = dwOffset; dwcmapSize = length; foundUnicodeCmap = 1; break; case 2: if (format == 2) pAFont->cmapFormat = DTT_Win_J_cmap2; else /* must be 4 */ pAFont->cmapFormat = DTT_Win_J_cmap4; cmapOffset = dwOffset; dwcmapSize = length; break; case 3: /* PRC- TTF docs says Big5, but Win95CT's minglu.ttc is Big5, has encodingdID=4 */ if (format == 2) pAFont->cmapFormat = DTT_Win_CS_cmap2; else /* must be 4 */ pAFont->cmapFormat = DTT_Win_CS_cmap4; cmapOffset = dwOffset; dwcmapSize = length; break; case 4: /* MingLi.ttc on Win95CT has EncodiingID 4 even though TTF Doc says should be 3 */ if (format == 2) pAFont->cmapFormat = DTT_Win_CT_cmap2; else /* must be 4 */ pAFont->cmapFormat = DTT_Win_CT_cmap4; cmapOffset = dwOffset; dwcmapSize = length; break; case 5: if (format == 2) pAFont->cmapFormat = DTT_Win_K_cmap2; else /* must be 4 */ pAFont->cmapFormat = DTT_Win_K_cmap4; cmapOffset = dwOffset; dwcmapSize = length; break; default: break; } } if (cmapOffset == 0) goto exit0; /* Some TTFs have bad dwcmapSize (wData[1]), so far only Dfgihi7.ttc * (see bug 289106) has 4 bytes more than dwcmapSize. * Since we don't want to inspect the tables' relationships to get * the real length at this late stage (1-14-99), we just read * in 8 bytes more. * If these 8 bytes are not used, it doesn't hurt any one. * If they are use as in dfgihi.ttc, we fix 289106 */ dwcmapSize += 8; /* next buffer is global cache - not freed per job */ pAFont->pTTcmap = UFLNewPtr(pUFObj->pMem, dwcmapSize ); if (pAFont->pTTcmap == NULL) goto exit0; /* Get this cmap subtable data */ dwSize= GETTTFONTDATA(pUFObj, CMAP_TABLE, cmapOffset, (void *) pAFont->pTTcmap, dwcmapSize, pUFObj->pFData->fontIndex); if (dwSize > 0 && dwSize < 0xFFFFFFFF) { pAFont->hascmap = 1; pAFont->cmapTBSize = dwcmapSize; /* Set the convenient pointers */ if (TTcmap_IS_FORMAT2(pAFont->cmapFormat)) GetTTcmap2Stuff(pAFont->pTTcmap, &(pAFont->cmap2) ); else /* must be 4 */ GetTTcmap4Stuff(pAFont->pTTcmap, &(pAFont->cmap4) ); retVal = 1; /* finally success */ } else { goto exit0; } /* Continue to get GSUB and mort only if we have Unicode/CJK cmap */ if (retVal == 0) goto exit0; /**********************/ /* get mort table */ /**********************/ dwSize= GETTTFONTDATA(pUFObj, MORT_TABLE, 0, NULL, /* use NULL to ask for size first */ 0, pUFObj->pFData->fontIndex); if (dwSize > mort_HEADERSIZE && dwSize < 0xFFFFFFFF) { /* Has "mort" in this font - it is Optional */ /* next buffer is global cache - not freed per job */ pAFont->pTTmort = UFLNewPtr(pUFObj->pMem, dwSize ); if (pAFont->pTTmort != NULL) { /* Get the mort table data */ dwSize= GETTTFONTDATA(pUFObj, MORT_TABLE, 0, (void *) pAFont->pTTmort, dwSize, pUFObj->pFData->fontIndex); if (dwSize > mort_HEADERSIZE && dwSize < 0xFFFFFFFF) { pAFont->hasmort = 1; pAFont->mortTBSize = dwSize; /* Set the convenient pointers */ GetTTmortStuff(pAFont->pTTmort, &(pAFont->mortStuff) ); } } } /**********************/ /* get GSUB table */ /**********************/ dwSize= GETTTFONTDATA(pUFObj, GSUB_TABLE, 0, NULL, /* use NULL to ask for size first */ 0, pUFObj->pFData->fontIndex); if (dwSize > GSUB_HEADERSIZE && dwSize < 0xFFFFFFFF) { /* Has "GSUB" in this font - it is Optional */ /* next buffer is global cache - not freed per job */ pAFont->pTTGSUB = UFLNewPtr(pUFObj->pMem, dwSize ); if (pAFont->pTTGSUB != NULL) { /* Get the GSUB table data */ dwSize= GETTTFONTDATA(pUFObj, GSUB_TABLE, 0, (void *) pAFont->pTTGSUB, dwSize, pUFObj->pFData->fontIndex); if (dwSize > GSUB_HEADERSIZE && dwSize < 0xFFFFFFFF) { pAFont->hasGSUB = 1; pAFont->gsubTBSize = dwSize; /* Set the convenient pointers */ GetTTGSUBStuff(pAFont->pTTGSUB, &(pAFont->GSUBStuff) ); } } } exit0: if (pTableEntry) UFLDeletePtr(pUFObj->pMem, pTableEntry); return retVal; } static unsigned short ParseTTcmap2( TTcmap2Stuff *p2 , /* all convenient pointers are here */ unsigned char *pTTCMAPEnd, unsigned short gid ) { unsigned short codeVal; unsigned short index; unsigned short subHdrKey; PTTcmap2SH pSubHeader; unsigned short highByte, first, count, byteOffset, id; short delta; unsigned short *pTemp; /* This function parses cmap Format 2: High-byte mapping through table Win 3-(1/2/3/4/5)-2 */ codeVal = 0; if (((unsigned char *)(p2->subHeaderKeys) < (unsigned char *)(p2->pByte)) || ((unsigned char *)(p2->subHeaderKeys + 256) > pTTCMAPEnd)) return codeVal; /* Search subHeaders one by one: subHeader 0 is special: it is used for single-byte character codes, Other highByte mapped to subHeader 0 should be ignored */ for (highByte = 0; highByte<256; highByte++) { subHdrKey = MOTOROLAINT(p2->subHeaderKeys[highByte]); if (highByte != 0 && subHdrKey == 0 ) continue; pSubHeader = p2->subHeaders + (subHdrKey / 8); if (((unsigned char *)(pSubHeader) < (unsigned char *)(p2->pByte)) || ((unsigned char *)(pSubHeader + 1) > pTTCMAPEnd)) continue; first = MOTOROLAINT(pSubHeader->firstCode); count = MOTOROLAINT(pSubHeader->entryCount); delta = MOTOROLAINT(pSubHeader->idDelta); byteOffset = MOTOROLAINT(pSubHeader->idRangeOffset); /* How to use idRangeOffset? The document says: "The value of the idRangeOffset is the number of bytes past the actual location of the idRangeOffset word where the glyphIndexArray element corresponding to firstCode appears" * Parsing cmap == parsing these words carefully (trial-and-error)! Offset to idRangeOffset is 524 + subHdrKey - now we know why subHdrKey is i*8 */ byteOffset += 524 + subHdrKey ; for (index = 0; index < count; index++) { pTemp = (unsigned short *) (p2->pByte + byteOffset + 2 * index); if (((unsigned char *)pTemp < (unsigned char *)(p2->pByte)) || ((unsigned char *)pTemp > pTTCMAPEnd)) continue; id = *(pTemp); id = MOTOROLAINT(id); if (id == 0) continue; id += delta ; if (id == gid) { codeVal = (highByte << 8) + index + first ; return codeVal; } } } return codeVal; } static unsigned short ParseTTcmap4( TTcmap4Stuff *p4 , /* all convenient pointers are here */ unsigned char *pTTCMAP, unsigned char *pTTCMAPEnd, unsigned short gid ) { unsigned short codeVal; long index, j, k, rangeNum; unsigned short gidStart, gidEnd; unsigned short n1, n2; /* This function parses cmap Format 4: Segment mapping to delta values Win 3-(1/2/3/4/5)-4 */ codeVal = 0; if (((unsigned char *)(p4->idRangeOffset) < pTTCMAP) || ((unsigned char *)(p4->idRangeOffset + p4->segCount) > pTTCMAPEnd)) return codeVal; if (((unsigned char *)(p4->startCode) < pTTCMAP) || ((unsigned char *)(p4->startCode + p4->segCount) > pTTCMAPEnd)) return codeVal; if (((unsigned char *)(p4->idDelta) < pTTCMAP) || ((unsigned char *)(p4->idDelta + p4->segCount) > pTTCMAPEnd)) return codeVal; if (((unsigned char *)(p4->endCode) < pTTCMAP) || ((unsigned char *)(p4->endCode + p4->segCount) > pTTCMAPEnd)) return codeVal; /* Search for segments with idRangeOffset[i] =0 */ for (index = 0; index <= (long)p4->segCount; index++) { if (p4->idRangeOffset[index] != 0) continue; gidStart = MOTOROLAINT(p4->idDelta[index]) + MOTOROLAINT(p4->startCode[index]); gidEnd = MOTOROLAINT(p4->idDelta[index]) + MOTOROLAINT(p4->endCode[index]); if (gidStart <= gid && gidEnd >= gid) { codeVal = gid - MOTOROLAINT(p4->idDelta[index]); return codeVal; } } /* Still not found, Search for segments with idRangeOffset[i] !=0 */ for (index = 0; index <= (long)p4->segCount; index++) { if (p4->idRangeOffset[index] == 0) continue; n1 = MOTOROLAINT(p4->startCode[index]); n2 = MOTOROLAINT(p4->endCode[index]); rangeNum = n2 - n1; /* check for End of cmap - fix bug 261628 */ if (n1 == 0xFFFF) break; /* check for Bad cmap */ if (n1 > n2) break; /* have to check one-by-one */ for (j = 0; j <= rangeNum; j++) { /* Word index to glyphIDArray */ k = j + MOTOROLAINT(p4->idRangeOffset[index]) / 2 - p4->segCount + index ; gidStart = MOTOROLAINT(p4->glyphIdArray[k]); if (gidStart != 0) { gidStart += MOTOROLAINT(p4->idDelta[index]); if (gidStart == gid) { codeVal = MOTOROLAINT(p4->startCode[index]) + (unsigned short)j; return codeVal; } } } } return codeVal; } static unsigned short ParseTTcmapForUnicode( AFontStruct *pAFont, unsigned short gid, unsigned short *pUV, unsigned short wSize ) { unsigned short codeVal; unsigned char *pTTCMAPEnd; pTTCMAPEnd = ((unsigned char *)pAFont->pTTcmap) + pAFont->cmapTBSize; /* Find code point for the glyph id by reversing cmap */ if (TTcmap_IS_FORMAT2(pAFont->cmapFormat)) codeVal = ParseTTcmap2(&(pAFont->cmap2), pTTCMAPEnd, gid); else /* must be 4 */ codeVal = ParseTTcmap4(&(pAFont->cmap4), pAFont->pTTcmap, pTTCMAPEnd, gid); if (codeVal == 0) return 0; /* found corresponding code - convert to Unicode if code is not Unicode*/ *pUV = codeVal; return 1; } /* ------------------------------------------------------------------ */ /* Coverage Table: CoverageFormat1 table: Individual glyph indices Type Name Description uint16 CoverageFormat Format identifier format = 1 uint16 GlyphCount Number of glyphs in the GlyphArray GlyphID GlyphArray[GlyphCount] Array of GlyphIDs in numerical order CoverageFormat2 table: Range of glyphs Type Name Description uint16 CoverageFormat Format identifier format = 2 uint16 RangeCount Number of RangeRecords struct RangeRecord[RangeCount] Array of glyph ranges ordered by Start GlyphID RangeRecord Type Name Description GlyphID Start First GlyphID in the range GlyphID End Last GlyphID in the range uint16 StartCoverageIndex Coverage Index of first GlyphID in range */ /* A lcoal function to enumerate/parse Coverage table used in GSUB: * gid0 - starting point for next covered Gid; * it is also used as state variable, 0 at beginning. * gidInput - one gid Covered >= gid; 0xFFFF when there is no such gid * coverageIndex - Coverage Index for gidIn * return: true if there are more glyphs covered */ static UFLBool EnumTTCoverage( void *pCoverage, /* Coverage table */ unsigned char *pTTGSUBEnd, unsigned short gid0, /* start */ unsigned short *gidInput, /* gid covered */ unsigned short *coverageIndex /* corresponding Index */ ) { unsigned short *pWord; unsigned short cFormat, gCount, gid; unsigned short *pGid; long index; if (pCoverage == NULL || gid0 == 0xFFFF) return 0; // pCoverage is a good pointer, it has been checked begore calling this function pWord = (unsigned short *) pCoverage; cFormat = MOTOROLAINT(pWord[0]); gCount = MOTOROLAINT(pWord[1]); /* count of Glyph or ranGes */ pGid = (unsigned short *)((unsigned char *)pCoverage + 4); /* find next gid >= gid0 -- Coverage is ordered! */ if (cFormat == 1) { // Fixed bug #516519 if ((unsigned char *)(pGid + gCount) > pTTGSUBEnd) goto Done; /* List format, pGid points to GlyphArray and coverageIndex starts from 0 */ for (index = 0; index < (long)gCount; index++ ) { gid = MOTOROLAINT(pGid[index]); if (gid >= gid0) { *gidInput = gid; *coverageIndex = (unsigned short)index; return 1; } } } else if (cFormat == 2) { /* Range format, pGid points to first RangeRecord */ unsigned short gidStart, gidEnd, startCoverageIndex; // Fixed bug #516519 if ((unsigned char *)(pGid + gCount*3) > pTTGSUBEnd) goto Done; for (index = 0; index < (long)gCount; index++ ) { gidStart = MOTOROLAINT(pGid[0]); gidEnd = MOTOROLAINT(pGid[1]); startCoverageIndex = MOTOROLAINT(pGid[2]); /* first if gid0==0 */ if (gid0 == 0) { if ( index == 0 ) { *gidInput = gidStart; *coverageIndex = startCoverageIndex; return 1; } } /* find the first range that covers gid0 */ else if (gid0 >= gidStart && gid0 <= gidEnd ) { *gidInput = gid0; *coverageIndex = startCoverageIndex + gid0 - gidStart; return 1; } pGid += 3; /* Each RangeRecord is 3 ASUns16 */ } } /* else don't know or new format */ Done: *gidInput = 0xFFFF; *coverageIndex = 0xFFFF; return 0; } /* ------------------------------------------------------------------ */ /* SingleSubstitution Table SingleSubstFormat1 subtable: Calculated output glyph indices Type Name Description uint16 SubstFormat Format identifier-format = 1 Offset Coverage Offset to Coverage table-from beginning of substitution table int16 DeltaGlyphID Add to original GlyphID to get substitute GlyphID SingleSubstFormat2 subtable: Specified output glyph indices Type Name Description uint16 SubstFormat Format identifier-format = 2 Offset Coverage Offset to Coverage table-from beginning of Substitution table uint16 GlyphCount Number of GlyphIDs in the Substitute array GlyphID Substitute[GlyphCount] Array of substitute GlyphIDs -ordered by Coverage Index */ /* A local function to parse Substitution Table Format 1 * have to linear search - we are doing reverse mapping from * the substitutedGID to original gid */ static unsigned short ParseTTSubstTable_1( void *pSubstTable, /* Subst table data */ unsigned char *pTTGSUBEnd, unsigned short gid, /* Given GID */ unsigned short *pRSubGid /* Reverse Substitution */ ) { unsigned short *pTable; void *pCoverage; unsigned short substFormat; unsigned short offSet; unsigned short gidIn, coverageIndex; // pTable is a good pointer, it has been checked begore calling this function pTable = (unsigned short *)(pSubstTable); /* pTable points to either SingleSubstFormat1 or SingleSubstFormat2 */ substFormat = MOTOROLAINT(pTable[0]); offSet = MOTOROLAINT(pTable[1]); pCoverage = (void *) ((unsigned char *)pSubstTable + offSet ); // Fixed bug #516519. // Check to make sure pConverage is good. It is used in function EnumTTCoverage if (((unsigned char *)pCoverage < (unsigned char *)pSubstTable) || ((unsigned char *)pCoverage + 3*sizeof(unsigned short)) > pTTGSUBEnd) { return 0; } if (substFormat == 1 ) { unsigned short delta = MOTOROLAINT(pTable[2]); gidIn = 0; coverageIndex = 0; while (EnumTTCoverage(pCoverage, pTTGSUBEnd, gidIn, &gidIn, &coverageIndex) ) { if (gid == gidIn + delta) { /* gidIn may be substituted by gid in the PS file, return the reverseSubstituiton */ *pRSubGid = gidIn; return 1; } gidIn++; /* For EnumTTCoverage() */ } return 0; } else if (substFormat == 2 ) { unsigned short count, gidSub; count = MOTOROLAINT(pTable[2]); gidIn = 0; coverageIndex = 0; while (EnumTTCoverage(pCoverage, pTTGSUBEnd, gidIn, &gidIn, &coverageIndex) ) { if (coverageIndex < count) { gidSub = MOTOROLAINT(pTable[ 3 + coverageIndex]); if (gid == gidSub) { /* gidIn may be substituted by gid in the PS file, return the reverseSubstituiton */ *pRSubGid = gidIn; return 1; } } gidIn++; /* For EnumTTCoverage() */ } return 0; } /* else unknow or not found */ return 0; } /* ------------------------------------------------------------------ */ /* AlternateSubstFormat1 subtable: Alternative output glyphs Type Name Description uint16 SubstFormat Format identifier-format = 1 Offset Coverage Offset to Coverage table-from beginning of Substitution table uint16 AlternateSetCount Number of AlternateSet tables Offset AlternateSet[AlternateSetCount] Array of offsets to AlternateSet tables -from beginning of Substitution table-ordered by Coverage Index AlternateSet table Type Name Description uint16 GlyphCount Number of GlyphIDs in the Alternate array GlyphID Alternate[GlyphCount] Array of alternate GlyphIDs-in arbitrary order */ /* A local function to parse Substitution Table Format 1 * have to linear search - we are doing reverse mapping from * the substitutedGID to original gid */ static unsigned short ParseTTSubstTable_3( void *pSubstTable, /* Subst table data */ unsigned char *pTTGSUBEnd, unsigned short gid, /* Given GID */ unsigned short *pRSubGid /* Reverse Substitution */ ) { unsigned short *pTable; void *pCoverage; unsigned short substFormat; unsigned short offSet, altCount; unsigned short gidIn, coverageIndex; unsigned short *pAlt; // pTable is a good pointer, it has been checked begore calling this function pTable = (unsigned short *)(pSubstTable); /* pTable points to AlternateSubstFormat1 */ substFormat = MOTOROLAINT(pTable[0]); offSet = MOTOROLAINT(pTable[1]); pCoverage = (void *) ((unsigned char *)pSubstTable + offSet ); // Fixed bug #516519 // Check to make sure pConverage is good. It is used in function EnumTTCoverage if (((unsigned char *)pCoverage < (unsigned char *)pSubstTable) || ((unsigned char *)pCoverage + 3*sizeof(unsigned short)) > pTTGSUBEnd) { return 0; } altCount = MOTOROLAINT(pTable[2]); if (substFormat == 1 ) { unsigned short index, gCount, gidAlt; gidIn = 0; coverageIndex = 0; while (EnumTTCoverage(pCoverage, pTTGSUBEnd, gidIn, &gidIn, &coverageIndex) ) { if (coverageIndex < altCount) { /* For each gidIn, we have an array of alternates */ offSet = MOTOROLAINT(pTable[3 + coverageIndex] ); pAlt = (unsigned short *) ((unsigned char *)pSubstTable + offSet ); gCount = MOTOROLAINT(pAlt[0]); for (index = 0; index < gCount; index++) { gidAlt = MOTOROLAINT(pAlt[1 + index]); if (gidAlt == gid) { /* gidIn may be substituted by gid in the PS file, return the reverseSubstituiton */ *pRSubGid = gidIn; return 1; } } } gidIn++; /* For EnumTTCoverage() */ } return 0; } /* else unknow or not found */ return 0; } /* ------------------------------------------------------------------ */ /* Function to parse GSUB table. Since we only want the substituted glyph index * to parse cmap for Unicode information, we don't have to look at * ScriptList/FeatureList. For LookUpTypes, we only check 1-1 substitution: * -Single- replace one glyph with another ,and * -Alternate- replace one glyph with one of many glyphs * Ignore Multple/Ligature/Context - are they for a text layout only??? * GSUB Header (Offset = uint16) Type Name Description fixed32 Version Version of the GSUB table initially set to 0x00010000 Offset ScriptList Offset to ScriptList table from beginning of GSUB table Offset FeatureList Offset to FeatureList table from beginning of GSUB table Offset LookupList Offset to LookupList table from beginning of GSUB table Lookup List: Type Name Description uint16 LookupCount Number of lookups in this table Offset Lookup[LookupCount] Array of offsets to Lookup tables from beginning of LookupList (first lookup is Lookup index = 0) Lookup Table Type Name Description uint16 LookupType Different enumerations for GSUB uint16 LookupFlag Lookup qualifiers uint16 SubTableCount Number of SubTables for this lookup Offset SubTable[SubTableCount] Array of offsets to SubTables from beginning of Lookup table */ static unsigned short ParseTTGSUBForSubGid( void *pTTGSUB, /* GSUB table data */ unsigned long gsubTBSize, TTGSUBStuff *p, unsigned short gid, /* Given GID */ unsigned short *pRSubGid /* Reverse Substitution */ ) { unsigned short lookupType; unsigned short subTableCount; long i, j; unsigned short offSet; unsigned short *pTable; void *pSubstTable; unsigned char *pTTGSUBEnd; if (pTTGSUB == NULL || gid == 0 || p == NULL ) return 0; // Fixed bug #516519. // Check to see if pointer p and lookup array are whthin GSUB table pTTGSUBEnd = (unsigned char *)pTTGSUB + gsubTBSize; if (((unsigned char *)p < (unsigned char *)pTTGSUB) || ((unsigned char *)p + sizeof(unsigned short) * p->lookupCount) > pTTGSUBEnd) { return 0; } /* GSUB must be good - should check for pTTFData->hasGSUB first */ /* now look through the lookup tables one by one */ for (i = 0; i < (long)p->lookupCount; i++) { offSet = MOTOROLAINT(p->pLookupList[1 + i]); /* skip lookupCount */ pTable = (unsigned short *)((unsigned char *)p->pLookupList + offSet); // Fixed bug #516519. // check to see if the offset is within the gsub table. // Add 3 to make sure we can get pTable[0],pTable[1],pTable[2] (see code right below) if (((unsigned char *)pTable < (unsigned char *)pTTGSUB) || ((unsigned char *)(pTable + 3) > pTTGSUBEnd)) { continue; } lookupType = MOTOROLAINT(pTable[0]); subTableCount = MOTOROLAINT(pTable[2]); // Fixed bug #516519 if ((unsigned char *)(pTable + subTableCount + 3) > pTTGSUBEnd) { continue; } /* Only parse Type Single(1) and Alternate(3) tables */ if (lookupType == 1 ) { for (j = 0; j < (long)subTableCount; j++) { offSet = MOTOROLAINT(pTable[3 + j]); pSubstTable = (void *) ((unsigned char *)pTable + offSet ); // Fixed bug #516519. // add 6 bytes (3*sizeof(ushort)) to make sure we can get Table[0..2] // Please see ParseTTSubstTable_1 if (((unsigned char *)pSubstTable < (unsigned char*)pTTGSUB) || (((unsigned char *)pSubstTable + 3*sizeof(unsigned short)) > pTTGSUBEnd)) { continue; } if (ParseTTSubstTable_1(pSubstTable, pTTGSUBEnd, gid, pRSubGid) ) return 1; } } else if (lookupType == 3) { for (j = 0; j < (long) subTableCount; j++) { offSet = MOTOROLAINT(pTable[3 + j]); pSubstTable = (void *) ((unsigned char *)pTable + offSet ); // Fixed bug #516519 if (((unsigned char *)pSubstTable < (unsigned char *) pTTGSUB) || (((unsigned char *)pSubstTable + 3*sizeof(unsigned short)) > pTTGSUBEnd)) { continue; } if (ParseTTSubstTable_3(pSubstTable, pTTGSUBEnd, gid, pRSubGid) ) return 1; } } /* else - ignore other Substitutions */ } /* not found */ return 0; } /* ------------------------------------------------------------------ */ /* Function to parse mort table. We recognize only a very specific implementation of 'mort' as used/understood by GDI: Name Contents (constants) 0x000100000000000100000001 Uint32 length1 Length of the whole table - 8 (constants) 0x000300010003000000000001FFFFFFFF (constants) 0x0003000100000000FFFFFFFE00080000 (constants) 0x0000000000000000 Uint16 length2 Length of the whole table - 0x38 (constants) 0x8004000000010006 BinSrchHeader binSrchHeader Binary Search Header LookupSingle entries[n] Actual lookup entries, sorted by glyph index BinSrchHeader Type Name Contents Uint16 entrySize Size in bytes of a lookup entry (set to 4) Uint16 nEntries Number of lookup entries to be searched Uint16 searchRange entrySize * (largest power of 2 less than or equal to nEntries) Uint16 entrySelector log2 of (largest power of 2 less than or equal to nEntries) Uint16 rangeShift entrySize * (nEntries - largest power of 2 less than or equal to nEntries) LookupSingle Type Name Contents GlyphID glyphid1 The glyph index for the horizontal shape GlyphID glyphid2 The glyph index for the vertical shape The last lookup entry must be a sentinel with glyphid1=glyphid2=0xFFFF. */ static unsigned short ParseTTmortForSubGid( void *pTTmort, /* mort table data */ unsigned long mortTBSize, TTmortStuff *p, /* all convenient pointers are here */ unsigned short gid, /* Given GID */ unsigned short *pRSubGid /* Reverse Substitution */ ) { unsigned short gid1, gid2; long i; if (pTTmort == NULL || gid == 0 || p == NULL) return 0; // Fixed bug #516519. // Check to see if pointer p and lookup array are whthin MORT table if (((unsigned char *)p < (unsigned char *)pTTmort) || ((unsigned char *)p + sizeof(unsigned short) * 2 * p->nEntries) > ((unsigned char *)pTTmort + mortTBSize)) { return 0; } /* mort must be good - should check for pTTFData->hasmort first */ /* Search for gid - we do linear search because 'mort' table is usually for vertical substitution and we want the original Horizontal gid for a given Vertical gid */ for (i = 0; i <= (long) p->nEntries; i++) { gid1 = MOTOROLAINT(p->pGlyphSet[i * 2]); gid2 = MOTOROLAINT(p->pGlyphSet[i * 2 + 1]); if (gid1 == 0xFFFF && gid2 == 0xFFFF) break; if (gid2 == gid) { *pRSubGid = gid1; return 1; } } /* not found */ return 0; } /* ------------------------------------------------------------------ */ unsigned short ParseTTTablesForUnicode( UFOStruct *pUFObj, unsigned short gid, unsigned short *pUV, unsigned short wSize, TTparseFlag ParseFlag ) { AFontStruct *pAFont; unsigned short retVal = 0; unsigned short gidSave; unsigned short i; *pUV = 0; if (pUFObj == NULL) return 0; pAFont = pUFObj->pAFont; if (pAFont == NULL) return 0; /* This function only get at most 1 UV for a glyph ID */ if (pUV == NULL) return 1; if (!GetTablesFromTTFont(pUFObj)) return 0; /* This function depends on good cmap format: Platform=3, Encoding=1/2/3/4/5, Format=4 */ if (pAFont->pTTcmap == NULL || pAFont->hascmap == 0 || gid == 0 ) return 0; /* if DTT_parseCmapOnly flag is set, means that /* we need unicode only. do not need char code */ if ((ParseFlag == DTT_parseCmapOnly) && (!TTcmap_IS_UNICODE(pAFont->cmapFormat))) return 0; if ((ParseFlag == DTT_parseCmapOnly) || (ParseFlag == DTT_parseAllTables)) { retVal = ParseTTcmapForUnicode(pAFont, gid, pUV, wSize); } if ((retVal == 0) && (pAFont->hasmort || pAFont->hasGSUB) && ((ParseFlag == DTT_parseMoreGSUBOnly) || (ParseFlag == DTT_parseAllTables))) { unsigned short revSubGid; unsigned short hasSub; /* Still not found, try GSUB table */ if (retVal == 0 && pAFont->hasGSUB ) { gidSave = gid; for (i = 0; i < 10; i++) // Loop Max 10 times. { hasSub = ParseTTGSUBForSubGid(pAFont->pTTGSUB, pAFont->gsubTBSize, &(pAFont->GSUBStuff), gid, &revSubGid); if (hasSub) { retVal = ParseTTcmapForUnicode(pAFont, revSubGid, pUV, wSize); if (retVal != 0) break; else gid = revSubGid; } else break; } gid = gidSave; } /* try mort table for substitution (reverse searching) */ if (retVal == 0 && pAFont->hasmort) { hasSub = ParseTTmortForSubGid(pAFont->pTTmort, pAFont->mortTBSize, &(pAFont->mortStuff), gid, &revSubGid); if (hasSub) { retVal = ParseTTcmapForUnicode(pAFont, revSubGid, pUV, wSize); } } } return retVal; }