You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1140 lines
38 KiB
1140 lines
38 KiB
/*
|
|
* 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;
|
|
}
|
|
|