blob: f6aabfeec234143c35a1d12c4a08c9227e19c3cb [file] [log] [blame]
/***************************************************************************/
/* */
/* ttcmap.c */
/* */
/* TrueType character mapping table (cmap) support (body). */
/* */
/* Copyright 1996-2001, 2002 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
/* modified, and distributed under the terms of the FreeType project */
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
/* this file you indicate that you have read the license and */
/* understand and accept it fully. */
/* */
/***************************************************************************/
#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include "ttload.h"
#include "ttcmap.h"
#include "sferrors.h"
/*************************************************************************/
/* */
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
/* messages during execution. */
/* */
#undef FT_COMPONENT
#define FT_COMPONENT trace_ttcmap
FT_CALLBACK_DEF( FT_UInt )
code_to_index0( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_ULong )
code_to_next0( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_UInt )
code_to_index2( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_ULong )
code_to_next2( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_UInt )
code_to_index4( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_ULong )
code_to_next4( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_UInt )
code_to_index6( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_ULong )
code_to_next6( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_UInt )
code_to_index8_12( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_ULong )
code_to_next8_12( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_UInt )
code_to_index10( TT_CMapTable charmap,
FT_ULong char_code );
FT_CALLBACK_DEF( FT_ULong )
code_to_next10( TT_CMapTable charmap,
FT_ULong char_code );
/*************************************************************************/
/* */
/* <Function> */
/* tt_face_load_charmap */
/* */
/* <Description> */
/* Loads a given TrueType character map into memory. */
/* */
/* <Input> */
/* face :: A handle to the parent face object. */
/* */
/* stream :: A handle to the current stream object. */
/* */
/* <InOut> */
/* table :: A pointer to a cmap object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* The function assumes that the stream is already in use (i.e., */
/* opened). In case of error, all partially allocated tables are */
/* released. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_face_load_charmap( TT_Face face,
TT_CMapTable cmap,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory;
FT_UShort num_SH, num_Seg, i;
FT_ULong j, n;
FT_UShort u, l;
TT_CMap0 cmap0;
TT_CMap2 cmap2;
TT_CMap4 cmap4;
TT_CMap6 cmap6;
TT_CMap8_12 cmap8_12;
TT_CMap10 cmap10;
TT_CMap2SubHeader cmap2sub;
TT_CMap4Segment segments;
TT_CMapGroup groups;
if ( cmap->loaded )
return SFNT_Err_Ok;
memory = stream->memory;
if ( FT_STREAM_SEEK( cmap->offset ) )
return error;
switch ( cmap->format )
{
case 0:
cmap0 = &cmap->c.cmap0;
if ( FT_READ_USHORT( cmap0->language ) ||
FT_ALLOC( cmap0->glyphIdArray, 256L ) ||
FT_STREAM_READ( cmap0->glyphIdArray, 256L ) )
goto Fail;
cmap->get_index = code_to_index0;
cmap->get_next_char = code_to_next0;
break;
case 2:
num_SH = 0;
cmap2 = &cmap->c.cmap2;
/* allocate subheader keys */
if ( FT_NEW_ARRAY( cmap2->subHeaderKeys, 256 ) ||
FT_FRAME_ENTER( 2L + 512L ) )
goto Fail;
cmap2->language = FT_GET_USHORT();
for ( i = 0; i < 256; i++ )
{
u = (FT_UShort)( FT_GET_USHORT() / 8 );
cmap2->subHeaderKeys[i] = u;
if ( num_SH < u )
num_SH = u;
}
FT_FRAME_EXIT();
/* load subheaders */
cmap2->numGlyphId = l = (FT_UShort)(
( ( cmap->length - 2L * ( 256 + 3 ) - num_SH * 8L ) & 0xFFFFU ) / 2 );
if ( FT_NEW_ARRAY( cmap2->subHeaders, num_SH + 1 ) ||
FT_FRAME_ENTER( ( num_SH + 1 ) * 8L ) )
{
FT_FREE( cmap2->subHeaderKeys );
goto Fail;
}
cmap2sub = cmap2->subHeaders;
for ( i = 0; i <= num_SH; i++ )
{
cmap2sub->firstCode = FT_GET_USHORT();
cmap2sub->entryCount = FT_GET_USHORT();
cmap2sub->idDelta = FT_GET_SHORT();
/* we apply the location offset immediately */
cmap2sub->idRangeOffset = (FT_UShort)(
FT_GET_USHORT() - ( num_SH - i ) * 8 - 2 );
cmap2sub++;
}
FT_FRAME_EXIT();
/* load glyph IDs */
if ( FT_NEW_ARRAY( cmap2->glyphIdArray, l ) ||
FT_FRAME_ENTER( l * 2L ) )
{
FT_FREE( cmap2->subHeaders );
FT_FREE( cmap2->subHeaderKeys );
goto Fail;
}
for ( i = 0; i < l; i++ )
cmap2->glyphIdArray[i] = FT_GET_USHORT();
FT_FRAME_EXIT();
cmap->get_index = code_to_index2;
cmap->get_next_char = code_to_next2;
break;
case 4:
cmap4 = &cmap->c.cmap4;
/* load header */
if ( FT_FRAME_ENTER( 10L ) )
goto Fail;
cmap4->language = FT_GET_USHORT();
cmap4->segCountX2 = FT_GET_USHORT();
cmap4->searchRange = FT_GET_USHORT();
cmap4->entrySelector = FT_GET_USHORT();
cmap4->rangeShift = FT_GET_USHORT();
num_Seg = (FT_UShort)( cmap4->segCountX2 / 2 );
FT_FRAME_EXIT();
/* load segments */
if ( FT_NEW_ARRAY( cmap4->segments, num_Seg ) ||
FT_FRAME_ENTER( ( num_Seg * 4 + 1 ) * 2L ) )
goto Fail;
segments = cmap4->segments;
for ( i = 0; i < num_Seg; i++ )
segments[i].endCount = FT_GET_USHORT();
(void)FT_GET_USHORT();
for ( i = 0; i < num_Seg; i++ )
segments[i].startCount = FT_GET_USHORT();
for ( i = 0; i < num_Seg; i++ )
segments[i].idDelta = FT_GET_SHORT();
for ( i = 0; i < num_Seg; i++ )
segments[i].idRangeOffset = FT_GET_USHORT();
FT_FRAME_EXIT();
cmap4->numGlyphId = l = (FT_UShort)(
( ( cmap->length - ( 16L + 8L * num_Seg ) ) & 0xFFFFU ) / 2 );
/* load IDs */
if ( FT_NEW_ARRAY( cmap4->glyphIdArray, l ) ||
FT_FRAME_ENTER( l * 2L ) )
{
FT_FREE( cmap4->segments );
goto Fail;
}
for ( i = 0; i < l; i++ )
cmap4->glyphIdArray[i] = FT_GET_USHORT();
FT_FRAME_EXIT();
cmap4->last_segment = cmap4->segments;
cmap->get_index = code_to_index4;
cmap->get_next_char = code_to_next4;
break;
case 6:
cmap6 = &cmap->c.cmap6;
if ( FT_FRAME_ENTER( 6L ) )
goto Fail;
cmap6->language = FT_GET_USHORT();
cmap6->firstCode = FT_GET_USHORT();
cmap6->entryCount = FT_GET_USHORT();
FT_FRAME_EXIT();
l = cmap6->entryCount;
if ( FT_NEW_ARRAY( cmap6->glyphIdArray, l ) ||
FT_FRAME_ENTER( l * 2L ) )
goto Fail;
for ( i = 0; i < l; i++ )
cmap6->glyphIdArray[i] = FT_GET_USHORT();
FT_FRAME_EXIT();
cmap->get_index = code_to_index6;
cmap->get_next_char = code_to_next6;
break;
case 8:
case 12:
cmap8_12 = &cmap->c.cmap8_12;
if ( FT_FRAME_ENTER( 8L ) )
goto Fail;
cmap->length = FT_GET_ULONG();
cmap8_12->language = FT_GET_ULONG();
FT_FRAME_EXIT();
if ( cmap->format == 8 )
if ( FT_STREAM_SKIP( 8192L ) )
goto Fail;
if ( FT_READ_ULONG( cmap8_12->nGroups ) )
goto Fail;
n = cmap8_12->nGroups;
if ( FT_NEW_ARRAY( cmap8_12->groups, n ) ||
FT_FRAME_ENTER( n * 3 * 4L ) )
goto Fail;
groups = cmap8_12->groups;
for ( j = 0; j < n; j++ )
{
groups[j].startCharCode = FT_GET_ULONG();
groups[j].endCharCode = FT_GET_ULONG();
groups[j].startGlyphID = FT_GET_ULONG();
}
FT_FRAME_EXIT();
cmap8_12->last_group = cmap8_12->groups;
cmap->get_index = code_to_index8_12;
cmap->get_next_char = code_to_next8_12;
break;
case 10:
cmap10 = &cmap->c.cmap10;
if ( FT_FRAME_ENTER( 16L ) )
goto Fail;
cmap->length = FT_GET_ULONG();
cmap10->language = FT_GET_ULONG();
cmap10->startCharCode = FT_GET_ULONG();
cmap10->numChars = FT_GET_ULONG();
FT_FRAME_EXIT();
n = cmap10->numChars;
if ( FT_NEW_ARRAY( cmap10->glyphs, n ) ||
FT_FRAME_ENTER( n * 2L ) )
goto Fail;
for ( j = 0; j < n; j++ )
cmap10->glyphs[j] = FT_GET_USHORT();
FT_FRAME_EXIT();
cmap->get_index = code_to_index10;
cmap->get_next_char = code_to_next10;
break;
default: /* corrupt character mapping table */
return SFNT_Err_Invalid_CharMap_Format;
}
return SFNT_Err_Ok;
Fail:
tt_face_free_charmap( face, cmap );
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_face_free_charmap */
/* */
/* <Description> */
/* Destroys a character mapping table. */
/* */
/* <Input> */
/* face :: A handle to the parent face object. */
/* */
/* cmap :: A handle to a cmap object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_face_free_charmap( TT_Face face,
TT_CMapTable cmap )
{
FT_Memory memory;
if ( !cmap )
return SFNT_Err_Ok;
memory = face->root.driver->root.memory;
switch ( cmap->format )
{
case 0:
FT_FREE( cmap->c.cmap0.glyphIdArray );
break;
case 2:
FT_FREE( cmap->c.cmap2.subHeaderKeys );
FT_FREE( cmap->c.cmap2.subHeaders );
FT_FREE( cmap->c.cmap2.glyphIdArray );
break;
case 4:
FT_FREE( cmap->c.cmap4.segments );
FT_FREE( cmap->c.cmap4.glyphIdArray );
cmap->c.cmap4.segCountX2 = 0;
break;
case 6:
FT_FREE( cmap->c.cmap6.glyphIdArray );
cmap->c.cmap6.entryCount = 0;
break;
case 8:
case 12:
FT_FREE( cmap->c.cmap8_12.groups );
cmap->c.cmap8_12.nGroups = 0;
break;
case 10:
FT_FREE( cmap->c.cmap10.glyphs );
cmap->c.cmap10.numChars = 0;
break;
default:
/* invalid table format, do nothing */
;
}
cmap->loaded = FALSE;
return SFNT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_index0 */
/* */
/* <Description> */
/* Converts the character code into a glyph index. Uses format 0. */
/* `charCode' must be in the range 0x00-0xFF (otherwise 0 is */
/* returned). */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap0 :: A pointer to a cmap table in format 0. */
/* */
/* <Return> */
/* Glyph index into the glyphs array. 0 if the glyph does not exist. */
/* */
FT_CALLBACK_DEF( FT_UInt )
code_to_index0( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap0 cmap0 = &cmap->c.cmap0;
return ( charCode <= 0xFF ? cmap0->glyphIdArray[charCode] : 0 );
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_next0 */
/* */
/* <Description> */
/* Finds the next encoded character after the given one. Uses */
/* format 0. `charCode' must be in the range 0x00-0xFF (otherwise 0 */
/* is returned). */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap0 :: A pointer to a cmap table in format 0. */
/* */
/* <Return> */
/* Next char code. 0 if no higher one is encoded. */
/* */
FT_CALLBACK_DEF( FT_ULong )
code_to_next0( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap0 cmap0 = &cmap->c.cmap0;
while ( ++charCode <= 0xFF )
if ( cmap0->glyphIdArray[charCode] )
return ( charCode );
return ( 0 );
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_index2 */
/* */
/* <Description> */
/* Converts the character code into a glyph index. Uses format 2. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap2 :: A pointer to a cmap table in format 2. */
/* */
/* <Return> */
/* Glyph index into the glyphs array. 0 if the glyph does not exist. */
/* */
FT_CALLBACK_DEF( FT_UInt )
code_to_index2( TT_CMapTable cmap,
FT_ULong charCode )
{
FT_UInt result, index1, offset;
FT_UInt char_lo;
FT_ULong char_hi;
TT_CMap2SubHeader sh2;
TT_CMap2 cmap2;
cmap2 = &cmap->c.cmap2;
result = 0;
char_lo = (FT_UInt)( charCode & 0xFF );
char_hi = charCode >> 8;
if ( char_hi == 0 )
{
/* an 8-bit character code -- we use the subHeader 0 in this case */
/* to test whether the character code is in the charmap */
index1 = cmap2->subHeaderKeys[char_lo];
if ( index1 != 0 )
return 0;
}
else
{
/* a 16-bit character code */
index1 = cmap2->subHeaderKeys[char_hi & 0xFF];
if ( index1 == 0 )
return 0;
}
sh2 = cmap2->subHeaders + index1;
char_lo -= sh2->firstCode;
if ( char_lo < (FT_UInt)sh2->entryCount )
{
offset = sh2->idRangeOffset / 2 + char_lo;
if ( offset < (FT_UInt)cmap2->numGlyphId )
{
result = cmap2->glyphIdArray[offset];
if ( result )
result = ( result + sh2->idDelta ) & 0xFFFFU;
}
}
return result;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_next2 */
/* */
/* <Description> */
/* Find the next encoded character. Uses format 2. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap2 :: A pointer to a cmap table in format 2. */
/* */
/* <Return> */
/* Next encoded character. 0 if none exists. */
/* */
FT_CALLBACK_DEF( FT_ULong )
code_to_next2( TT_CMapTable cmap,
FT_ULong charCode )
{
FT_UInt index1, offset;
FT_UInt char_lo;
FT_ULong char_hi;
TT_CMap2SubHeader sh2;
TT_CMap2 cmap2;
cmap2 = &cmap->c.cmap2;
charCode++;
/*
* This is relatively simplistic -- look for a subHeader containing
* glyphs and then walk to the first glyph in that subHeader.
*/
while ( charCode < 0x10000L )
{
char_lo = (FT_UInt)( charCode & 0xFF );
char_hi = charCode >> 8;
if ( char_hi == 0 )
{
/* an 8-bit character code -- we use the subHeader 0 in this case */
/* to test whether the character code is in the charmap */
index1 = cmap2->subHeaderKeys[char_lo];
if ( index1 != 0 )
{
charCode++;
continue;
}
}
else
{
/* a 16-bit character code */
index1 = cmap2->subHeaderKeys[char_hi & 0xFF];
if ( index1 == 0 )
{
charCode = ( char_hi + 1 ) << 8;
continue;
}
}
sh2 = cmap2->subHeaders + index1;
char_lo -= sh2->firstCode;
if ( char_lo > (FT_UInt)sh2->entryCount )
{
charCode = ( char_hi + 1 ) << 8;
continue;
}
offset = sh2->idRangeOffset / 2 + char_lo;
if ( offset >= (FT_UInt)cmap2->numGlyphId ||
cmap2->glyphIdArray[offset] == 0 )
{
charCode++;
continue;
}
return charCode;
}
return 0;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_index4 */
/* */
/* <Description> */
/* Converts the character code into a glyph index. Uses format 4. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap4 :: A pointer to a cmap table in format 4. */
/* */
/* <Return> */
/* Glyph index into the glyphs array. 0 if the glyph does not exist. */
/* */
FT_CALLBACK_DEF( FT_UInt )
code_to_index4( TT_CMapTable cmap,
FT_ULong charCode )
{
FT_UInt result, index1, segCount;
TT_CMap4 cmap4;
TT_CMap4SegmentRec *seg4, *limit;
cmap4 = &cmap->c.cmap4;
result = 0;
segCount = cmap4->segCountX2 / 2;
limit = cmap4->segments + segCount;
/* first, check against the last used segment */
seg4 = cmap4->last_segment;
/* the following is equivalent to performing two tests, as in */
/* */
/* if ( charCode >= seg4->startCount && charCode <= seg4->endCount ) */
/* */
/* This is a bit strange, but it is faster, and the idea behind the */
/* cache is to significantly speed up charcode to glyph index */
/* conversion. */
if ( (FT_ULong)( charCode - seg4->startCount ) <
(FT_ULong)( seg4->endCount - seg4->startCount ) )
goto Found1;
for ( seg4 = cmap4->segments; seg4 < limit; seg4++ )
{
/* the ranges are sorted in increasing order. If we are out of */
/* the range here, the char code isn't in the charmap, so exit. */
if ( charCode > (FT_UInt)seg4->endCount )
continue;
if ( charCode >= (FT_UInt)seg4->startCount )
goto Found;
}
return 0;
Found:
cmap4->last_segment = seg4;
Found1:
/* if the idRangeOffset is 0, we can compute the glyph index */
/* directly */
if ( seg4->idRangeOffset == 0 )
result = (FT_UInt)( charCode + seg4->idDelta ) & 0xFFFFU;
else
{
/* otherwise, we must use the glyphIdArray to do it */
index1 = (FT_UInt)( seg4->idRangeOffset / 2
+ ( charCode - seg4->startCount )
+ ( seg4 - cmap4->segments )
- segCount );
if ( index1 < (FT_UInt)cmap4->numGlyphId &&
cmap4->glyphIdArray[index1] != 0 )
result = ( cmap4->glyphIdArray[index1] + seg4->idDelta ) & 0xFFFFU;
}
return result;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_next4 */
/* */
/* <Description> */
/* Find the next encoded character. Uses format 4. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap :: A pointer to a cmap table in format 4. */
/* */
/* <Return> */
/* Next encoded character. 0 if none exists. */
/* */
FT_CALLBACK_DEF( FT_ULong )
code_to_next4( TT_CMapTable cmap,
FT_ULong charCode )
{
FT_UInt index1, segCount;
TT_CMap4 cmap4;
TT_CMap4SegmentRec *seg4, *limit;
cmap4 = &cmap->c.cmap4;
segCount = cmap4->segCountX2 / 2;
limit = cmap4->segments + segCount;
charCode++;
for ( seg4 = cmap4->segments; seg4 < limit; seg4++ )
{
/* The ranges are sorted in increasing order. If we are out of */
/* the range here, the char code isn't in the charmap, so exit. */
if ( charCode <= (FT_UInt)seg4->endCount )
goto Found;
}
return 0;
Found:
if ( charCode < (FT_ULong) seg4->startCount )
charCode = seg4->startCount;
/* if the idRangeOffset is 0, all chars in the map exist */
if ( seg4->idRangeOffset == 0 )
return ( charCode );
while ( charCode <= (FT_UInt) seg4->endCount )
{
/* otherwise, we must use the glyphIdArray to do it */
index1 = (FT_UInt)( seg4->idRangeOffset / 2
+ ( charCode - seg4->startCount )
+ ( seg4 - cmap4->segments )
- segCount );
if ( index1 < (FT_UInt)cmap4->numGlyphId &&
cmap4->glyphIdArray[index1] != 0 )
return ( charCode );
charCode++;
}
return 0;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_index6 */
/* */
/* <Description> */
/* Converts the character code into a glyph index. Uses format 6. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap6 :: A pointer to a cmap table in format 6. */
/* */
/* <Return> */
/* Glyph index into the glyphs array. 0 if the glyph does not exist. */
/* */
FT_CALLBACK_DEF( FT_UInt )
code_to_index6( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap6 cmap6;
FT_UInt result = 0;
cmap6 = &cmap->c.cmap6;
charCode -= cmap6->firstCode;
if ( charCode < (FT_UInt)cmap6->entryCount )
result = cmap6->glyphIdArray[charCode];
return result;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_next6 */
/* */
/* <Description> */
/* Find the next encoded character. Uses format 6. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap :: A pointer to a cmap table in format 6. */
/* */
/* <Return> */
/* Next encoded character. 0 if none exists. */
/* */
FT_CALLBACK_DEF( FT_ULong )
code_to_next6( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap6 cmap6;
charCode++;
cmap6 = &cmap->c.cmap6;
if ( charCode < (FT_ULong) cmap6->firstCode )
charCode = cmap6->firstCode;
charCode -= cmap6->firstCode;
while ( charCode < (FT_UInt)cmap6->entryCount )
{
if ( cmap6->glyphIdArray[charCode] != 0 )
return charCode + cmap6->firstCode;
charCode++;
}
return 0;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_index8_12 */
/* */
/* <Description> */
/* Converts the (possibly 32bit) character code into a glyph index. */
/* Uses format 8 or 12. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap8_12 :: A pointer to a cmap table in format 8 or 12. */
/* */
/* <Return> */
/* Glyph index into the glyphs array. 0 if the glyph does not exist. */
/* */
FT_CALLBACK_DEF( FT_UInt )
code_to_index8_12( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap8_12 cmap8_12;
TT_CMapGroupRec *group, *limit;
cmap8_12 = &cmap->c.cmap8_12;
limit = cmap8_12->groups + cmap8_12->nGroups;
/* first, check against the last used group */
group = cmap8_12->last_group;
/* the following is equivalent to performing two tests, as in */
/* */
/* if ( charCode >= group->startCharCode && */
/* charCode <= group->endCharCode ) */
/* */
/* This is a bit strange, but it is faster, and the idea behind the */
/* cache is to significantly speed up charcode to glyph index */
/* conversion. */
if ( (FT_ULong)( charCode - group->startCharCode ) <
(FT_ULong)( group->endCharCode - group->startCharCode ) )
goto Found1;
for ( group = cmap8_12->groups; group < limit; group++ )
{
/* the ranges are sorted in increasing order. If we are out of */
/* the range here, the char code isn't in the charmap, so exit. */
if ( charCode > group->endCharCode )
continue;
if ( charCode >= group->startCharCode )
goto Found;
}
return 0;
Found:
cmap8_12->last_group = group;
Found1:
return (FT_UInt)( group->startGlyphID +
( charCode - group->startCharCode ) );
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_next8_12 */
/* */
/* <Description> */
/* Find the next encoded character. Uses format 8 or 12. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap :: A pointer to a cmap table in format 8 or 12. */
/* */
/* <Return> */
/* Next encoded character. 0 if none exists. */
/* */
FT_CALLBACK_DEF( FT_ULong )
code_to_next8_12( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap8_12 cmap8_12;
TT_CMapGroupRec *group, *limit;
charCode++;
cmap8_12 = &cmap->c.cmap8_12;
limit = cmap8_12->groups + cmap8_12->nGroups;
for ( group = cmap8_12->groups; group < limit; group++ )
{
/* the ranges are sorted in increasing order. If we are out of */
/* the range here, the char code isn't in the charmap, so exit. */
if ( charCode <= group->endCharCode )
goto Found;
}
return 0;
Found:
if ( charCode < group->startCharCode )
charCode = group->startCharCode;
return charCode;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_index10 */
/* */
/* <Description> */
/* Converts the (possibly 32bit) character code into a glyph index. */
/* Uses format 10. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap10 :: A pointer to a cmap table in format 10. */
/* */
/* <Return> */
/* Glyph index into the glyphs array. 0 if the glyph does not exist. */
/* */
FT_CALLBACK_DEF( FT_UInt )
code_to_index10( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap10 cmap10;
FT_UInt result = 0;
cmap10 = &cmap->c.cmap10;
charCode -= cmap10->startCharCode;
/* the overflow trick for comparison works here also since the number */
/* of glyphs (even if numChars is specified as ULong in the specs) in */
/* an OpenType font is limited to 64k */
if ( charCode < cmap10->numChars )
result = cmap10->glyphs[charCode];
return result;
}
/*************************************************************************/
/* */
/* <Function> */
/* code_to_next10 */
/* */
/* <Description> */
/* Find the next encoded character. Uses format 10. */
/* */
/* <Input> */
/* charCode :: The wanted character code. */
/* */
/* cmap :: A pointer to a cmap table in format 10. */
/* */
/* <Return> */
/* Next encoded character. 0 if none exists. */
/* */
FT_CALLBACK_DEF( FT_ULong )
code_to_next10( TT_CMapTable cmap,
FT_ULong charCode )
{
TT_CMap10 cmap10;
charCode++;
cmap10 = &cmap->c.cmap10;
if ( charCode < cmap10->startCharCode )
charCode = cmap10->startCharCode;
charCode -= cmap10->startCharCode;
/* the overflow trick for comparison works here also since the number */
/* of glyphs (even if numChars is specified as ULong in the specs) in */
/* an OpenType font is limited to 64k */
while ( charCode < cmap10->numChars )
{
if ( cmap10->glyphs[charCode] )
return ( charCode + cmap10->startCharCode );
charCode++;
}
return 0;
}
/* END */