326 lines
9.6 KiB
C
326 lines
9.6 KiB
C
/****************************************************************************
|
|
*
|
|
* ftccmap.c
|
|
*
|
|
* FreeType CharMap cache (body)
|
|
*
|
|
* Copyright (C) 2000-2022 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/freetype.h>
|
|
#include <freetype/ftcache.h>
|
|
#include "ftcmanag.h"
|
|
#include <freetype/internal/ftmemory.h>
|
|
#include <freetype/internal/ftobjs.h>
|
|
#include <freetype/internal/ftdebug.h>
|
|
|
|
#include "ftccback.h"
|
|
#include "ftcerror.h"
|
|
|
|
#undef FT_COMPONENT
|
|
#define FT_COMPONENT cache
|
|
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Each FTC_CMapNode contains a simple array to map a range of character
|
|
* codes to equivalent glyph indices.
|
|
*
|
|
* For now, the implementation is very basic: Each node maps a range of
|
|
* 128 consecutive character codes to their corresponding glyph indices.
|
|
*
|
|
* We could do more complex things, but I don't think it is really very
|
|
* useful.
|
|
*
|
|
*/
|
|
|
|
|
|
/* number of glyph indices / character code per node */
|
|
#define FTC_CMAP_INDICES_MAX 128
|
|
|
|
/* compute a query/node hash */
|
|
#define FTC_CMAP_HASH( faceid, index, charcode ) \
|
|
( FTC_FACE_ID_HASH( faceid ) + 211 * (index) + \
|
|
( (charcode) / FTC_CMAP_INDICES_MAX ) )
|
|
|
|
/* the charmap query */
|
|
typedef struct FTC_CMapQueryRec_
|
|
{
|
|
FTC_FaceID face_id;
|
|
FT_UInt cmap_index;
|
|
FT_UInt32 char_code;
|
|
|
|
} FTC_CMapQueryRec, *FTC_CMapQuery;
|
|
|
|
#define FTC_CMAP_QUERY( x ) ((FTC_CMapQuery)(x))
|
|
|
|
/* the cmap cache node */
|
|
typedef struct FTC_CMapNodeRec_
|
|
{
|
|
FTC_NodeRec node;
|
|
FTC_FaceID face_id;
|
|
FT_UInt cmap_index;
|
|
FT_UInt32 first; /* first character in node */
|
|
FT_UInt16 indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices */
|
|
|
|
} FTC_CMapNodeRec, *FTC_CMapNode;
|
|
|
|
#define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) )
|
|
|
|
/* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */
|
|
/* glyph indices haven't been queried through FT_Get_Glyph_Index() yet */
|
|
#define FTC_CMAP_UNKNOWN (FT_UInt16)~0
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** CHARMAP NODES *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
FT_CALLBACK_DEF( void )
|
|
ftc_cmap_node_free( FTC_Node ftcnode,
|
|
FTC_Cache cache )
|
|
{
|
|
FTC_CMapNode node = (FTC_CMapNode)ftcnode;
|
|
FT_Memory memory = cache->memory;
|
|
|
|
|
|
FT_FREE( node );
|
|
}
|
|
|
|
|
|
/* initialize a new cmap node */
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
ftc_cmap_node_new( FTC_Node *ftcanode,
|
|
FT_Pointer ftcquery,
|
|
FTC_Cache cache )
|
|
{
|
|
FTC_CMapNode *anode = (FTC_CMapNode*)ftcanode;
|
|
FTC_CMapQuery query = (FTC_CMapQuery)ftcquery;
|
|
FT_Error error;
|
|
FT_Memory memory = cache->memory;
|
|
FTC_CMapNode node;
|
|
FT_UInt nn;
|
|
|
|
|
|
if ( !FT_QNEW( node ) )
|
|
{
|
|
node->face_id = query->face_id;
|
|
node->cmap_index = query->cmap_index;
|
|
node->first = (query->char_code / FTC_CMAP_INDICES_MAX) *
|
|
FTC_CMAP_INDICES_MAX;
|
|
|
|
for ( nn = 0; nn < FTC_CMAP_INDICES_MAX; nn++ )
|
|
node->indices[nn] = FTC_CMAP_UNKNOWN;
|
|
}
|
|
|
|
*anode = node;
|
|
return error;
|
|
}
|
|
|
|
|
|
/* compute the weight of a given cmap node */
|
|
FT_CALLBACK_DEF( FT_Offset )
|
|
ftc_cmap_node_weight( FTC_Node cnode,
|
|
FTC_Cache cache )
|
|
{
|
|
FT_UNUSED( cnode );
|
|
FT_UNUSED( cache );
|
|
|
|
return sizeof ( *cnode );
|
|
}
|
|
|
|
|
|
/* compare a cmap node to a given query */
|
|
FT_CALLBACK_DEF( FT_Bool )
|
|
ftc_cmap_node_compare( FTC_Node ftcnode,
|
|
FT_Pointer ftcquery,
|
|
FTC_Cache cache,
|
|
FT_Bool* list_changed )
|
|
{
|
|
FTC_CMapNode node = (FTC_CMapNode)ftcnode;
|
|
FTC_CMapQuery query = (FTC_CMapQuery)ftcquery;
|
|
FT_UNUSED( cache );
|
|
|
|
|
|
if ( list_changed )
|
|
*list_changed = FALSE;
|
|
if ( node->face_id == query->face_id &&
|
|
node->cmap_index == query->cmap_index )
|
|
{
|
|
FT_UInt32 offset = (FT_UInt32)( query->char_code - node->first );
|
|
|
|
|
|
return FT_BOOL( offset < FTC_CMAP_INDICES_MAX );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Bool )
|
|
ftc_cmap_node_remove_faceid( FTC_Node ftcnode,
|
|
FT_Pointer ftcface_id,
|
|
FTC_Cache cache,
|
|
FT_Bool* list_changed )
|
|
{
|
|
FTC_CMapNode node = (FTC_CMapNode)ftcnode;
|
|
FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
|
|
FT_UNUSED( cache );
|
|
|
|
|
|
if ( list_changed )
|
|
*list_changed = FALSE;
|
|
return FT_BOOL( node->face_id == face_id );
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** GLYPH IMAGE CACHE *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
static
|
|
const FTC_CacheClassRec ftc_cmap_cache_class =
|
|
{
|
|
ftc_cmap_node_new, /* FTC_Node_NewFunc node_new */
|
|
ftc_cmap_node_weight, /* FTC_Node_WeightFunc node_weight */
|
|
ftc_cmap_node_compare, /* FTC_Node_CompareFunc node_compare */
|
|
ftc_cmap_node_remove_faceid, /* FTC_Node_CompareFunc node_remove_faceid */
|
|
ftc_cmap_node_free, /* FTC_Node_FreeFunc node_free */
|
|
|
|
sizeof ( FTC_CacheRec ),
|
|
ftc_cache_init, /* FTC_Cache_InitFunc cache_init */
|
|
ftc_cache_done, /* FTC_Cache_DoneFunc cache_done */
|
|
};
|
|
|
|
|
|
/* documentation is in ftcache.h */
|
|
|
|
FT_EXPORT_DEF( FT_Error )
|
|
FTC_CMapCache_New( FTC_Manager manager,
|
|
FTC_CMapCache *acache )
|
|
{
|
|
return FTC_Manager_RegisterCache( manager,
|
|
&ftc_cmap_cache_class,
|
|
FTC_CACHE_P( acache ) );
|
|
}
|
|
|
|
|
|
/* documentation is in ftcache.h */
|
|
|
|
FT_EXPORT_DEF( FT_UInt )
|
|
FTC_CMapCache_Lookup( FTC_CMapCache cmap_cache,
|
|
FTC_FaceID face_id,
|
|
FT_Int cmap_index,
|
|
FT_UInt32 char_code )
|
|
{
|
|
FTC_Cache cache = FTC_CACHE( cmap_cache );
|
|
FTC_CMapQueryRec query;
|
|
FTC_Node node;
|
|
FT_Error error;
|
|
FT_UInt gindex = 0;
|
|
FT_Offset hash;
|
|
FT_Int no_cmap_change = 0;
|
|
|
|
|
|
if ( cmap_index < 0 )
|
|
{
|
|
/* Treat a negative cmap index as a special value, meaning that you */
|
|
/* don't want to change the FT_Face's character map through this */
|
|
/* call. This can be useful if the face requester callback already */
|
|
/* sets the face's charmap to the appropriate value. */
|
|
|
|
no_cmap_change = 1;
|
|
cmap_index = 0;
|
|
}
|
|
|
|
if ( !cache )
|
|
{
|
|
FT_TRACE0(( "FTC_CMapCache_Lookup: bad arguments, returning 0\n" ));
|
|
return 0;
|
|
}
|
|
|
|
query.face_id = face_id;
|
|
query.cmap_index = (FT_UInt)cmap_index;
|
|
query.char_code = char_code;
|
|
|
|
hash = FTC_CMAP_HASH( face_id, (FT_UInt)cmap_index, char_code );
|
|
|
|
#if 1
|
|
FTC_CACHE_LOOKUP_CMP( cache, ftc_cmap_node_compare, hash, &query,
|
|
node, error );
|
|
#else
|
|
error = FTC_Cache_Lookup( cache, hash, &query, &node );
|
|
#endif
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
FT_ASSERT( char_code - FTC_CMAP_NODE( node )->first <
|
|
FTC_CMAP_INDICES_MAX );
|
|
|
|
/* something rotten can happen with rogue clients */
|
|
if ( char_code - FTC_CMAP_NODE( node )->first >= FTC_CMAP_INDICES_MAX )
|
|
return 0; /* XXX: should return appropriate error */
|
|
|
|
gindex = FTC_CMAP_NODE( node )->indices[char_code -
|
|
FTC_CMAP_NODE( node )->first];
|
|
if ( gindex == FTC_CMAP_UNKNOWN )
|
|
{
|
|
FT_Face face;
|
|
|
|
|
|
gindex = 0;
|
|
|
|
error = FTC_Manager_LookupFace( cache->manager,
|
|
FTC_CMAP_NODE( node )->face_id,
|
|
&face );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
if ( (FT_UInt)cmap_index < (FT_UInt)face->num_charmaps )
|
|
{
|
|
FT_CharMap old, cmap = NULL;
|
|
|
|
|
|
old = face->charmap;
|
|
cmap = face->charmaps[cmap_index];
|
|
|
|
if ( old != cmap && !no_cmap_change )
|
|
FT_Set_Charmap( face, cmap );
|
|
|
|
gindex = FT_Get_Char_Index( face, char_code );
|
|
|
|
if ( old != cmap && !no_cmap_change )
|
|
FT_Set_Charmap( face, old );
|
|
}
|
|
|
|
FTC_CMAP_NODE( node )->indices[char_code -
|
|
FTC_CMAP_NODE( node )->first]
|
|
= (FT_UShort)gindex;
|
|
}
|
|
|
|
Exit:
|
|
return gindex;
|
|
}
|
|
|
|
|
|
/* END */
|