| /***************************************************************************/ |
| /* */ |
| /* ttcmap.c */ |
| /* */ |
| /* TrueType character mapping table (cmap) support (body). */ |
| /* */ |
| /* 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 <freetype/internal/ftdebug.h> |
| #include <freetype/internal/tterrors.h> |
| |
| #include <ttload.h> |
| #include <ttcmap.h> |
| |
| /* required by the tracing mode */ |
| #undef FT_COMPONENT |
| #define FT_COMPONENT trace_ttcmap |
| |
| |
| static TT_UInt code_to_index0( TT_CMapTable* charmap, TT_ULong char_code ); |
| static TT_UInt code_to_index2( TT_CMapTable* charmap, TT_ULong char_code ); |
| static TT_UInt code_to_index4( TT_CMapTable* charmap, TT_ULong char_code ); |
| static TT_UInt code_to_index6( TT_CMapTable* charmap, TT_ULong char_code ); |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_CharMap_Load */ |
| /* */ |
| /* <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> */ |
| /* 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. */ |
| /* */ |
| LOCAL_FUNC |
| TT_Error TT_CharMap_Load( TT_Face face, |
| TT_CMapTable* cmap, |
| FT_Stream stream ) |
| { |
| TT_Error error; |
| FT_Memory memory; |
| TT_UShort num_SH, num_Seg, i; |
| |
| TT_UShort u, l; |
| |
| TT_CMap0* cmap0; |
| TT_CMap2* cmap2; |
| TT_CMap4* cmap4; |
| TT_CMap6* cmap6; |
| |
| TT_CMap2SubHeader* cmap2sub; |
| TT_CMap4Segment* segments; |
| |
| |
| if ( cmap->loaded ) |
| return TT_Err_Ok; |
| |
| memory = stream->memory; |
| |
| 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; |
| |
| cmap->get_index = code_to_index0; |
| break; |
| |
| case 2: |
| num_SH = 0; |
| cmap2 = &cmap->c.cmap2; |
| |
| /* allocate subheader keys */ |
| |
| if ( ALLOC_ARRAY( cmap2->subHeaderKeys, 256, TT_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, |
| TT_CMap2SubHeader ) || |
| 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, TT_UShort ) || |
| ACCESS_Frame( l * 2L ) ) |
| goto Fail; |
| |
| for ( i = 0; i < l; i++ ) |
| cmap2->glyphIdArray[i] = GET_UShort(); |
| |
| FORGET_Frame(); |
| |
| cmap->get_index = code_to_index2; |
| 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, |
| TT_CMap4Segment ) || |
| 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, TT_UShort ) || |
| ACCESS_Frame( l*2L ) ) |
| goto Fail; |
| |
| for ( i = 0; i < l; i++ ) |
| cmap4->glyphIdArray[i] = GET_UShort(); |
| |
| FORGET_Frame(); |
| |
| cmap->get_index = code_to_index4; |
| 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, |
| TT_Short ) || |
| ACCESS_Frame( l * 2L ) ) |
| goto Fail; |
| |
| for ( i = 0; i < l; i++ ) |
| cmap6->glyphIdArray[i] = GET_UShort(); |
| |
| FORGET_Frame(); |
| cmap->get_index = code_to_index6; |
| break; |
| |
| default: /* corrupt character mapping table */ |
| return TT_Err_Invalid_CharMap_Format; |
| |
| } |
| return TT_Err_Ok; |
| |
| Fail: |
| TT_CharMap_Free( face, cmap ); |
| return error; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_CharMap_Free */ |
| /* */ |
| /* <Description> */ |
| /* Destroys a character mapping table. */ |
| /* */ |
| /* <Input> */ |
| /* face :: A handle to the parent face object. */ |
| /* cmap :: A handle to a cmap object. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success. */ |
| /* */ |
| LOCAL_FUNC |
| TT_Error TT_CharMap_Free( TT_Face face, |
| TT_CMapTable* cmap ) |
| { |
| FT_Memory memory; |
| |
| |
| if ( !cmap ) |
| return TT_Err_Ok; |
| |
| memory = face->root.driver->memory; |
| |
| 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> */ |
| /* 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. */ |
| /* */ |
| static |
| TT_UInt code_to_index0( TT_CMapTable* cmap, |
| TT_ULong charCode ) |
| { |
| TT_CMap0* cmap0 = &cmap->c.cmap0; |
| |
| return ( charCode <= 0xFF ? cmap0->glyphIdArray[charCode] : 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. */ |
| /* */ |
| static |
| TT_UInt code_to_index2( TT_CMapTable* cmap, |
| TT_ULong charCode ) |
| { |
| TT_UInt result, index1, offset; |
| TT_UInt char_lo; |
| TT_ULong char_hi; |
| TT_CMap2SubHeader* sh2; |
| TT_CMap2* cmap2; |
| |
| cmap2 = &cmap->c.cmap2; |
| result = 0; |
| char_lo = (TT_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 wether the character code is in the charmap */ |
| if ( cmap2->subHeaderKeys[char_lo] == 0 ) |
| { |
| result = cmap2->glyphIdArray[char_lo]; |
| } |
| } |
| else |
| { |
| /* a 16-bit character code */ |
| index1 = cmap2->subHeaderKeys[ char_hi & 0xFF ]; |
| if (index1) |
| { |
| sh2 = cmap2->subHeaders + index1; |
| char_lo -= sh2->firstCode; |
| |
| if (char_lo < sh2->entryCount) |
| { |
| offset = sh2->idRangeOffset/2 + char_lo; |
| if (offset < cmap2->numGlyphId) |
| { |
| result = cmap2->glyphIdArray[offset]; |
| if (result) |
| result = (result + sh2->idDelta) & 0xFFFF; |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <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. */ |
| /* */ |
| static |
| TT_UInt code_to_index4( TT_CMapTable* cmap, |
| TT_ULong charCode ) |
| { |
| TT_UInt result, index1, segCount; |
| TT_CMap4* cmap4; |
| TT_CMap4Segment *seg4, *limit; |
| |
| cmap4 = &cmap->c.cmap4; |
| result = 0; |
| segCount = cmap4->segCountX2 / 2; |
| seg4 = cmap4->segments; |
| limit = seg4 + segCount; |
| |
| for ( ; seg4 < limit; seg4++, segCount-- ) |
| { |
| if ( charCode <= seg4->endCount ) |
| { |
| /* the ranges are sorted in increasing order, if we're out of */ |
| /* the range here, the char code isn't in the charmap, so exit */ |
| if ( charCode < seg4->startCount ) |
| break; |
| |
| /* when the idRangeOffset is 0, we can compute the glyph index */ |
| /* directly.. */ |
| if ( seg4->idRangeOffset == 0 ) |
| result = (charCode + seg4->idDelta) & 0xFFFF; |
| else |
| /* otherwise, we must use the glyphIdArray to do it */ |
| { |
| index1 = seg4->idRangeOffset/2 + (charCode - seg4->startCount) |
| - segCount; |
| |
| if ( index1 < cmap4->numGlyphId && |
| cmap4->glyphIdArray[index1] != 0 ) |
| { |
| result = (cmap4->glyphIdArray[index1] + seg4->idDelta) & 0xFFFF; |
| } |
| } |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <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. */ |
| /* */ |
| static |
| TT_UInt code_to_index6( TT_CMapTable* cmap, |
| TT_ULong charCode ) |
| { |
| TT_CMap6* cmap6; |
| TT_UInt result = 0; |
| |
| cmap6 = &cmap->c.cmap6; |
| result = 0; |
| charCode -= cmap6->firstCode; |
| |
| if ( charCode < cmap6->entryCount ) |
| result = cmap6->glyphIdArray[charCode]; |
| |
| return result; |
| } |
| |
| |
| /* END */ |