| 1 | /**************************************************************************** |
| 2 | * |
| 3 | * ftccmap.c |
| 4 | * |
| 5 | * FreeType CharMap cache (body) |
| 6 | * |
| 7 | * Copyright (C) 2000-2023 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 ) |
| 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 ) |
| 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_QNEW( 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 ) |
| 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 ) |
| 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 ) |
| 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 ) |
| 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 ) |
| 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( char_code - FTC_CMAP_NODE( node )->first < |
| 277 | FTC_CMAP_INDICES_MAX ); |
| 278 | |
| 279 | /* something rotten can happen with rogue clients */ |
| 280 | if ( char_code - FTC_CMAP_NODE( node )->first >= FTC_CMAP_INDICES_MAX ) |
| 281 | return 0; /* XXX: should return appropriate error */ |
| 282 | |
| 283 | gindex = FTC_CMAP_NODE( node )->indices[char_code - |
| 284 | FTC_CMAP_NODE( node )->first]; |
| 285 | if ( gindex == FTC_CMAP_UNKNOWN ) |
| 286 | { |
| 287 | FT_Face face; |
| 288 | |
| 289 | |
| 290 | gindex = 0; |
| 291 | |
| 292 | error = FTC_Manager_LookupFace( cache->manager, |
| 293 | FTC_CMAP_NODE( node )->face_id, |
| 294 | &face ); |
| 295 | if ( error ) |
| 296 | goto Exit; |
| 297 | |
| 298 | if ( cmap_index < face->num_charmaps ) |
| 299 | { |
| 300 | FT_CharMap old = face->charmap; |
| 301 | FT_CharMap cmap = face->charmaps[cmap_index]; |
| 302 | |
| 303 | |
| 304 | if ( !no_cmap_change ) |
| 305 | face->charmap = cmap; |
| 306 | |
| 307 | gindex = FT_Get_Char_Index( face, char_code ); |
| 308 | |
| 309 | if ( !no_cmap_change ) |
| 310 | face->charmap = old; |
| 311 | } |
| 312 | |
| 313 | FTC_CMAP_NODE( node )->indices[char_code - |
| 314 | FTC_CMAP_NODE( node )->first] |
| 315 | = (FT_UShort)gindex; |
| 316 | } |
| 317 | |
| 318 | Exit: |
| 319 | return gindex; |
| 320 | } |
| 321 | |
| 322 | |
| 323 | /* END */ |
| 324 | |