tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ttkern.c (7183B)


      1 /****************************************************************************
      2 *
      3 * ttkern.c
      4 *
      5 *   Routines to parse and access the 'kern' table for kerning (body).
      6 *
      7 * Copyright (C) 1996-2025 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/internal/ftdebug.h>
     20 #include <freetype/internal/ftstream.h>
     21 #include <freetype/tttags.h>
     22 #include "ttkern.h"
     23 
     24 #include "sferrors.h"
     25 
     26 
     27  /**************************************************************************
     28   *
     29   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     30   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     31   * messages during execution.
     32   */
     33 #undef  FT_COMPONENT
     34 #define FT_COMPONENT  ttkern
     35 
     36 
     37 #undef  TT_KERN_INDEX
     38 #define TT_KERN_INDEX( g1, g2 )  ( ( (FT_ULong)(g1) << 16 ) | (g2) )
     39 
     40 
     41  FT_LOCAL_DEF( FT_Error )
     42  tt_face_load_kern( TT_Face    face,
     43                     FT_Stream  stream )
     44  {
     45    FT_Error   error;
     46    FT_ULong   table_size;
     47    FT_Byte*   p;
     48    FT_Byte*   p_limit;
     49    FT_UInt    nn, num_tables;
     50    FT_UInt32  avail = 0, ordered = 0;
     51 
     52 
     53    /* the kern table is optional; exit silently if it is missing */
     54    error = face->goto_table( face, TTAG_kern, stream, &table_size );
     55    if ( error )
     56      goto Exit;
     57 
     58    if ( table_size < 4 )  /* the case of a malformed table */
     59    {
     60      FT_ERROR(( "tt_face_load_kern:"
     61                 " kerning table is too small - ignored\n" ));
     62      error = FT_THROW( Table_Missing );
     63      goto Exit;
     64    }
     65 
     66    if ( FT_FRAME_EXTRACT( table_size, face->kern_table ) )
     67    {
     68      FT_ERROR(( "tt_face_load_kern:"
     69                 " could not extract kerning table\n" ));
     70      goto Exit;
     71    }
     72 
     73    face->kern_table_size = table_size;
     74 
     75    p       = face->kern_table;
     76    p_limit = p + table_size;
     77 
     78    p         += 2; /* skip version */
     79    num_tables = FT_NEXT_USHORT( p );
     80 
     81    if ( num_tables > 32 ) /* we only support up to 32 sub-tables */
     82      num_tables = 32;
     83 
     84    for ( nn = 0; nn < num_tables; nn++ )
     85    {
     86      FT_UInt    num_pairs, length, coverage, format;
     87      FT_Byte*   p_next;
     88      FT_UInt32  mask = (FT_UInt32)1UL << nn;
     89 
     90 
     91      if ( p + 6 > p_limit )
     92        break;
     93 
     94      p_next = p;
     95 
     96      p       += 2; /* skip version */
     97      length   = FT_NEXT_USHORT( p );
     98      coverage = FT_NEXT_USHORT( p );
     99 
    100      if ( length <= 6 + 8 )
    101        break;
    102 
    103      p_next += length;
    104 
    105      if ( p_next > p_limit )  /* handle broken table */
    106        p_next = p_limit;
    107 
    108      format = coverage >> 8;
    109 
    110      /* we currently only support format 0 kerning tables */
    111      if ( format != 0 )
    112        goto NextTable;
    113 
    114      /* only use horizontal kerning tables */
    115      if ( ( coverage & 3U ) != 0x0001 ||
    116           p + 8 > p_next              )
    117        goto NextTable;
    118 
    119      num_pairs = FT_NEXT_USHORT( p );
    120      p        += 6;
    121 
    122      if ( ( p_next - p ) < 6 * (int)num_pairs ) /* handle broken count */
    123        num_pairs = (FT_UInt)( ( p_next - p ) / 6 );
    124 
    125      avail |= mask;
    126 
    127      /*
    128       * Now check whether the pairs in this table are ordered.
    129       * We then can use binary search.
    130       */
    131      if ( num_pairs > 0 )
    132      {
    133        FT_ULong  count;
    134        FT_ULong  old_pair;
    135 
    136 
    137        old_pair = FT_NEXT_ULONG( p );
    138        p       += 2;
    139 
    140        for ( count = num_pairs - 1; count > 0; count-- )
    141        {
    142          FT_UInt32  cur_pair;
    143 
    144 
    145          cur_pair = FT_NEXT_ULONG( p );
    146          if ( cur_pair < old_pair )
    147            break;
    148 
    149          p += 2;
    150          old_pair = cur_pair;
    151        }
    152 
    153        if ( count == 0 )
    154          ordered |= mask;
    155      }
    156 
    157    NextTable:
    158      p = p_next;
    159    }
    160 
    161    face->num_kern_tables = nn;
    162    face->kern_avail_bits = avail;
    163    face->kern_order_bits = ordered;
    164 
    165  Exit:
    166    return error;
    167  }
    168 
    169 
    170  FT_LOCAL_DEF( void )
    171  tt_face_done_kern( TT_Face  face )
    172  {
    173    FT_Stream  stream = face->root.stream;
    174 
    175 
    176    FT_FRAME_RELEASE( face->kern_table );
    177    face->kern_table_size = 0;
    178    face->num_kern_tables = 0;
    179    face->kern_avail_bits = 0;
    180    face->kern_order_bits = 0;
    181  }
    182 
    183 
    184  FT_LOCAL_DEF( FT_Int )
    185  tt_face_get_kerning( TT_Face  face,
    186                       FT_UInt  left_glyph,
    187                       FT_UInt  right_glyph )
    188  {
    189    FT_Int   result = 0;
    190    FT_UInt  count, mask;
    191 
    192    FT_Byte*  p;
    193    FT_Byte*  p_limit;
    194 
    195 
    196    if ( !face->kern_table )
    197      return result;
    198 
    199    p       = face->kern_table;
    200    p_limit = p + face->kern_table_size;
    201 
    202    p   += 4;
    203    mask = 0x0001;
    204 
    205    for ( count = face->num_kern_tables;
    206          count > 0 && p + 6 <= p_limit;
    207          count--, mask <<= 1 )
    208    {
    209      FT_Byte* base     = p;
    210      FT_Byte* next;
    211      FT_UInt  version  = FT_NEXT_USHORT( p );
    212      FT_UInt  length   = FT_NEXT_USHORT( p );
    213      FT_UInt  coverage = FT_NEXT_USHORT( p );
    214      FT_UInt  num_pairs;
    215      FT_Int   value    = 0;
    216 
    217      FT_UNUSED( version );
    218 
    219 
    220      next = base + length;
    221 
    222      if ( next > p_limit )  /* handle broken table */
    223        next = p_limit;
    224 
    225      if ( ( face->kern_avail_bits & mask ) == 0 )
    226        goto NextTable;
    227 
    228      FT_ASSERT( p + 8 <= next ); /* tested in tt_face_load_kern */
    229 
    230      num_pairs = FT_NEXT_USHORT( p );
    231      p        += 6;
    232 
    233      if ( ( next - p ) < 6 * (int)num_pairs )  /* handle broken count  */
    234        num_pairs = (FT_UInt)( ( next - p ) / 6 );
    235 
    236      switch ( coverage >> 8 )
    237      {
    238      case 0:
    239        {
    240          FT_ULong  key0 = TT_KERN_INDEX( left_glyph, right_glyph );
    241 
    242 
    243          if ( face->kern_order_bits & mask )   /* binary search */
    244          {
    245            FT_UInt   min = 0;
    246            FT_UInt   max = num_pairs;
    247 
    248 
    249            while ( min < max )
    250            {
    251              FT_UInt   mid = ( min + max ) >> 1;
    252              FT_Byte*  q   = p + 6 * mid;
    253              FT_ULong  key;
    254 
    255 
    256              key = FT_NEXT_ULONG( q );
    257 
    258              if ( key == key0 )
    259              {
    260                value = FT_PEEK_SHORT( q );
    261                goto Found;
    262              }
    263              if ( key < key0 )
    264                min = mid + 1;
    265              else
    266                max = mid;
    267            }
    268          }
    269          else /* linear search */
    270          {
    271            FT_UInt  count2;
    272 
    273 
    274            for ( count2 = num_pairs; count2 > 0; count2-- )
    275            {
    276              FT_ULong  key = FT_NEXT_ULONG( p );
    277 
    278 
    279              if ( key == key0 )
    280              {
    281                value = FT_PEEK_SHORT( p );
    282                goto Found;
    283              }
    284              p += 2;
    285            }
    286          }
    287        }
    288        break;
    289 
    290       /*
    291        * We don't support format 2 because we haven't seen a single font
    292        * using it in real life...
    293        */
    294 
    295      default:
    296        ;
    297      }
    298 
    299      goto NextTable;
    300 
    301    Found:
    302      if ( coverage & 8 ) /* override or add */
    303        result = value;
    304      else
    305        result += value;
    306 
    307    NextTable:
    308      p = next;
    309    }
    310 
    311    return result;
    312  }
    313 
    314 #undef TT_KERN_INDEX
    315 
    316 /* END */