/* cmap.c -- Load and Print the content of cmap table * Copyright (C) 1996 Li-Da Lho, All right reserved * * The structure of a cmap is really complex, it depends on which format the * cmap is and loading a cmap is a kind of confusing thing. * The structure of CMAP: * A cmap begins with a general description of the table * USHORT version indicates the version of this cmap. * USHORT numEncodings is the number of encodings in the cmap table. * Then follow 'numEncodings' of encoding tables, each of which contains * the following informations for each subtable. The subtable contains the * true data to map character code to glyph index * USHORT PlatformID shows which platform the subtable is to be used * (e.g. Mac, PC) * USHORT Platfrom specofic EncodingID shows what kind of encoding scheme * this table uses (e.g. Big5, iso8859, Unicode etc.) * ULONG offset is the offset from the beginning of the whole cmap table * to the beginning of the corresponding subtable * ----------------------- --- --- * | USHORT version | ^ ^ * ----------------------- | | * | USHORT numEncodings | | | * ----------------------- | | * | Encoding Table 1 | | | * | USHORT PlatformID | | | * | USHORT EncodingID | | | * | ULONG offset 1 |------ | | * ----------------------- | | offset 1 | offset 2 * | Encoding Table 2 | | | | * | USHORT PlatformID | | | | * | USHORT EncodingID | | | | * | ULONG offset 2 |--- | v | * ----------------------- | | --- | * | Subtable 1 |<----- | * | with format 0 | | v * ----------------------- | --- * | Subtable 2 |<-- * | with format 2 | * ----------------------| * * Problem with Character Code vs. Byte Endianess. * */ #include #include #include "config.h" #include "ttf.h" #include "ttfutil.h" #ifdef MEMCHECK #include #endif /* $Id: cmap.c,v 1.1.1.1 1998/06/05 07:47:52 robert Exp $ */ #ifndef lint static char vcid[] = "$Id: cmap.c,v 1.1.1.1 1998/06/05 07:47:52 robert Exp $"; #endif /* lint */ static CMAPPtr ttfAllocCMAP(TTFontPtr font); static void ttfLoadCMAP(FILE *fp,CMAPPtr cmap,ULONG offset); static void ttfLoadEncodingTable(FILE *fp,SubTablePtr subTable,ULONG offset); static SubTablePtr ttfAllocSubTable(CMAPPtr cmap); static void ttfLoadSubTable(FILE *fp,SubTablePtr subTable,ULONG offset); static void ttfPrintSubTable(FILE* fp,SubTablePtr ptable); static void ttfFreeSubTable(SubTablePtr ptable); static CMAP0 * ttfAllocCMAP0(SubTablePtr subTable); static void ttfLoadCMAP0(FILE *fp,SubTablePtr subTable,ULONG offset); static void ttfPrintCMAP0(FILE *fp,SubTablePtr subTable); static USHORT ttfLookUpCMAP0(SubTablePtr subTable,USHORT cc); static void ttfFreeCMAP0(SubTablePtr subTable); static void ttfLoadCMAP2(FILE *fp,SubTablePtr subTable,ULONG offset); static void ttfPrintCMAP2(FILE *fp,SubTablePtr subTable); static USHORT ttfLookUpCMAP2(SubTablePtr subTable,USHORT cc); static void ttfFreeCMAP2(SubTablePtr subTable); static void ttfLoadCMAP4(FILE *fp,SubTablePtr subTable,ULONG offset); static void ttfPrintCMAP4(FILE *fp,SubTablePtr subTable); static USHORT ttfLookUpCMAP4(SubTablePtr subTable,USHORT cc); static void ttfFreeCMAP4(SubTablePtr subTable); static void ttfLoadCMAP6(FILE *fp,SubTablePtr subTable,ULONG offset); static void ttfPrintCMAP6(FILE *fp, SubTablePtr sbuTable); static USHORT ttfLookUpCMAP6(SubTablePtr subTable,USHORT cc); static void ttfFreeCMAP6(SubTablePtr subTable); void ttfInitCMAP(TTFontPtr font) { ULONG tag = 'c' | 'm' << 8 | 'a' << 16 | 'p' << 24; TableDirPtr ptd; if ((ptd = ttfLookUpTableDir(tag,font)) != NULL) { font->cmap = ttfAllocCMAP(font); ttfLoadCMAP(font->fp,font->cmap,ptd->offset); } } static CMAPPtr ttfAllocCMAP(TTFontPtr font) { CMAPPtr cmap; if ((cmap = (CMAPPtr) calloc(1,sizeof(CMAP))) == NULL) { ttfError("Out Of Memory in __FILE_ : __LINE__ \n"); return NULL; } return cmap; } static void ttfLoadCMAP(FILE *fp,CMAPPtr cmap,ULONG offset) { USHORT i,n; ULONG posEnc; /* beginning of the Encoding Table */ ULONG baseSub = offset; /* base of SubTable offset */ if (fseek(fp,offset,SEEK_SET) !=0) ttfError("Fseek Failed in ttfLOADCMAP \n"); cmap->version = ttfGetUSHORT(fp); cmap->numberOfEncodings = n = ttfGetUSHORT(fp); cmap->subTables = ttfAllocSubTable(cmap); posEnc = baseSub + sizeof(USHORT)*2; /* step over the beginning of encoding * table */ /* for each encoding scheme, load the encoding table (EncodingTable) and * the real cmap data (SubTable) */ for (i=0;isubTables+i,posEnc); ttfLoadSubTable(fp,cmap->subTables+i,baseSub); } } void ttfPrintCMAP(FILE *fp,CMAPPtr cmap) { USHORT i; fprintf(fp,"'cmap' Table - Character to Glyph Index Mapping Table\n"); fprintf(fp,"-----------------------------------------------------\n"); fprintf(fp,"\t 'cmap' version: %d\n",cmap->version); fprintf(fp,"\t number of encoding: %d\n\n",cmap->numberOfEncodings); for (i=0;inumberOfEncodings;i++) { fprintf(fp,"Subtable %3d.\t",i); ttfPrintSubTable(fp,cmap->subTables+i); fprintf(fp,"\n"); } } USHORT ttfLookUpCMAP(SubTablePtr subTable,USHORT cc) { USHORT idx,format = subTable->format; switch (format) { case 0: idx = ttfLookUpCMAP0(subTable,cc); break; case 2: idx = ttfLookUpCMAP2(subTable,cc); break; case 4: idx = ttfLookUpCMAP4(subTable,cc); break; case 6: idx = ttfLookUpCMAP6(subTable,cc); break; default: ttfError("Unrecognized CMAP format\n"); return 0; } return idx; } SubTablePtr ttfSelectCMAP(CMAPPtr cmap, USHORT PlatformID, USHORT EncodingID) { USHORT i; for (i=0;inumberOfEncodings;i++) { if ((cmap->subTables+i)->PlatformID == PlatformID && (cmap->subTables+i)->EncodingID == EncodingID) return cmap->subTables+i; } return NULL; } void ttfFreeCMAP(CMAPPtr cmap) { USHORT i; for (i=0;inumberOfEncodings;i++) ttfFreeSubTable(cmap->subTables+i); free(cmap->subTables); free(cmap); } /* actually, EncodingTable is a part of SubTable */ static void ttfLoadEncodingTable(FILE *fp,SubTablePtr subTable,ULONG offset) { if (fseek(fp,offset,SEEK_SET) !=0) ttfError("Fseek Failed in ttfLoadEncodingTable \n"); subTable->PlatformID = ttfGetUSHORT(fp); subTable->EncodingID = ttfGetUSHORT(fp); subTable->offset = ttfGetULONG(fp); } static SubTablePtr ttfAllocSubTable(CMAPPtr cmap) { SubTablePtr subTable; if ((subTable = (SubTablePtr) calloc(cmap->numberOfEncodings,sizeof(SubTable))) == NULL) { ttfError("Out Of Memory in __FILE__ : __LINE__ \n"); return NULL; } return subTable; } /* should this one be static ? */ static void ttfLoadSubTable(FILE *fp,SubTablePtr subTable,ULONG base) { ULONG pos; USHORT format; /* seek to the actuall position for this subtable * base: beginning of cmap * offset: offset field of each encoding table */ pos = base + subTable->offset; if (fseek(fp,pos,SEEK_SET) !=0) ttfError("Fseek Failed in ttfLoadSubTable\n"); subTable->format = format = ttfGetUSHORT(fp); subTable->length = ttfGetUSHORT(fp); subTable->version = ttfGetUSHORT(fp); pos += 6;/* step over format independent data,USHORT*3 */ switch(format) { case 0: ttfLoadCMAP0(fp,subTable,pos); break; case 2: ttfLoadCMAP2(fp,subTable,pos); break; case 4: ttfLoadCMAP4(fp,subTable,pos); break; case 6: ttfLoadCMAP6(fp,subTable,pos); break; default: ttfError("Unrecognized CMAP format\n"); } } static void ttfPrintSubTable(FILE* fp,SubTablePtr ptable) { USHORT format = ptable->format; /* print encoding table */ fprintf(fp, " PlatformID: %2d\n",ptable->PlatformID); fprintf(fp,"\t\t EcodingID: %2d\n",ptable->EncodingID); fprintf(fp,"\t\t 'cmap' Offset: 0x%08x\n",ptable->offset); /* print SubTable part */ fprintf(fp,"\t\t Length: %6d\n",ptable->length); fprintf(fp,"\t\t Version: %6d\n",ptable->version); switch(format) { case 0: fprintf(fp,"\t\t Format 0 - Byte encoding table\n"); ttfPrintCMAP0(fp,ptable); break; case 2: fprintf(fp,"\t\t Format 2 - High-byte mapping through table\n"); ttfPrintCMAP2(fp,ptable); break; case 4: fprintf(fp,"\t\t Format 4 - Segment mapping to delta values\n"); ttfPrintCMAP4(fp,ptable); break; case 6: fprintf(fp,"\t\t Format 6 - Trimmed table mapping\n"); ttfPrintCMAP6(fp,ptable); break; default: ttfError("Unrecognized CMAP format\n"); } } static void ttfFreeSubTable(SubTablePtr ptable) { USHORT format = ptable->format; switch(format) { case 0: ttfFreeCMAP0(ptable); break; case 2: ttfFreeCMAP2(ptable); break; case 4: ttfFreeCMAP4(ptable); break; case 6: ttfFreeCMAP6(ptable); break; } } static CMAP0 * ttfAllocCMAP0(SubTablePtr subTable) { CMAP0 * cmap0; if ((cmap0 = (CMAP0 *) calloc(1,sizeof(CMAP0))) == NULL) { ttfError("Out Of Memory in __FILE__ : __LINE__ \n"); return NULL; } return cmap0; } static void ttfLoadCMAP0(FILE *fp,SubTablePtr subTable,ULONG offset) { BYTE * array; subTable->map.cmap0 = ttfAllocCMAP0(subTable); array = subTable->map.cmap0->glyphIndexArray; if (fseek(fp,offset,SEEK_SET) != 0) ttfError("Fseek Failed in ttfLoadCMAP0 \n"); /* Attention: we get lots of bytes at once as a work around of the * usual ttfGet*, this cause byte sex trouble as will be seen * in the fellowing procedures */ if (fread(array,sizeof(BYTE),256,fp) != 256) ttfError("Error when getting glyphIndexArray \n"); } static void ttfPrintCMAP0(FILE *fp,SubTablePtr subTable) { USHORT index; int i; for (i=0;i<256;i++) { index = ttfLookUpCMAP(subTable,i); fprintf(fp,"\t\t Char %3d -> Index %4d \n",i,index); } } static USHORT ttfLookUpCMAP0(SubTablePtr subTable,USHORT cc) { return subTable->map.cmap0->glyphIndexArray[cc & 0x00ff]; } static void ttfFreeCMAP0(SubTablePtr subTable) { free(subTable->map.cmap0); } static void ttfLoadCMAP2(FILE *fp,SubTablePtr subTable,ULONG offset) { USHORT * array,i,n = 0; USHORT numGlyphId; SubHeaderPtr header; if (fseek(fp,offset,SEEK_SET) != 0) ttfError("Fseek Failed in ttfLoadCMAP2 \n"); subTable->map.cmap2 = (CMAP2 *) calloc(1,sizeof(CMAP2)); array = subTable->map.cmap2->subHeaderKeys; if (fread(array,sizeof(USHORT),256,fp) != 256) ttfError("Error when getting subHeaderKeys \n"); #ifndef WORDS_BIGENDIAN TwoByteSwap((unsigned char *) array,256*sizeof(USHORT)); #endif for (i=0;i<256;i++) { array[i] /= 8; if (n< array[i]) n = array[i]; /* find the max of subHeaderKeys */ } n += 1; /* the number of subHeaders is one plus the max of subHeaderKeys */ subTable->map.cmap2->subHeaders = header = (SubHeaderPtr) calloc(n,sizeof(SubHeader)); for (i=0;ifirstCode = ttfGetUSHORT(fp); (header+i)->entryCount = ttfGetUSHORT(fp); (header+i)->idDelta = ttfGetSHORT(fp); (header+i)->idRangeOffset = ttfGetUSHORT(fp); /* it makes things easier to let the offset starts from * the beginning of glyphIndexArray */ if ((header+i)->idRangeOffset != 0) (header+i)->idRangeOffset -= (sizeof(USHORT) + (n-i-1) * sizeof(SubHeader)) ; } /* caculate the length of glyphIndexArray, this is ugly, there should be * a better way to get this information. */ numGlyphId = subTable->length - (256 + 3) * sizeof(USHORT) - n * sizeof(SubHeader); numGlyphId /= sizeof(USHORT); subTable->map.cmap2->glyphIndexArray = (USHORT *) calloc(numGlyphId,sizeof(USHORT)); for (i=0;imap.cmap2->glyphIndexArray[i] = ttfGetUSHORT(fp); } } static void ttfPrintCMAP2(FILE *fp,SubTablePtr subTable) { USHORT i,j,numGlyphId; USHORT *array,n=0,index; SubHeaderPtr header; unsigned short cc; array = subTable->map.cmap2->subHeaderKeys; header = subTable->map.cmap2->subHeaders; for (i=0;i<256;i++) { /* find the number of subHeader */ if (n< array[i]) n = array[i]; fprintf(fp,"\t\t subHeaderKeys[%d] = %d\n",i,array[i]); } n += 1; /* the number of subHeaders is one plus the max of subHeaderKeys */ fprintf(fp,"\t\t Number of SubHeaders is %d\n",n); for (i=0;i<=n;i++) { fprintf(fp,"\t\t SubHeader[%d]\n",i); fprintf(fp,"\t\t firstCode \t 0x%04x\n",(header+i)->firstCode); fprintf(fp,"\t\t entryCount \t %d\n",(header+i)->entryCount); fprintf(fp,"\t\t idDelta \t %d\n",(header+i)->idDelta); fprintf(fp,"\t\t idRangeOffset \t 0x%04x\n\n", (header+i)->idRangeOffset); } /* caculate the length of glyphIndexArray, this is ugly, there should be * a better way to get this information. */ numGlyphId = subTable->length - (256 + 3) * sizeof(USHORT) - n * sizeof(SubHeader); numGlyphId /= sizeof(USHORT); fprintf(fp,"Number of glyphIndex: %d\n", numGlyphId); for (i=0;imap.cmap2->glyphIndexArray[i]); } i = 0; fprintf(fp,"\t\t First Byte:\t %2x\n",i); for(j=0;j<=255;j++) { #ifndef WORDS_BIGENDIAN cc = i + (j << 8); #else cc = j + (i << 8); #endif index = ttfLookUpCMAP2(subTable,cc); fprintf(fp,"\t\t Char %2x -> Index %d\n",j,index); } for (i=128;i<=255;i++) { fprintf(fp,"\t\t First Byte:\t %2x\n",i); for(j=0;j<=255;j++) { #ifndef WORDS_BIGENDIAN cc = i + (j << 8); #else cc = j + (i << 8); #endif index = ttfLookUpCMAP2(subTable,cc); fprintf(fp,"\t\t Char %2x -> Index %d\n",j,index); } } } static USHORT ttfLookUpCMAP2(SubTablePtr subTable,USHORT cc) { USHORT index,idx = 0; USHORT *array = subTable->map.cmap2->subHeaderKeys; SubHeaderPtr headers = subTable->map.cmap2->subHeaders; SHORT idDelta; USHORT firstCode, entryCount, idRangeOffset; unsigned char first,second; /* On little endian platforms "low byte" is the "first byte". * It determines that if it is necessary to use the second byte to * fully interprete the character code, for expamle, if the first byte * is obivously an ASCII character then it is not necessary to * interpret the second byte, on the other hand, if the first byte is * zero then the second byte is a ASCII char, when the first byte * is not an ASCII char nor zero, those two bytes together determine * the meanning of the character code */ #ifndef WORDS_BIGENDIAN first = cc & 0x00ff; second = cc >> 8; #else first = cc >> 8; second = cc & 0x00ff; #endif /* select which subHeader to use */ idx = array[first]; firstCode = (headers+idx)->firstCode; entryCount = (headers+idx)->entryCount; idDelta = (headers+idx)->idDelta; idRangeOffset = (headers+idx)->idRangeOffset / sizeof (USHORT); if (second >= firstCode && second < firstCode+entryCount) { /* use idRangeOffset to find where in the glyphIndexArray * the correspinding index is */ idRangeOffset += (second - firstCode); index = subTable->map.cmap2->glyphIndexArray[idRangeOffset]; if (index != 0) /* if the character is not a missing character then * add idDelta to it */ index += idDelta; } else /* The second code is out ranged then return the * missing glyph character */ index = 0; return index; } static void ttfFreeCMAP2(SubTablePtr subTable) { free(subTable->map.cmap2->subHeaders); free(subTable->map.cmap2->glyphIndexArray); free(subTable->map.cmap2); } static void ttfLoadCMAP4(FILE *fp,SubTablePtr subTable,ULONG offset) { USHORT segCount; USHORT * array,len; if (fseek(fp,offset,SEEK_SET) !=0) ttfError("Fseek Failed in ttfLoadCMAP4 \n"); subTable->map.cmap4 = (CMAP4 *) calloc(1,sizeof(CMAP4)); subTable->map.cmap4->segCountX2 = segCount = ttfGetUSHORT(fp); subTable->map.cmap4->searchRange = ttfGetUSHORT(fp); subTable->map.cmap4->entrySelector = ttfGetUSHORT(fp); subTable->map.cmap4->rangeShift = ttfGetUSHORT(fp); segCount /= 2; subTable->map.cmap4->endCount = array = (USHORT *) calloc(segCount,sizeof(USHORT)); if (fread(array,sizeof(USHORT),segCount,fp) != segCount) ttfError("Error when getting endCount\n"); #ifndef WORDS_BIGENDIAN TwoByteSwap((unsigned char *) array,segCount*sizeof(USHORT)); #endif subTable->map.cmap4->reservedPad = ttfGetUSHORT(fp); subTable->map.cmap4->startCount = array = (USHORT *) calloc(segCount,sizeof(USHORT)); if (fread(array,sizeof(USHORT),segCount,fp) != segCount) ttfError("Error when getting startCount\n"); #ifndef WORDS_BIGENDIAN TwoByteSwap((unsigned char *) array,segCount*sizeof(USHORT)); #endif subTable->map.cmap4->idDelta = array = (USHORT *) calloc(segCount,sizeof(USHORT)); if (fread(array,sizeof(USHORT),segCount,fp) != segCount) ttfError("Error when getting idDelta\n"); #ifndef WORDS_BIGENDIAN TwoByteSwap((unsigned char *) array,segCount*sizeof(USHORT)); #endif subTable->map.cmap4->idRangeOffset = array = (USHORT *) calloc(segCount,sizeof(USHORT)); if (fread(array,sizeof(USHORT),segCount,fp) != segCount) ttfError("Error when getting idRangeOffset\n"); #ifndef WORDS_BIGENDIAN TwoByteSwap((unsigned char *) array,segCount*sizeof(USHORT)); #endif /* caculate the length of glyphIndexArray, this is ugly, there should be * a better way to get this information. */ len = subTable->length - 8*sizeof(USHORT) - 4*segCount*sizeof(USHORT); len /= sizeof(USHORT); subTable->map.cmap4->glyphIndexArray = array = (USHORT *) calloc(len,sizeof(USHORT)); if (fread(array,sizeof(USHORT),len,fp) != len) ttfError("Error when getting idRangeOffset\n"); #ifndef WORDS_BIGENDIAN TwoByteSwap((unsigned char *) array,len*sizeof(USHORT)); #endif } static void ttfPrintCMAP4(FILE *fp,SubTablePtr subTable) { USHORT i; USHORT segCount,len; segCount = subTable->map.cmap4->segCountX2/2; fprintf(fp, "\t\t segCount:\t %d\n", segCount); fprintf(fp, "\t\t searchRange:\t %d\n", subTable->map.cmap4->searchRange); fprintf(fp, "\t\t entrySelector:\t %d\n", subTable->map.cmap4->entrySelector); fprintf(fp, "\t\t rangeShift:\t %d\n", subTable->map.cmap4->rangeShift); for (i=0;imap.cmap4->startCount[i]); fprintf(fp, " En = %04x,", subTable->map.cmap4->endCount[i]); /* should this filed be SHORT or USHORT ?? */ fprintf(fp, " D = %6d," , (subTable->map.cmap4->idDelta[i])); fprintf(fp, " RO = %6d," , subTable->map.cmap4->idRangeOffset[i]); /* find the glyphIndex correpsonding to this segment */ if (subTable->map.cmap4->idRangeOffset[i] != 0) { USHORT j; j = subTable->map.cmap4->segCountX2/2 - i; j = subTable->map.cmap4->idRangeOffset[i] - j*sizeof(USHORT); fprintf(fp, " gId# = %d\n", (int) (j/sizeof(USHORT))); } else fprintf(fp, " gId# = N/A\n"); } /* caculate the length of glyphIndexArray, this is ugly, there should be * a better way to get this information. */ len = subTable->length - 8*sizeof(USHORT) - 4*segCount*sizeof(USHORT); len /= sizeof(USHORT); fprintf(fp,"\t\t Number of glyphIndex %d\n",len); for (i=0;imap.cmap4->glyphIndexArray[i]); } for (i=0;imap.cmap4->startCount[i]; j<=subTable->map.cmap4->endCount[i];j++) { index = ttfLookUpCMAP4(subTable,j); fprintf(fp,"\t\tChar 0x%04x -> Index %d\n",j,index); } } } static USHORT ttfLookUpCMAP4(SubTablePtr subTable,USHORT cc) { USHORT i; USHORT index=0, segCount = subTable->map.cmap4->segCountX2/2; for (i=0;imap.cmap4->endCount[i] && cc >= subTable->map.cmap4->startCount[i]) { USHORT j; if (subTable->map.cmap4->idRangeOffset[i] != 0) { j = subTable->map.cmap4->idRangeOffset[i] - (segCount - i)*sizeof(USHORT); j = cc - subTable->map.cmap4->startCount[i] + j/2; index = subTable->map.cmap4->glyphIndexArray[j]; if (index != 0) index += subTable->map.cmap4->idDelta[i]; } else { index = cc + subTable->map.cmap4->idDelta[i]; } break; } } return index; } static void ttfFreeCMAP4(SubTablePtr subTable) { free(subTable->map.cmap4->endCount); free(subTable->map.cmap4->startCount); free(subTable->map.cmap4->idDelta); free(subTable->map.cmap4->idRangeOffset); free(subTable->map.cmap4->glyphIndexArray); free(subTable->map.cmap4); } static void ttfLoadCMAP6(FILE *fp,SubTablePtr subTable,ULONG offset) { USHORT * array,len; if (fseek(fp,offset,SEEK_SET) !=0) ttfError("Fseek Failed in ttfLoadCMAP6 \n"); subTable->map.cmap6 = (CMAP6 *) calloc(1,sizeof(CMAP6)); subTable->map.cmap6->firstCode = ttfGetUSHORT(fp); subTable->map.cmap6->entryCount = len = ttfGetUSHORT(fp); subTable->map.cmap6->glyphIndexArray = array = (USHORT *) calloc(subTable->map.cmap6->entryCount,sizeof(USHORT)); if (fread(array,sizeof(USHORT),len,fp) != len) ttfError("Error when getting idRangeOffset\n"); #ifndef WORDS_BIGENDIAN TwoByteSwap((unsigned char *) array,len*sizeof(USHORT)); #endif } static void ttfPrintCMAP6(FILE *fp, SubTablePtr subTable) { USHORT i; fprintf(fp,"\t\t First Code: 0x%04x\n",subTable->map.cmap6->firstCode); fprintf(fp,"\t\t Entry Count: %d\n",subTable->map.cmap6->entryCount); for (i=0;imap.cmap6->entryCount;i++) { fprintf(fp,"\t\t glyphIdArray[%d] = %d\n",i, subTable->map.cmap6->glyphIndexArray[i]); } } static USHORT ttfLookUpCMAP6(SubTablePtr subTable,USHORT cc) { USHORT index; index = cc - subTable->map.cmap6->firstCode; if (index < subTable->map.cmap6->entryCount) return subTable->map.cmap6->glyphIndexArray[index]; else /* index out of range, return missing glyph */ return 0; } static void ttfFreeCMAP6(SubTablePtr subTable) { free(subTable->map.cmap6->glyphIndexArray); free(subTable->map.cmap6); }