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