1 /**************************************************************************** 2 * 3 * ftccmap.c 4 * 5 * FreeType CharMap cache (body) 6 * 7 * Copyright (C) 2000-2020 by 8 * David Turner, Robert Wilhelm, and Werner Lemberg. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 19 #include <freetype/freetype.h> 20 #include <freetype/ftcache.h> 21 #include "ftcmanag.h" 22 #include <freetype/internal/ftmemory.h> 23 #include <freetype/internal/ftobjs.h> 24 #include <freetype/internal/ftdebug.h> 25 26 #include "ftccback.h" 27 #include "ftcerror.h" 28 29 #undef FT_COMPONENT 30 #define FT_COMPONENT cache 31 32 33 /************************************************************************** 34 * 35 * Each FTC_CMapNode contains a simple array to map a range of character 36 * codes to equivalent glyph indices. 37 * 38 * For now, the implementation is very basic: Each node maps a range of 39 * 128 consecutive character codes to their corresponding glyph indices. 40 * 41 * We could do more complex things, but I don't think it is really very 42 * useful. 43 * 44 */ 45 46 47 /* number of glyph indices / character code per node */ 48 #define FTC_CMAP_INDICES_MAX 128 49 50 /* compute a query/node hash */ 51 #define FTC_CMAP_HASH( faceid, index, charcode ) \ 52 ( FTC_FACE_ID_HASH( faceid ) + 211 * (index) + \ 53 ( (charcode) / FTC_CMAP_INDICES_MAX ) ) 54 55 /* the charmap query */ 56 typedef struct FTC_CMapQueryRec_ 57 { 58 FTC_FaceID face_id; 59 FT_UInt cmap_index; 60 FT_UInt32 char_code; 61 62 } FTC_CMapQueryRec, *FTC_CMapQuery; 63 64 #define FTC_CMAP_QUERY( x ) ((FTC_CMapQuery)(x)) 65 66 /* the cmap cache node */ 67 typedef struct FTC_CMapNodeRec_ 68 { 69 FTC_NodeRec node; 70 FTC_FaceID face_id; 71 FT_UInt cmap_index; 72 FT_UInt32 first; /* first character in node */ 73 FT_UInt16 indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices */ 74 75 } FTC_CMapNodeRec, *FTC_CMapNode; 76 77 #define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) ) 78 79 /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */ 80 /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet */ 81 #define FTC_CMAP_UNKNOWN (FT_UInt16)~0 82 83 84 /*************************************************************************/ 85 /*************************************************************************/ 86 /***** *****/ 87 /***** CHARMAP NODES *****/ 88 /***** *****/ 89 /*************************************************************************/ 90 /*************************************************************************/ 91 92 93 FT_CALLBACK_DEF( void ) ftc_cmap_node_free(FTC_Node ftcnode,FTC_Cache cache)94 ftc_cmap_node_free( FTC_Node ftcnode, 95 FTC_Cache cache ) 96 { 97 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 98 FT_Memory memory = cache->memory; 99 100 101 FT_FREE( node ); 102 } 103 104 105 /* initialize a new cmap node */ 106 FT_CALLBACK_DEF( FT_Error ) ftc_cmap_node_new(FTC_Node * ftcanode,FT_Pointer ftcquery,FTC_Cache cache)107 ftc_cmap_node_new( FTC_Node *ftcanode, 108 FT_Pointer ftcquery, 109 FTC_Cache cache ) 110 { 111 FTC_CMapNode *anode = (FTC_CMapNode*)ftcanode; 112 FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; 113 FT_Error error; 114 FT_Memory memory = cache->memory; 115 FTC_CMapNode node = NULL; 116 FT_UInt nn; 117 118 119 if ( !FT_NEW( node ) ) 120 { 121 node->face_id = query->face_id; 122 node->cmap_index = query->cmap_index; 123 node->first = (query->char_code / FTC_CMAP_INDICES_MAX) * 124 FTC_CMAP_INDICES_MAX; 125 126 for ( nn = 0; nn < FTC_CMAP_INDICES_MAX; nn++ ) 127 node->indices[nn] = FTC_CMAP_UNKNOWN; 128 } 129 130 *anode = node; 131 return error; 132 } 133 134 135 /* compute the weight of a given cmap node */ 136 FT_CALLBACK_DEF( FT_Offset ) ftc_cmap_node_weight(FTC_Node cnode,FTC_Cache cache)137 ftc_cmap_node_weight( FTC_Node cnode, 138 FTC_Cache cache ) 139 { 140 FT_UNUSED( cnode ); 141 FT_UNUSED( cache ); 142 143 return sizeof ( *cnode ); 144 } 145 146 147 /* compare a cmap node to a given query */ 148 FT_CALLBACK_DEF( FT_Bool ) ftc_cmap_node_compare(FTC_Node ftcnode,FT_Pointer ftcquery,FTC_Cache cache,FT_Bool * list_changed)149 ftc_cmap_node_compare( FTC_Node ftcnode, 150 FT_Pointer ftcquery, 151 FTC_Cache cache, 152 FT_Bool* list_changed ) 153 { 154 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 155 FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; 156 FT_UNUSED( cache ); 157 158 159 if ( list_changed ) 160 *list_changed = FALSE; 161 if ( node->face_id == query->face_id && 162 node->cmap_index == query->cmap_index ) 163 { 164 FT_UInt32 offset = (FT_UInt32)( query->char_code - node->first ); 165 166 167 return FT_BOOL( offset < FTC_CMAP_INDICES_MAX ); 168 } 169 170 return 0; 171 } 172 173 174 FT_CALLBACK_DEF( FT_Bool ) ftc_cmap_node_remove_faceid(FTC_Node ftcnode,FT_Pointer ftcface_id,FTC_Cache cache,FT_Bool * list_changed)175 ftc_cmap_node_remove_faceid( FTC_Node ftcnode, 176 FT_Pointer ftcface_id, 177 FTC_Cache cache, 178 FT_Bool* list_changed ) 179 { 180 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 181 FTC_FaceID face_id = (FTC_FaceID)ftcface_id; 182 FT_UNUSED( cache ); 183 184 185 if ( list_changed ) 186 *list_changed = FALSE; 187 return FT_BOOL( node->face_id == face_id ); 188 } 189 190 191 /*************************************************************************/ 192 /*************************************************************************/ 193 /***** *****/ 194 /***** GLYPH IMAGE CACHE *****/ 195 /***** *****/ 196 /*************************************************************************/ 197 /*************************************************************************/ 198 199 200 static 201 const FTC_CacheClassRec ftc_cmap_cache_class = 202 { 203 ftc_cmap_node_new, /* FTC_Node_NewFunc node_new */ 204 ftc_cmap_node_weight, /* FTC_Node_WeightFunc node_weight */ 205 ftc_cmap_node_compare, /* FTC_Node_CompareFunc node_compare */ 206 ftc_cmap_node_remove_faceid, /* FTC_Node_CompareFunc node_remove_faceid */ 207 ftc_cmap_node_free, /* FTC_Node_FreeFunc node_free */ 208 209 sizeof ( FTC_CacheRec ), 210 ftc_cache_init, /* FTC_Cache_InitFunc cache_init */ 211 ftc_cache_done, /* FTC_Cache_DoneFunc cache_done */ 212 }; 213 214 215 /* documentation is in ftcache.h */ 216 217 FT_EXPORT_DEF( FT_Error ) FTC_CMapCache_New(FTC_Manager manager,FTC_CMapCache * acache)218 FTC_CMapCache_New( FTC_Manager manager, 219 FTC_CMapCache *acache ) 220 { 221 return FTC_Manager_RegisterCache( manager, 222 &ftc_cmap_cache_class, 223 FTC_CACHE_P( acache ) ); 224 } 225 226 227 /* documentation is in ftcache.h */ 228 229 FT_EXPORT_DEF( FT_UInt ) FTC_CMapCache_Lookup(FTC_CMapCache cmap_cache,FTC_FaceID face_id,FT_Int cmap_index,FT_UInt32 char_code)230 FTC_CMapCache_Lookup( FTC_CMapCache cmap_cache, 231 FTC_FaceID face_id, 232 FT_Int cmap_index, 233 FT_UInt32 char_code ) 234 { 235 FTC_Cache cache = FTC_CACHE( cmap_cache ); 236 FTC_CMapQueryRec query; 237 FTC_Node node; 238 FT_Error error; 239 FT_UInt gindex = 0; 240 FT_Offset hash; 241 FT_Int no_cmap_change = 0; 242 243 244 if ( cmap_index < 0 ) 245 { 246 /* Treat a negative cmap index as a special value, meaning that you */ 247 /* don't want to change the FT_Face's character map through this */ 248 /* call. This can be useful if the face requester callback already */ 249 /* sets the face's charmap to the appropriate value. */ 250 251 no_cmap_change = 1; 252 cmap_index = 0; 253 } 254 255 if ( !cache ) 256 { 257 FT_TRACE0(( "FTC_CMapCache_Lookup: bad arguments, returning 0\n" )); 258 return 0; 259 } 260 261 query.face_id = face_id; 262 query.cmap_index = (FT_UInt)cmap_index; 263 query.char_code = char_code; 264 265 hash = FTC_CMAP_HASH( face_id, (FT_UInt)cmap_index, char_code ); 266 267 #if 1 268 FTC_CACHE_LOOKUP_CMP( cache, ftc_cmap_node_compare, hash, &query, 269 node, error ); 270 #else 271 error = FTC_Cache_Lookup( cache, hash, &query, &node ); 272 #endif 273 if ( error ) 274 goto Exit; 275 276 FT_ASSERT( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first ) < 277 FTC_CMAP_INDICES_MAX ); 278 279 /* something rotten can happen with rogue clients */ 280 if ( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first >= 281 FTC_CMAP_INDICES_MAX ) ) 282 return 0; /* XXX: should return appropriate error */ 283 284 gindex = FTC_CMAP_NODE( node )->indices[char_code - 285 FTC_CMAP_NODE( node )->first]; 286 if ( gindex == FTC_CMAP_UNKNOWN ) 287 { 288 FT_Face face; 289 290 291 gindex = 0; 292 293 error = FTC_Manager_LookupFace( cache->manager, 294 FTC_CMAP_NODE( node )->face_id, 295 &face ); 296 if ( error ) 297 goto Exit; 298 299 if ( (FT_UInt)cmap_index < (FT_UInt)face->num_charmaps ) 300 { 301 FT_CharMap old, cmap = NULL; 302 303 304 old = face->charmap; 305 cmap = face->charmaps[cmap_index]; 306 307 if ( old != cmap && !no_cmap_change ) 308 FT_Set_Charmap( face, cmap ); 309 310 gindex = FT_Get_Char_Index( face, char_code ); 311 312 if ( old != cmap && !no_cmap_change ) 313 FT_Set_Charmap( face, old ); 314 } 315 316 FTC_CMAP_NODE( node )->indices[char_code - 317 FTC_CMAP_NODE( node )->first] 318 = (FT_UShort)gindex; 319 } 320 321 Exit: 322 return gindex; 323 } 324 325 326 /* END */ 327