/******************************************************************* * * ttcmap.c 1.0 * * TrueType Character Mappings * * Copyright 1996-1999 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 "ttobjs.h" #include "ttdebug.h" #include "ttfile.h" #include "ttmemory.h" #include "ttload.h" #include "ttcmap.h" /* required by the tracing mode */ #undef TT_COMPONENT #define TT_COMPONENT trace_cmap /******************************************************************* * * Function : CharMap_Load * * Description : Loads a given charmap into memory. * * Input : cmap pointer to cmap table * * Output : Error code. * * Notes : - Assumes the the stream is already used (opened). * * - In case of error, releases all partially allocated * tables. * ******************************************************************/ LOCAL_FUNC TT_Error CharMap_Load( PCMapTable cmap, TT_Stream input ) { DEFINE_LOAD_LOCALS( input ); UShort num_SH, num_Seg, i; UShort u, l; PCMap0 cmap0; PCMap2 cmap2; PCMap4 cmap4; PCMap6 cmap6; PCMap2SubHeader cmap2sub; PCMap4Segment segments; if ( cmap->loaded ) return TT_Err_Ok; if ( FILE_Seek( cmap->offset ) ) return error; switch ( cmap->format ) { case 0: cmap0 = &cmap->c.cmap0; if ( ALLOC( cmap0->glyphIdArray, 256L ) || FILE_Read( (void*)cmap0->glyphIdArray, 256L ) ) goto Fail; break; case 2: num_SH = 0; cmap2 = &cmap->c.cmap2; /* allocate subheader keys */ if ( ALLOC_ARRAY( cmap2->subHeaderKeys, 256, UShort ) || ACCESS_Frame( 512L ) ) goto Fail; for ( i = 0; i < 256; i++ ) { u = GET_UShort() / 8; cmap2->subHeaderKeys[i] = u; if ( num_SH < u ) num_SH = u; } FORGET_Frame(); /* load subheaders */ cmap2->numGlyphId = l = ( ( cmap->length - 2L * (256 + 3) - num_SH * 8L ) & 0xffff) / 2; if ( ALLOC_ARRAY( cmap2->subHeaders, num_SH + 1, TCMap2SubHeader ) || ACCESS_Frame( ( num_SH + 1 ) * 8L ) ) goto Fail; cmap2sub = cmap2->subHeaders; for ( i = 0; i <= num_SH; i++ ) { cmap2sub->firstCode = GET_UShort(); cmap2sub->entryCount = GET_UShort(); cmap2sub->idDelta = GET_Short(); /* we apply the location offset immediately */ cmap2sub->idRangeOffset = GET_UShort() - ( num_SH - i ) * 8 - 2; cmap2sub++; } FORGET_Frame(); /* load glyph ids */ if ( ALLOC_ARRAY( cmap2->glyphIdArray, l, UShort ) || ACCESS_Frame( l * 2L ) ) goto Fail; for ( i = 0; i < l; i++ ) cmap2->glyphIdArray[i] = GET_UShort(); FORGET_Frame(); break; case 4: cmap4 = &cmap->c.cmap4; /* load header */ if ( ACCESS_Frame( 8L ) ) goto Fail; cmap4->segCountX2 = GET_UShort(); cmap4->searchRange = GET_UShort(); cmap4->entrySelector = GET_UShort(); cmap4->rangeShift = GET_UShort(); num_Seg = cmap4->segCountX2 / 2; FORGET_Frame(); /* load segments */ if ( ALLOC_ARRAY( cmap4->segments, num_Seg, TCMap4Segment ) || ACCESS_Frame( (num_Seg * 4 + 1) * 2L ) ) goto Fail; segments = cmap4->segments; for ( i = 0; i < num_Seg; i++ ) segments[i].endCount = GET_UShort(); (void)GET_UShort(); for ( i = 0; i < num_Seg; i++ ) segments[i].startCount = GET_UShort(); for ( i = 0; i < num_Seg; i++ ) segments[i].idDelta = GET_Short(); for ( i = 0; i < num_Seg; i++ ) segments[i].idRangeOffset = GET_UShort(); FORGET_Frame(); cmap4->numGlyphId = l = ( ( cmap->length - ( 16L + 8L * num_Seg ) ) & 0xffff ) / 2; /* load ids */ if ( ALLOC_ARRAY( cmap4->glyphIdArray, l , UShort ) || ACCESS_Frame( l * 2L ) ) goto Fail; for ( i = 0; i < l; i++ ) cmap4->glyphIdArray[i] = GET_UShort(); FORGET_Frame(); break; case 6: cmap6 = &cmap->c.cmap6; if ( ACCESS_Frame( 4L ) ) goto Fail; cmap6->firstCode = GET_UShort(); cmap6->entryCount = GET_UShort(); FORGET_Frame(); l = cmap6->entryCount; if ( ALLOC_ARRAY( cmap6->glyphIdArray, cmap6->entryCount, Short ) || ACCESS_Frame( l * 2L ) ) goto Fail; for ( i = 0; i < l; i++ ) cmap6->glyphIdArray[i] = GET_UShort(); FORGET_Frame(); break; default: /* corrupt character mapping table */ return TT_Err_Invalid_CharMap_Format; } return TT_Err_Ok; Fail: CharMap_Free( cmap ); return error; } /******************************************************************* * * Function : CharMap_Free * * Description : Releases a given charmap table. * * Input : cmap pointer to cmap table * * Output : Error code. * ******************************************************************/ LOCAL_FUNC TT_Error CharMap_Free( PCMapTable cmap ) { if ( !cmap ) return TT_Err_Ok; switch ( cmap->format ) { case 0: FREE( cmap->c.cmap0.glyphIdArray ); break; case 2: FREE( cmap->c.cmap2.subHeaderKeys ); FREE( cmap->c.cmap2.subHeaders ); FREE( cmap->c.cmap2.glyphIdArray ); break; case 4: FREE( cmap->c.cmap4.segments ); FREE( cmap->c.cmap4.glyphIdArray ); cmap->c.cmap4.segCountX2 = 0; break; case 6: FREE( cmap->c.cmap6.glyphIdArray ); cmap->c.cmap6.entryCount = 0; break; default: /* invalid table format, do nothing */ ; } cmap->loaded = FALSE; return TT_Err_Ok; } /******************************************************************* * * Function : CharMap_Index * * Description : Performs charcode->glyph index translation. * * Input : cmap pointer to cmap table * * Output : Glyph index, 0 in case of failure. * ******************************************************************/ static UShort code_to_index0( UShort charCode, PCMap0 cmap0 ); static UShort code_to_index2( UShort charCode, PCMap2 cmap2 ); static UShort code_to_index4( UShort charCode, PCMap4 cmap4 ); static UShort code_to_index6( UShort charCode, PCMap6 cmap6 ); LOCAL_FUNC UShort CharMap_Index( PCMapTable cmap, UShort charcode ) { switch ( cmap->format ) { case 0: return code_to_index0( charcode, &cmap->c.cmap0 ); case 2: return code_to_index2( charcode, &cmap->c.cmap2 ); case 4: return code_to_index4( charcode, &cmap->c.cmap4 ); case 6: return code_to_index6( charcode, &cmap->c.cmap6 ); default: return 0; } } /******************************************************************* * * Function : code_to_index0 * * Description : Converts the character code into a glyph index. * Uses format 0. * charCode will be masked to get a value in the range * 0x00-0xFF. * * Input : charCode the wanted character code * cmap0 a pointer to a cmap table in format 0 * * Output : Glyph index into the glyphs array. * 0 if the glyph does not exist. * ******************************************************************/ static UShort code_to_index0( UShort charCode, PCMap0 cmap0 ) { if ( charCode <= 0xFF ) return cmap0->glyphIdArray[charCode]; else 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 * * Output : Glyph index into the glyphs array. * 0 if the glyph does not exist. * ******************************************************************/ static UShort code_to_index2( UShort charCode, PCMap2 cmap2 ) { UShort index1, idx, offset; TCMap2SubHeader sh2; index1 = cmap2->subHeaderKeys[charCode <= 0xFF ? charCode : (charCode >> 8)]; if ( index1 == 0 ) { if ( charCode <= 0xFF ) return cmap2->glyphIdArray[charCode]; /* 8bit character code */ else return 0; } else /* 16bit character code */ { if ( charCode <= 0xFF ) return 0; sh2 = cmap2->subHeaders[index1]; if ( (charCode & 0xFF) < sh2.firstCode ) return 0; if ( (charCode & 0xFF) >= (sh2.firstCode + sh2.entryCount) ) return 0; offset = sh2.idRangeOffset / 2 + (charCode & 0xFF) - sh2.firstCode; if ( offset < cmap2->numGlyphId ) idx = cmap2->glyphIdArray[offset]; else return 0; if ( idx ) return (idx + sh2.idDelta) & 0xFFFF; else 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 * * Output : Glyph index into the glyphs array. * 0 if the glyph does not exist. * ******************************************************************/ static UShort code_to_index4( UShort charCode, PCMap4 cmap4 ) { UShort index1, segCount; UShort i; TCMap4Segment seg4; segCount = cmap4->segCountX2 / 2; for ( i = 0; i < segCount; i++ ) if ( charCode <= cmap4->segments[i].endCount ) break; /* Safety check - even though the last endCount should be 0xFFFF */ if ( i >= segCount ) return 0; seg4 = cmap4->segments[i]; if ( charCode < seg4.startCount ) return 0; if ( seg4.idRangeOffset == 0 ) return ( charCode + seg4.idDelta ) & 0xFFFF; else { index1 = seg4.idRangeOffset / 2 + (charCode - seg4.startCount) - (segCount - i); if ( index1 < cmap4->numGlyphId ) { if ( cmap4->glyphIdArray[index1] == 0 ) return 0; else return ( cmap4->glyphIdArray[index1] + seg4.idDelta ) & 0xFFFF; } else 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 * * Output : Glyph index into the glyphs array. * 0 if the glyph does not exist (`missing character glyph'). * ******************************************************************/ static UShort code_to_index6( UShort charCode, PCMap6 cmap6 ) { UShort firstCode; firstCode = cmap6->firstCode; if ( charCode < firstCode ) return 0; if ( charCode >= (firstCode + cmap6->entryCount) ) return 0; return cmap6->glyphIdArray[charCode - firstCode]; } /* END */