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