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