1/***************************************************************************/ 2/* */ 3/* ftccmap.c */ 4/* */ 5/* FreeType CharMap cache (body) */ 6/* */ 7/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, */ 8/* 2010, 2011 by */ 9/* David Turner, Robert Wilhelm, and Werner Lemberg. */ 10/* */ 11/* This file is part of the FreeType project, and may only be used, */ 12/* modified, and distributed under the terms of the FreeType project */ 13/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 14/* this file you indicate that you have read the license and */ 15/* understand and accept it fully. */ 16/* */ 17/***************************************************************************/ 18 19 20#include <ft2build.h> 21#include FT_FREETYPE_H 22#include FT_CACHE_H 23#include "ftcmanag.h" 24#include FT_INTERNAL_MEMORY_H 25#include FT_INTERNAL_OBJECTS_H 26#include FT_INTERNAL_DEBUG_H 27 28#include "ftccback.h" 29#include "ftcerror.h" 30 31#undef FT_COMPONENT 32#define FT_COMPONENT trace_cache 33 34 35#ifdef FT_CONFIG_OPTION_OLD_INTERNALS 36 37 typedef enum FTC_OldCMapType_ 38 { 39 FTC_OLD_CMAP_BY_INDEX = 0, 40 FTC_OLD_CMAP_BY_ENCODING = 1, 41 FTC_OLD_CMAP_BY_ID = 2 42 43 } FTC_OldCMapType; 44 45 46 typedef struct FTC_OldCMapIdRec_ 47 { 48 FT_UInt platform; 49 FT_UInt encoding; 50 51 } FTC_OldCMapIdRec, *FTC_OldCMapId; 52 53 54 typedef struct FTC_OldCMapDescRec_ 55 { 56 FTC_FaceID face_id; 57 FTC_OldCMapType type; 58 59 union 60 { 61 FT_UInt index; 62 FT_Encoding encoding; 63 FTC_OldCMapIdRec id; 64 65 } u; 66 67 } FTC_OldCMapDescRec, *FTC_OldCMapDesc; 68 69#endif /* FT_CONFIG_OLD_INTERNALS */ 70 71 72 /*************************************************************************/ 73 /* */ 74 /* Each FTC_CMapNode contains a simple array to map a range of character */ 75 /* codes to equivalent glyph indices. */ 76 /* */ 77 /* For now, the implementation is very basic: Each node maps a range of */ 78 /* 128 consecutive character codes to their corresponding glyph indices. */ 79 /* */ 80 /* We could do more complex things, but I don't think it is really very */ 81 /* useful. */ 82 /* */ 83 /*************************************************************************/ 84 85 86 /* number of glyph indices / character code per node */ 87#define FTC_CMAP_INDICES_MAX 128 88 89 /* compute a query/node hash */ 90#define FTC_CMAP_HASH( faceid, index, charcode ) \ 91 ( _FTC_FACE_ID_HASH( faceid ) + 211 * (index) + \ 92 ( (charcode) / FTC_CMAP_INDICES_MAX ) ) 93 94 /* the charmap query */ 95 typedef struct FTC_CMapQueryRec_ 96 { 97 FTC_FaceID face_id; 98 FT_UInt cmap_index; 99 FT_UInt32 char_code; 100 101 } FTC_CMapQueryRec, *FTC_CMapQuery; 102 103#define FTC_CMAP_QUERY( x ) ((FTC_CMapQuery)(x)) 104#define FTC_CMAP_QUERY_HASH( x ) \ 105 FTC_CMAP_HASH( (x)->face_id, (x)->cmap_index, (x)->char_code ) 106 107 /* the cmap cache node */ 108 typedef struct FTC_CMapNodeRec_ 109 { 110 FTC_NodeRec node; 111 FTC_FaceID face_id; 112 FT_UInt cmap_index; 113 FT_UInt32 first; /* first character in node */ 114 FT_UInt16 indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices */ 115 116 } FTC_CMapNodeRec, *FTC_CMapNode; 117 118#define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) ) 119#define FTC_CMAP_NODE_HASH( x ) \ 120 FTC_CMAP_HASH( (x)->face_id, (x)->cmap_index, (x)->first ) 121 122 /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */ 123 /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet */ 124#define FTC_CMAP_UNKNOWN ( (FT_UInt16)-1 ) 125 126 127 /*************************************************************************/ 128 /*************************************************************************/ 129 /***** *****/ 130 /***** CHARMAP NODES *****/ 131 /***** *****/ 132 /*************************************************************************/ 133 /*************************************************************************/ 134 135 136 FT_CALLBACK_DEF( void ) 137 ftc_cmap_node_free( FTC_Node ftcnode, 138 FTC_Cache cache ) 139 { 140 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 141 FT_Memory memory = cache->memory; 142 143 144 FT_FREE( node ); 145 } 146 147 148 /* initialize a new cmap node */ 149 FT_CALLBACK_DEF( FT_Error ) 150 ftc_cmap_node_new( FTC_Node *ftcanode, 151 FT_Pointer ftcquery, 152 FTC_Cache cache ) 153 { 154 FTC_CMapNode *anode = (FTC_CMapNode*)ftcanode; 155 FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; 156 FT_Error error; 157 FT_Memory memory = cache->memory; 158 FTC_CMapNode node = NULL; 159 FT_UInt nn; 160 161 162 if ( !FT_NEW( node ) ) 163 { 164 node->face_id = query->face_id; 165 node->cmap_index = query->cmap_index; 166 node->first = (query->char_code / FTC_CMAP_INDICES_MAX) * 167 FTC_CMAP_INDICES_MAX; 168 169 for ( nn = 0; nn < FTC_CMAP_INDICES_MAX; nn++ ) 170 node->indices[nn] = FTC_CMAP_UNKNOWN; 171 } 172 173 *anode = node; 174 return error; 175 } 176 177 178 /* compute the weight of a given cmap node */ 179 FT_CALLBACK_DEF( FT_Offset ) 180 ftc_cmap_node_weight( FTC_Node cnode, 181 FTC_Cache cache ) 182 { 183 FT_UNUSED( cnode ); 184 FT_UNUSED( cache ); 185 186 return sizeof ( *cnode ); 187 } 188 189 190 /* compare a cmap node to a given query */ 191 FT_CALLBACK_DEF( FT_Bool ) 192 ftc_cmap_node_compare( FTC_Node ftcnode, 193 FT_Pointer ftcquery, 194 FTC_Cache cache, 195 FT_Bool* list_changed ) 196 { 197 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 198 FTC_CMapQuery query = (FTC_CMapQuery)ftcquery; 199 FT_UNUSED( cache ); 200 201 202 if ( list_changed ) 203 *list_changed = FALSE; 204 if ( node->face_id == query->face_id && 205 node->cmap_index == query->cmap_index ) 206 { 207 FT_UInt32 offset = (FT_UInt32)( query->char_code - node->first ); 208 209 210 return FT_BOOL( offset < FTC_CMAP_INDICES_MAX ); 211 } 212 213 return 0; 214 } 215 216 217 FT_CALLBACK_DEF( FT_Bool ) 218 ftc_cmap_node_remove_faceid( FTC_Node ftcnode, 219 FT_Pointer ftcface_id, 220 FTC_Cache cache, 221 FT_Bool* list_changed ) 222 { 223 FTC_CMapNode node = (FTC_CMapNode)ftcnode; 224 FTC_FaceID face_id = (FTC_FaceID)ftcface_id; 225 FT_UNUSED( cache ); 226 227 228 if ( list_changed ) 229 *list_changed = FALSE; 230 return FT_BOOL( node->face_id == face_id ); 231 } 232 233 234 /*************************************************************************/ 235 /*************************************************************************/ 236 /***** *****/ 237 /***** GLYPH IMAGE CACHE *****/ 238 /***** *****/ 239 /*************************************************************************/ 240 /*************************************************************************/ 241 242 243 FT_CALLBACK_TABLE_DEF 244 const FTC_CacheClassRec ftc_cmap_cache_class = 245 { 246 ftc_cmap_node_new, 247 ftc_cmap_node_weight, 248 ftc_cmap_node_compare, 249 ftc_cmap_node_remove_faceid, 250 ftc_cmap_node_free, 251 252 sizeof ( FTC_CacheRec ), 253 ftc_cache_init, 254 ftc_cache_done, 255 }; 256 257 258 /* documentation is in ftcache.h */ 259 260 FT_EXPORT_DEF( FT_Error ) 261 FTC_CMapCache_New( FTC_Manager manager, 262 FTC_CMapCache *acache ) 263 { 264 return FTC_Manager_RegisterCache( manager, 265 &ftc_cmap_cache_class, 266 FTC_CACHE_P( acache ) ); 267 } 268 269 270#ifdef FT_CONFIG_OPTION_OLD_INTERNALS 271 272 /* 273 * Unfortunately, it is not possible to support binary backwards 274 * compatibility in the cmap cache. The FTC_CMapCache_Lookup signature 275 * changes were too deep, and there is no clever hackish way to detect 276 * what kind of structure we are being passed. 277 * 278 * On the other hand it seems that no production code is using this 279 * function on Unix distributions. 280 */ 281 282#endif 283 284 285 /* documentation is in ftcache.h */ 286 287 FT_EXPORT_DEF( FT_UInt ) 288 FTC_CMapCache_Lookup( FTC_CMapCache cmap_cache, 289 FTC_FaceID face_id, 290 FT_Int cmap_index, 291 FT_UInt32 char_code ) 292 { 293 FTC_Cache cache = FTC_CACHE( cmap_cache ); 294 FTC_CMapQueryRec query; 295 FTC_Node node; 296 FT_Error error; 297 FT_UInt gindex = 0; 298 FT_PtrDist hash; 299 FT_Int no_cmap_change = 0; 300 301 302 if ( cmap_index < 0 ) 303 { 304 /* Treat a negative cmap index as a special value, meaning that you */ 305 /* don't want to change the FT_Face's character map through this */ 306 /* call. This can be useful if the face requester callback already */ 307 /* sets the face's charmap to the appropriate value. */ 308 309 no_cmap_change = 1; 310 cmap_index = 0; 311 } 312 313 if ( !cache ) 314 { 315 FT_TRACE0(( "FTC_CMapCache_Lookup: bad arguments, returning 0\n" )); 316 return 0; 317 } 318 319#ifdef FT_CONFIG_OPTION_OLD_INTERNALS 320 321 /* 322 * If cmap_index is greater than the maximum number of cachable 323 * charmaps, we assume the request is from a legacy rogue client 324 * using old internal header. See include/config/ftoption.h. 325 */ 326 if ( cmap_index > FT_MAX_CHARMAP_CACHEABLE && !no_cmap_change ) 327 { 328 FTC_OldCMapDesc desc = (FTC_OldCMapDesc) face_id; 329 330 331 char_code = (FT_UInt32)cmap_index; 332 query.face_id = desc->face_id; 333 334 335 switch ( desc->type ) 336 { 337 case FTC_OLD_CMAP_BY_INDEX: 338 query.cmap_index = desc->u.index; 339 query.char_code = (FT_UInt32)cmap_index; 340 break; 341 342 case FTC_OLD_CMAP_BY_ENCODING: 343 { 344 FT_Face face; 345 346 347 error = FTC_Manager_LookupFace( cache->manager, desc->face_id, 348 &face ); 349 if ( error ) 350 return 0; 351 352 FT_Select_Charmap( face, desc->u.encoding ); 353 354 return FT_Get_Char_Index( face, char_code ); 355 } 356 357 default: 358 return 0; 359 } 360 } 361 else 362 363#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */ 364 365 { 366 query.face_id = face_id; 367 query.cmap_index = (FT_UInt)cmap_index; 368 query.char_code = char_code; 369 } 370 371 hash = FTC_CMAP_HASH( face_id, cmap_index, char_code ); 372 373#if 1 374 FTC_CACHE_LOOKUP_CMP( cache, ftc_cmap_node_compare, hash, &query, 375 node, error ); 376#else 377 error = FTC_Cache_Lookup( cache, hash, &query, &node ); 378#endif 379 if ( error ) 380 goto Exit; 381 382 FT_ASSERT( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first ) < 383 FTC_CMAP_INDICES_MAX ); 384 385 /* something rotten can happen with rogue clients */ 386 if ( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first >= 387 FTC_CMAP_INDICES_MAX ) ) 388 return 0; /* XXX: should return appropriate error */ 389 390 gindex = FTC_CMAP_NODE( node )->indices[char_code - 391 FTC_CMAP_NODE( node )->first]; 392 if ( gindex == FTC_CMAP_UNKNOWN ) 393 { 394 FT_Face face; 395 396 397 gindex = 0; 398 399 error = FTC_Manager_LookupFace( cache->manager, 400 FTC_CMAP_NODE( node )->face_id, 401 &face ); 402 if ( error ) 403 goto Exit; 404 405#ifdef FT_MAX_CHARMAP_CACHEABLE 406 /* something rotten can happen with rogue clients */ 407 if ( cmap_index > FT_MAX_CHARMAP_CACHEABLE ) 408 return 0; /* XXX: should return appropriate error */ 409#endif 410 411 if ( (FT_UInt)cmap_index < (FT_UInt)face->num_charmaps ) 412 { 413 FT_CharMap old, cmap = NULL; 414 415 416 old = face->charmap; 417 cmap = face->charmaps[cmap_index]; 418 419 if ( old != cmap && !no_cmap_change ) 420 FT_Set_Charmap( face, cmap ); 421 422 gindex = FT_Get_Char_Index( face, char_code ); 423 424 if ( old != cmap && !no_cmap_change ) 425 FT_Set_Charmap( face, old ); 426 } 427 428 FTC_CMAP_NODE( node )->indices[char_code - 429 FTC_CMAP_NODE( node )->first] 430 = (FT_UShort)gindex; 431 } 432 433 Exit: 434 return gindex; 435 } 436 437 438/* END */ 439