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 | |