update for HEAD-2003050101
[reactos.git] / lib / freetype / src / cache / ftccmap.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftccmap.c                                                              */
4 /*                                                                         */
5 /*    FreeType CharMap cache (body)                                        */
6 /*                                                                         */
7 /*  Copyright 2000-2001, 2002 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 <ft2build.h>
20 #include FT_FREETYPE_H
21 #include FT_CACHE_H
22 #include FT_CACHE_CHARMAP_H
23 #include FT_CACHE_MANAGER_H
24 #include FT_INTERNAL_MEMORY_H
25 #include FT_INTERNAL_DEBUG_H
26 #include FT_TRUETYPE_IDS_H
27
28 #include "ftcerror.h"
29
30 #undef  FT_COMPONENT
31 #define FT_COMPONENT  trace_cache
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
51   typedef struct  FTC_CMapNodeRec_
52   {
53     FTC_NodeRec  node;
54     FT_UInt32    first;                         /* first character in node */
55     FT_UInt16    indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices  */
56
57   } FTC_CMapNodeRec, *FTC_CMapNode;
58
59
60 #define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) )
61
62
63   /* compute node hash value from cmap family and "requested" glyph index */
64 #define FTC_CMAP_HASH( cfam, cquery )                                       \
65           ( (cfam)->hash + ( (cquery)->char_code / FTC_CMAP_INDICES_MAX ) )
66
67   /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */
68   /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet   */
69 #define FTC_CMAP_UNKNOWN  ( (FT_UInt16)-1 )
70
71
72   /* the charmap query */
73   typedef struct  FTC_CMapQueryRec_
74   {
75     FTC_QueryRec  query;
76     FTC_CMapDesc  desc;
77     FT_UInt32     char_code;
78
79   } FTC_CMapQueryRec, *FTC_CMapQuery;
80
81
82 #define FTC_CMAP_QUERY( x )  ( (FTC_CMapQuery)( x ) )
83
84
85   /* the charmap family */
86   typedef struct FTC_CMapFamilyRec_
87   {
88     FTC_FamilyRec    family;
89     FT_UInt32        hash;
90     FTC_CMapDescRec  desc;
91     FT_UInt          index;
92
93   } FTC_CMapFamilyRec, *FTC_CMapFamily;
94
95
96 #define FTC_CMAP_FAMILY( x )         ( (FTC_CMapFamily)( x ) )
97 #define FTC_CMAP_FAMILY_MEMORY( x )  FTC_FAMILY( x )->memory
98
99
100   /*************************************************************************/
101   /*************************************************************************/
102   /*****                                                               *****/
103   /*****                        CHARMAP NODES                          *****/
104   /*****                                                               *****/
105   /*************************************************************************/
106   /*************************************************************************/
107
108
109   /* no need for specific finalizer; we use "ftc_node_done" directly */
110
111   /* initialize a new cmap node */
112   FT_CALLBACK_DEF( FT_Error )
113   ftc_cmap_node_init( FTC_CMapNode   cnode,
114                       FTC_CMapQuery  cquery,
115                       FTC_Cache      cache )
116   {
117     FT_UInt32  first;
118     FT_UInt    n;
119     FT_UNUSED( cache );
120
121
122     first = ( cquery->char_code / FTC_CMAP_INDICES_MAX ) *
123             FTC_CMAP_INDICES_MAX;
124
125     cnode->first = first;
126     for ( n = 0; n < FTC_CMAP_INDICES_MAX; n++ )
127       cnode->indices[n] = FTC_CMAP_UNKNOWN;
128
129     return 0;
130   }
131
132
133   /* compute the weight of a given cmap node */
134   FT_CALLBACK_DEF( FT_ULong )
135   ftc_cmap_node_weight( FTC_CMapNode  cnode )
136   {
137     FT_UNUSED( cnode );
138
139     return sizeof ( *cnode );
140   }
141
142
143   /* compare a cmap node to a given query */
144   FT_CALLBACK_DEF( FT_Bool )
145   ftc_cmap_node_compare( FTC_CMapNode   cnode,
146                          FTC_CMapQuery  cquery )
147   {
148     FT_UInt32  offset = (FT_UInt32)( cquery->char_code - cnode->first );
149
150
151     return FT_BOOL( offset < FTC_CMAP_INDICES_MAX );
152   }
153
154
155   /*************************************************************************/
156   /*************************************************************************/
157   /*****                                                               *****/
158   /*****                    CHARMAP FAMILY                             *****/
159   /*****                                                               *****/
160   /*************************************************************************/
161   /*************************************************************************/
162
163
164   FT_CALLBACK_DEF( FT_Error )
165   ftc_cmap_family_init( FTC_CMapFamily  cfam,
166                         FTC_CMapQuery   cquery,
167                         FTC_Cache       cache )
168   {
169     FTC_Manager   manager = cache->manager;
170     FTC_CMapDesc  desc = cquery->desc;
171     FT_UInt32     hash = 0;
172     FT_Error      error;
173     FT_Face       face;
174
175
176     /* setup charmap descriptor */
177     cfam->desc = *desc;
178
179     /* let's see whether the rest is correct too */
180     error = FTC_Manager_Lookup_Face( manager, desc->face_id, &face );
181     if ( !error )
182     {
183       FT_UInt      count = face->num_charmaps;
184       FT_UInt      idx   = count;
185       FT_CharMap*  cur   = face->charmaps;
186
187
188       switch ( desc->type )
189       {
190       case FTC_CMAP_BY_INDEX:
191         idx  = desc->u.index;
192         hash = idx * 33;
193         break;
194
195       case FTC_CMAP_BY_ENCODING:
196         if (desc->u.encoding == FT_ENCODING_UNICODE)
197         {
198          /* since the `interesting' table, with id's 3,10, is normally the
199           * last one, we loop backwards. This looses with type1 fonts with
200           * non-BMP characters (<.0001%), this wins with .ttf with non-BMP
201           * chars (.01% ?), and this is the same about 99.99% of the time!
202           */
203
204           FT_UInt  unicmap_idx = count;  /* some UCS-2 map, if we found it */
205
206           cur += count - 1;
207
208           for ( idx = 0; idx < count; idx++, cur-- )
209           {
210             if ( cur[0]->encoding == FT_ENCODING_UNICODE )
211             {
212               unicmap_idx = idx;  /* record we found a Unicode charmap */
213
214              /* XXX If some new encodings to represent UCS-4 are added,
215               *     they should be added here.
216               */
217               if ( ( cur[0]->platform_id == TT_PLATFORM_MICROSOFT &&
218                      cur[0]->encoding_id == TT_MS_ID_UCS_4        )          ||
219                    ( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE &&
220                      cur[0]->encoding_id == TT_APPLE_ID_UNICODE_32    )      )
221
222               /* Hurray! We found a UCS-4 charmap. We can stop the scan! */
223               {
224                 idx = count - 1 - idx;
225                 goto Found_idx_for_FTC_CMAP_BY_ENCODING;
226               }
227             }
228           }
229
230          /* We do not have any UCS-4 charmap. Sigh.
231           * Let's see if we have some other kind of Unicode charmap, though.
232           */
233           if ( unicmap_idx < count )
234             idx = count - 1 - unicmap_idx;
235         }
236         else
237         {
238           for ( idx = 0; idx < count; idx++, cur++ )
239             if ( cur[0]->encoding == desc->u.encoding )
240               break;
241         }
242
243       Found_idx_for_FTC_CMAP_BY_ENCODING:
244         hash = idx * 67;
245         break;
246
247       case FTC_CMAP_BY_ID:
248         for ( idx = 0; idx < count; idx++, cur++ )
249         {
250           if ( (FT_UInt)cur[0]->platform_id == desc->u.id.platform &&
251                (FT_UInt)cur[0]->encoding_id == desc->u.id.encoding )
252           {
253             hash = ( ( desc->u.id.platform << 8 ) | desc->u.id.encoding ) * 7;
254             break;
255           }
256         }
257         break;
258
259       default:
260         ;
261       }
262
263       if ( idx >= count )
264         goto Bad_Descriptor;
265
266       /* compute hash value, both in family and query */
267       cfam->index               = idx;
268       cfam->hash                = hash ^ FTC_FACE_ID_HASH( desc->face_id );
269       FTC_QUERY( cquery )->hash = FTC_CMAP_HASH( cfam, cquery );
270
271       error = ftc_family_init( FTC_FAMILY( cfam ),
272                                FTC_QUERY( cquery ), cache );
273     }
274
275     return error;
276
277   Bad_Descriptor:
278     FT_TRACE1(( "ftp_cmap_family_init: invalid charmap descriptor\n" ));
279     return FTC_Err_Invalid_Argument;
280   }
281
282
283   FT_CALLBACK_DEF( FT_Bool )
284   ftc_cmap_family_compare( FTC_CMapFamily  cfam,
285                            FTC_CMapQuery   cquery )
286   {
287     FT_Int  result = 0;
288
289
290     /* first, compare face id and type */
291     if ( cfam->desc.face_id != cquery->desc->face_id ||
292          cfam->desc.type    != cquery->desc->type    )
293       goto Exit;
294
295     switch ( cfam->desc.type )
296     {
297     case FTC_CMAP_BY_INDEX:
298       result = ( cfam->desc.u.index == cquery->desc->u.index );
299       break;
300
301     case FTC_CMAP_BY_ENCODING:
302       result = ( cfam->desc.u.encoding == cquery->desc->u.encoding );
303       break;
304
305     case FTC_CMAP_BY_ID:
306       result = ( cfam->desc.u.id.platform == cquery->desc->u.id.platform &&
307                  cfam->desc.u.id.encoding == cquery->desc->u.id.encoding );
308       break;
309
310     default:
311       ;
312     }
313
314     if ( result )
315     {
316       /* when found, update the 'family' and 'hash' field of the query */
317       FTC_QUERY( cquery )->family = FTC_FAMILY( cfam );
318       FTC_QUERY( cquery )->hash   = FTC_CMAP_HASH( cfam, cquery );
319     }
320
321   Exit:
322     return FT_BOOL( result );
323   }
324
325
326   /*************************************************************************/
327   /*************************************************************************/
328   /*****                                                               *****/
329   /*****                    GLYPH IMAGE CACHE                          *****/
330   /*****                                                               *****/
331   /*************************************************************************/
332   /*************************************************************************/
333
334
335   FT_CALLBACK_TABLE_DEF
336   const FTC_Cache_ClassRec  ftc_cmap_cache_class =
337   {
338     sizeof ( FTC_CacheRec ),
339     (FTC_Cache_InitFunc) ftc_cache_init,
340     (FTC_Cache_ClearFunc)ftc_cache_clear,
341     (FTC_Cache_DoneFunc) ftc_cache_done,
342
343     sizeof ( FTC_CMapFamilyRec ),
344     (FTC_Family_InitFunc)   ftc_cmap_family_init,
345     (FTC_Family_CompareFunc)ftc_cmap_family_compare,
346     (FTC_Family_DoneFunc)   ftc_family_done,
347
348     sizeof ( FTC_CMapNodeRec ),
349     (FTC_Node_InitFunc)   ftc_cmap_node_init,
350     (FTC_Node_WeightFunc) ftc_cmap_node_weight,
351     (FTC_Node_CompareFunc)ftc_cmap_node_compare,
352     (FTC_Node_DoneFunc)   ftc_node_done
353   };
354
355
356   /* documentation is in ftccmap.h */
357
358   FT_EXPORT_DEF( FT_Error )
359   FTC_CMapCache_New( FTC_Manager     manager,
360                      FTC_CMapCache  *acache )
361   {
362     return FTC_Manager_Register_Cache(
363              manager,
364              (FTC_Cache_Class)&ftc_cmap_cache_class,
365              FTC_CACHE_P( acache ) );
366   }
367
368
369 #ifdef FTC_CACHE_USE_INLINE
370
371 #define GEN_CACHE_FAMILY_COMPARE( f, q, c ) \
372           ftc_cmap_family_compare( (FTC_CMapFamily)(f), (FTC_CMapQuery)(q) )
373
374 #define GEN_CACHE_NODE_COMPARE( n, q, c ) \
375           ftc_cmap_node_compare( (FTC_CMapNode)(n), (FTC_CMapQuery)(q) )
376
377 #define GEN_CACHE_LOOKUP  ftc_cmap_cache_lookup
378
379 #include "ftccache.i"
380
381 #else  /* !FTC_CACHE_USE_INLINE */
382
383 #define ftc_cmap_cache_lookup  ftc_cache_lookup
384
385 #endif /* !FTC_CACHE_USE_INLINE */
386
387
388   /* documentation is in ftccmap.h */
389
390   FT_EXPORT_DEF( FT_UInt )
391   FTC_CMapCache_Lookup( FTC_CMapCache  cache,
392                         FTC_CMapDesc   desc,
393                         FT_UInt32      char_code )
394   {
395     FTC_CMapQueryRec  cquery;
396     FTC_CMapNode      node;
397     FT_Error          error;
398     FT_UInt           gindex = 0;
399
400
401     if ( !cache || !desc )
402     {
403       FT_ERROR(( "FTC_CMapCache_Lookup: bad arguments, returning 0!\n" ));
404       return 0;
405     }
406
407     cquery.desc      = desc;
408     cquery.char_code = char_code;
409
410     error = ftc_cmap_cache_lookup( FTC_CACHE( cache ),
411                                    FTC_QUERY( &cquery ),
412                                    (FTC_Node*)&node );
413     if ( !error )
414     {
415       FT_UInt  offset = (FT_UInt)( char_code - node->first );
416
417
418       FT_ASSERT( offset < FTC_CMAP_INDICES_MAX );
419
420       gindex = node->indices[offset];
421       if ( gindex == FTC_CMAP_UNKNOWN )
422       {
423         FT_Face  face;
424
425
426         /* we need to use FT_Get_Char_Index */
427         gindex = 0;
428
429         error = FTC_Manager_Lookup_Face( FTC_CACHE(cache)->manager,
430                                          desc->face_id,
431                                          &face );
432         if ( !error )
433         {
434           FT_CharMap  old, cmap  = NULL;
435           FT_UInt     cmap_index;
436
437
438           /* save old charmap, select new one */
439           old        = face->charmap;
440           cmap_index = FTC_CMAP_FAMILY( FTC_QUERY( &cquery )->family )->index;
441           cmap       = face->charmaps[cmap_index];
442
443           FT_Set_Charmap( face, cmap );
444
445           /* perform lookup */
446           gindex                = FT_Get_Char_Index( face, char_code );
447           node->indices[offset] = (FT_UInt16)gindex;
448
449           /* restore old charmap */
450           FT_Set_Charmap( face, old );
451         }
452       }
453     }
454
455     return gindex;
456   }
457
458
459 /* END */