Home | History | Annotate | Download | only in cache
      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