tor-browser

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

ttgpos.c (24035B)


      1 /****************************************************************************
      2 *
      3 * ttgpos.c
      4 *
      5 *   Routines to parse and access the 'GPOS' table for simple kerning (body).
      6 *
      7 * Copyright (C) 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/freetype.h>
     20 #include <freetype/tttables.h>
     21 #include <freetype/tttags.h>
     22 
     23 #include <freetype/internal/ftdebug.h>
     24 #include <freetype/internal/ftstream.h>
     25 
     26 #include "ttgpos.h"
     27 
     28 
     29 #ifdef TT_CONFIG_OPTION_GPOS_KERNING
     30 
     31 
     32  /**************************************************************************
     33   *
     34   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     35   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     36   * messages during execution.
     37   */
     38 #undef  FT_COMPONENT
     39 #define FT_COMPONENT  ttgpos
     40 
     41 
     42  /*********************************/
     43  /********                 ********/
     44  /******** GPOS validation ********/
     45  /********                 ********/
     46  /*********************************/
     47 
     48  static FT_Bool
     49  tt_face_validate_coverage( FT_Byte*  table,
     50                             FT_Byte*  table_limit,
     51                             FT_UInt   max_num_coverage_indices )
     52  {
     53    FT_UInt  format;
     54 
     55    FT_Byte*  p = table;
     56    FT_Byte*  limit;
     57 
     58    FT_Long  last_id = -1;
     59 
     60 
     61    if ( table_limit < p + 4 )
     62      return FALSE;
     63 
     64    format = FT_NEXT_USHORT( p );
     65    if ( format == 1 )
     66    {
     67      FT_UInt  glyphCount = FT_NEXT_USHORT( p );
     68 
     69 
     70      if ( glyphCount > max_num_coverage_indices )
     71        return FALSE;
     72 
     73      limit = p + glyphCount * 2;
     74      if ( table_limit < limit )
     75        return FALSE;
     76 
     77      while ( p < limit )
     78      {
     79        FT_UInt  id = FT_NEXT_USHORT( p );
     80 
     81 
     82        if ( last_id >= id )
     83          return FALSE;
     84        last_id = id;
     85      }
     86    }
     87    else if ( format == 2 )
     88    {
     89      FT_UInt  rangeCount = FT_NEXT_USHORT( p );
     90 
     91 
     92      limit = p + rangeCount * 6;
     93      if ( table_limit < limit )
     94        return FALSE;
     95 
     96      while ( p < limit )
     97      {
     98        FT_UInt  startGlyphID       = FT_NEXT_USHORT( p );
     99        FT_UInt  endGlyphID         = FT_NEXT_USHORT( p );
    100        FT_UInt  startCoverageIndex = FT_NEXT_USHORT( p );
    101 
    102 
    103        if ( startGlyphID > endGlyphID )
    104          return FALSE;
    105 
    106        if ( last_id >= startGlyphID )
    107          return FALSE;
    108        last_id = endGlyphID;
    109 
    110        /* XXX: Is this modulo 65536 arithmetic? */
    111        if ( startCoverageIndex + endGlyphID - startGlyphID >=
    112               max_num_coverage_indices )
    113          return FALSE;
    114      }
    115    }
    116    else
    117      return FALSE;
    118 
    119    return TRUE;
    120  }
    121 
    122 
    123  static FT_Bool
    124  tt_face_validate_class_def( FT_Byte*  table,
    125                              FT_Byte*  table_limit,
    126                              FT_UInt   num_classes )
    127  {
    128    FT_UInt  format;
    129 
    130    FT_Byte*  p = table;
    131    FT_Byte*  limit;
    132 
    133    FT_UInt  max_class_value = 0;
    134 
    135 
    136    if ( table_limit < p + 2 )
    137      return FALSE;
    138 
    139    format = FT_NEXT_USHORT( p );
    140    if ( format == 1 )
    141    {
    142      FT_UInt  glyphCount;
    143 
    144 
    145      if ( table_limit < p + 4 )
    146        return FALSE;
    147 
    148      p += 2; /* Skip `startGlyphID`. */
    149 
    150      glyphCount = FT_NEXT_USHORT( p );
    151      limit      = p + glyphCount * 2;
    152      if ( table_limit < limit )
    153        return FALSE;
    154 
    155      while ( p < limit )
    156      {
    157        FT_UInt  class_value = FT_NEXT_USHORT( p );
    158 
    159 
    160        if ( class_value > max_class_value )
    161          max_class_value = class_value;
    162      }
    163    }
    164    else if ( format == 2 )
    165    {
    166      FT_UInt  classRangeCount;
    167      FT_Long  last_id = -1;
    168 
    169 
    170      if ( table_limit < p + 2 )
    171        return FALSE;
    172 
    173      classRangeCount = FT_NEXT_USHORT( p );
    174      limit           = p + classRangeCount * 6;
    175      if ( table_limit < limit )
    176        return FALSE;
    177 
    178      while ( p < limit )
    179      {
    180        FT_UInt  startGlyphID = FT_NEXT_USHORT( p );
    181        FT_UInt  endGlyphID   = FT_NEXT_USHORT( p );
    182        FT_UInt  class_value  = FT_NEXT_USHORT( p );
    183 
    184 
    185        if ( startGlyphID > endGlyphID )
    186          return FALSE;
    187 
    188        if ( last_id >= startGlyphID )
    189          return FALSE;
    190        last_id = endGlyphID;
    191 
    192        if ( class_value > max_class_value )
    193          max_class_value = class_value;
    194      }
    195    }
    196    else
    197      return FALSE;
    198 
    199    if ( max_class_value + 1 != num_classes )
    200      return FALSE;
    201 
    202    return TRUE;
    203  }
    204 
    205 
    206  static FT_Bool
    207  tt_face_validate_feature( FT_Byte*  table,
    208                            FT_Byte*  table_limit,
    209                            FT_UInt   use_lookup_table_size,
    210                            FT_Byte*  use_lookup_table )
    211  {
    212    FT_UInt  lookupIndexCount;
    213 
    214    FT_Byte*  p = table;
    215    FT_Byte*  limit;
    216 
    217 
    218    if ( table_limit < p + 4 )
    219      return FALSE;
    220 
    221    p += 2; /* Skip `featureParamsOffset`. */
    222 
    223    lookupIndexCount = FT_NEXT_USHORT( p );
    224    limit            = p + lookupIndexCount * 2;
    225    if ( table_limit < limit )
    226      return FALSE;
    227 
    228    while ( p < limit )
    229    {
    230      FT_UInt  lookup_index = FT_NEXT_USHORT( p );
    231 
    232 
    233      if ( lookup_index >= use_lookup_table_size )
    234        return FALSE;
    235 
    236      use_lookup_table[lookup_index] = TRUE;
    237    }
    238 
    239    return TRUE;
    240  }
    241 
    242 
    243  static FT_Bool
    244  tt_face_validate_feature_table( FT_Byte*  table,
    245                                  FT_Byte*  table_limit,
    246                                  FT_UInt   use_lookup_table_size,
    247                                  FT_Byte*  use_lookup_table )
    248  {
    249    FT_UInt  featureCount;
    250 
    251    FT_Byte*  p = table;
    252    FT_Byte*  limit;
    253 
    254 
    255    if ( table_limit < p + 2 )
    256      return FALSE;
    257 
    258    featureCount = FT_NEXT_USHORT( p );
    259    limit        = p + featureCount * 6;
    260    if ( table_limit < limit )
    261      return FALSE;
    262 
    263    /* We completely ignore GPOS script information      */
    264    /* and collect lookup tables of all 'kern' features. */
    265    while ( p < limit )
    266    {
    267      FT_ULong  featureTag    = FT_NEXT_ULONG( p );
    268      FT_UInt   featureOffset = FT_NEXT_USHORT( p );
    269 
    270 
    271      if ( featureTag == TTAG_kern )
    272      {
    273        if ( !tt_face_validate_feature( table + featureOffset,
    274                                        table_limit,
    275                                        use_lookup_table_size,
    276                                        use_lookup_table ) )
    277          return FALSE;
    278      }
    279    }
    280 
    281    return TRUE;
    282  }
    283 
    284 
    285  static FT_Bool
    286  tt_face_validate_pair_set( FT_Byte*  table,
    287                             FT_Byte*  table_limit )
    288  {
    289    FT_UInt  pairValueCount;
    290 
    291    FT_Byte*  p = table;
    292    FT_Byte*  limit;
    293 
    294    FT_Long  last_id = -1;
    295 
    296 
    297    if ( table_limit < p + 2 )
    298      return FALSE;
    299 
    300    /* For our purposes, the first value record only contains X advances */
    301    /* while the second one is empty; a `PairValue` record has thus a    */
    302    /* size of four bytes.                                               */
    303    pairValueCount = FT_NEXT_USHORT( p );
    304    limit          = p + pairValueCount * 4;
    305    if ( table_limit < limit )
    306      return FALSE;
    307 
    308    /* We validate the order of `secondGlyph` so that binary search works. */
    309    while ( p < limit )
    310    {
    311      FT_UInt  id = FT_NEXT_USHORT( p );
    312 
    313 
    314      if ( last_id >= id )
    315        return FALSE;
    316 
    317      last_id = id;
    318 
    319      p += 2; /* Skip `valueRecord1`. */
    320    }
    321 
    322    return TRUE;
    323  }
    324 
    325 
    326  static FT_Bool
    327  tt_face_validate_pair_pos1( FT_Byte*  table,
    328                              FT_Byte*  table_limit,
    329                              FT_Bool*  is_fitting )
    330  {
    331    FT_Byte*  coverage;
    332    FT_UInt   valueFormat1;
    333    FT_UInt   valueFormat2;
    334 
    335    /* Subtable format is already checked. */
    336    FT_Byte*  p = table + 2;
    337    FT_Byte*  limit;
    338 
    339 
    340    /* The six bytes for the coverage table offset */
    341    /* and the value formats are already checked.  */
    342    coverage = table + FT_NEXT_USHORT( p );
    343 
    344    /* For the limited purpose of accessing the simplest type of kerning */
    345    /* (similar to what FreeType's 'kern' table handling provides) we    */
    346    /* only consider tables that contains X advance values for the first */
    347    /* glyph and no data for the second glyph.                           */
    348    valueFormat1 = FT_NEXT_USHORT( p );
    349    valueFormat2 = FT_NEXT_USHORT( p );
    350    if ( valueFormat1 == 0x4 && valueFormat2 == 0 )
    351    {
    352      FT_UInt  pairSetCount;
    353 
    354 
    355      if ( table_limit < p + 2 )
    356        return FALSE;
    357 
    358      pairSetCount = FT_NEXT_USHORT( p );
    359      limit        = p + pairSetCount * 2;
    360      if ( table_limit < limit )
    361        return FALSE;
    362 
    363      if ( !tt_face_validate_coverage( coverage,
    364                                       table_limit,
    365                                       pairSetCount ) )
    366        return FALSE;
    367 
    368      while ( p < limit )
    369      {
    370        FT_Byte*  pair_set = table + FT_NEXT_USHORT( p );
    371 
    372 
    373        if ( !tt_face_validate_pair_set( pair_set, table_limit ) )
    374          return FALSE;
    375      }
    376 
    377      *is_fitting = TRUE;
    378    }
    379 
    380    return TRUE;
    381  }
    382 
    383 
    384  static FT_Bool
    385  tt_face_validate_pair_pos2( FT_Byte*  table,
    386                              FT_Byte*  table_limit,
    387                              FT_Bool*  is_fitting )
    388  {
    389    FT_Byte*  coverage;
    390    FT_UInt   valueFormat1;
    391    FT_UInt   valueFormat2;
    392 
    393    /* Subtable format is already checked. */
    394    FT_Byte*  p = table + 2;
    395    FT_Byte*  limit;
    396 
    397 
    398    /* The six bytes for the coverage table offset */
    399    /* and the value formats are already checked.  */
    400    coverage = table + FT_NEXT_USHORT( p );
    401 
    402    valueFormat1 = FT_NEXT_USHORT( p );
    403    valueFormat2 = FT_NEXT_USHORT( p );
    404    if ( valueFormat1 == 0x4 && valueFormat2 == 0 )
    405    {
    406      FT_Byte*  class_def1;
    407      FT_Byte*  class_def2;
    408      FT_UInt   class1Count;
    409      FT_UInt   class2Count;
    410 
    411 
    412      /* The number of coverage indices is not relevant here. */
    413      if ( !tt_face_validate_coverage( coverage, table_limit, FT_UINT_MAX ) )
    414        return FALSE;
    415 
    416      if ( table_limit < p + 8 )
    417        return FALSE;
    418 
    419      class_def1  = table + FT_NEXT_USHORT( p );
    420      class_def2  = table + FT_NEXT_USHORT( p );
    421      class1Count = FT_NEXT_USHORT( p );
    422      class2Count = FT_NEXT_USHORT( p );
    423 
    424      if ( !tt_face_validate_class_def( class_def1,
    425                                        table_limit,
    426                                        class1Count ) )
    427        return FALSE;
    428      if ( !tt_face_validate_class_def( class_def2,
    429                                        table_limit,
    430                                        class2Count ) )
    431        return FALSE;
    432 
    433      /* For our purposes, the first value record only contains */
    434      /* X advances while the second one is empty.              */
    435      limit = p + class1Count * class2Count * 2;
    436      if ( table_limit < limit )
    437        return FALSE;
    438 
    439      *is_fitting = TRUE;
    440    }
    441 
    442    return TRUE;
    443  }
    444 
    445 
    446  /* The return value is the number of fitting subtables. */
    447  static FT_UInt
    448  tt_face_validate_lookup_table( FT_Byte*  table,
    449                                 FT_Byte*  table_limit )
    450  {
    451    FT_UInt  lookupType;
    452    FT_UInt  real_lookupType = 0;
    453    FT_UInt  subtableCount;
    454 
    455    FT_Byte*  p = table;
    456    FT_Byte*  limit;
    457 
    458    FT_UInt  num_fitting_subtables = 0;
    459 
    460 
    461    if ( table_limit < p + 6 )
    462      return 0;
    463 
    464    lookupType = FT_NEXT_USHORT( p );
    465 
    466    p += 2; /* Skip `lookupFlag`. */
    467 
    468    subtableCount = FT_NEXT_USHORT( p );
    469    limit         = p + subtableCount * 2;
    470    if ( table_limit < limit )
    471      return 0;
    472 
    473    while ( p < limit )
    474    {
    475      FT_Byte*  subtable = table + FT_NEXT_USHORT( p );
    476      FT_UInt   format;
    477 
    478      FT_Bool  is_fitting = FALSE;
    479 
    480 
    481      if ( lookupType == 9 )
    482      {
    483        /* Positioning extension. */
    484        FT_Byte*  q = subtable;
    485 
    486 
    487        if ( table_limit < q + 8 )
    488          return 0;
    489 
    490        if ( FT_NEXT_USHORT( q ) != 1 ) /* format */
    491          return 0;
    492 
    493        if ( real_lookupType == 0 )
    494          real_lookupType = FT_NEXT_USHORT( q );
    495        else if ( real_lookupType != FT_NEXT_USHORT( q ) )
    496          return 0;
    497 
    498        subtable += FT_PEEK_ULONG( q );
    499      }
    500      else
    501        real_lookupType = lookupType;
    502 
    503      /* Ensure the first eight bytes of the subtable formats. */
    504      if ( table_limit < subtable + 8 )
    505        return 0;
    506 
    507      format = FT_PEEK_USHORT( subtable );
    508 
    509      if ( real_lookupType == 2 )
    510      {
    511        if ( format == 1 )
    512        {
    513          if ( !tt_face_validate_pair_pos1( subtable,
    514                                            table_limit,
    515                                            &is_fitting ) )
    516            return 0;
    517        }
    518        else if ( format == 2 )
    519        {
    520          if ( !tt_face_validate_pair_pos2( subtable,
    521                                            table_limit,
    522                                            &is_fitting ) )
    523            return 0;
    524        }
    525        else
    526          return 0;
    527      }
    528      else
    529        return 0;
    530 
    531      if ( is_fitting )
    532        num_fitting_subtables++;
    533    }
    534 
    535    return num_fitting_subtables;
    536  }
    537 
    538 
    539  static void
    540  tt_face_get_subtable_offsets( FT_Byte*    table,
    541                                FT_Byte*    gpos,
    542                                FT_UInt32*  gpos_lookups_kerning,
    543                                FT_UInt*    idx )
    544  {
    545    FT_UInt  lookupType;
    546    FT_UInt  subtableCount;
    547 
    548    FT_Byte*  p = table;
    549    FT_Byte*  limit;
    550 
    551 
    552    lookupType = FT_NEXT_USHORT( p );
    553 
    554    p += 2;
    555 
    556    subtableCount = FT_NEXT_USHORT( p );
    557    limit         = p + subtableCount * 2;
    558    while ( p < limit )
    559    {
    560      FT_Byte*  subtable = table + FT_NEXT_USHORT( p );
    561      FT_UInt   valueFormat1;
    562      FT_UInt   valueFormat2;
    563 
    564 
    565      if ( lookupType == 9 )
    566        subtable += FT_PEEK_ULONG( subtable + 4 );
    567 
    568      /* Table offsets for `valueFormat[12]` values */
    569      /* are identical for both subtable formats.   */
    570      valueFormat1 = FT_PEEK_USHORT( subtable + 4 );
    571      valueFormat2 = FT_PEEK_USHORT( subtable + 6 );
    572      if ( valueFormat1 == 0x4 && valueFormat2 == 0 )
    573      {
    574        /* We store offsets relative to the start of the GPOS table. */
    575        gpos_lookups_kerning[(*idx)++] = (FT_UInt32)( subtable - gpos );
    576      }
    577    }
    578  }
    579 
    580 
    581  FT_LOCAL_DEF( FT_Error )
    582  tt_face_load_gpos( TT_Face    face,
    583                     FT_Stream  stream )
    584  {
    585    FT_Error   error;
    586    FT_Memory  memory = face->root.memory;
    587 
    588    FT_ULong  gpos_length;
    589    FT_Byte*  gpos;
    590    FT_Byte*  gpos_limit;
    591 
    592    FT_UInt32*  gpos_lookups_kerning;
    593 
    594    FT_UInt  featureListOffset;
    595 
    596    FT_UInt   lookupListOffset;
    597    FT_Byte*  lookup_list;
    598    FT_UInt   lookupCount;
    599 
    600    FT_UInt  i;
    601 
    602    FT_Byte*  use_lookup_table = NULL;
    603    FT_UInt   num_fitting_subtables;
    604 
    605    FT_Byte*  p;
    606    FT_Byte*  limit;
    607 
    608 
    609    face->gpos_table               = NULL;
    610    face->gpos_lookups_kerning     = NULL;
    611    face->num_gpos_lookups_kerning = 0;
    612 
    613    gpos                 = NULL;
    614    gpos_lookups_kerning = NULL;
    615 
    616    error = face->goto_table( face, TTAG_GPOS, stream, &gpos_length );
    617    if ( error )
    618      goto Fail;
    619 
    620    if ( FT_FRAME_EXTRACT( gpos_length, gpos ) )
    621      goto Fail;
    622 
    623    if ( gpos_length < 10 )
    624      goto Fail;
    625 
    626    gpos_limit = gpos + gpos_length;
    627 
    628    /* We first need the number of GPOS lookups. */
    629    lookupListOffset = FT_PEEK_USHORT( gpos + 8 );
    630 
    631    lookup_list = gpos + lookupListOffset;
    632    p           = lookup_list;
    633    if ( gpos_limit < p + 2 )
    634      goto Fail;
    635 
    636    lookupCount = FT_NEXT_USHORT( p );
    637    limit       = p + lookupCount * 2;
    638    if ( gpos_limit < limit )
    639      goto Fail;
    640 
    641    /* Allocate an auxiliary array for Boolean values that */
    642    /* gets filled while walking over all 'kern' features. */
    643    if ( FT_NEW_ARRAY( use_lookup_table, lookupCount ) )
    644      goto Fail;
    645 
    646    featureListOffset = FT_PEEK_USHORT( gpos + 6 );
    647 
    648    if ( !tt_face_validate_feature_table( gpos + featureListOffset,
    649                                          gpos_limit,
    650                                          lookupCount,
    651                                          use_lookup_table ) )
    652      goto Fail;
    653 
    654    /* Now walk over all lookup tables and get the */
    655    /* number of fitting subtables.                */
    656    num_fitting_subtables = 0;
    657    for ( i = 0; i < lookupCount; i++ )
    658    {
    659      FT_UInt  lookupOffset;
    660 
    661 
    662      if ( !use_lookup_table[i] )
    663        continue;
    664 
    665      lookupOffset = FT_PEEK_USHORT( p + i * 2 );
    666 
    667      num_fitting_subtables +=
    668        tt_face_validate_lookup_table( lookup_list + lookupOffset,
    669                                       gpos_limit );
    670 
    671    }
    672 
    673    /* Loop again over all lookup tables and */
    674    /* collect offsets to those subtables.   */
    675    if ( num_fitting_subtables )
    676    {
    677      FT_UInt  idx;
    678 
    679 
    680      if ( FT_QNEW_ARRAY( gpos_lookups_kerning, num_fitting_subtables ) )
    681        goto Fail;
    682 
    683      idx = 0;
    684      for ( i = 0; i < lookupCount; i++ )
    685      {
    686        FT_UInt  lookupOffset;
    687 
    688 
    689        if ( !use_lookup_table[i] )
    690          continue;
    691 
    692        lookupOffset = FT_PEEK_USHORT( p + i * 2 );
    693 
    694        tt_face_get_subtable_offsets( lookup_list + lookupOffset,
    695                                      gpos,
    696                                      gpos_lookups_kerning,
    697                                      &idx );
    698      }
    699    }
    700 
    701    FT_FREE( use_lookup_table );
    702    use_lookup_table = NULL;
    703 
    704    face->gpos_table               = gpos;
    705    face->gpos_lookups_kerning     = gpos_lookups_kerning;
    706    face->num_gpos_lookups_kerning = num_fitting_subtables;
    707 
    708  Exit:
    709    return error;
    710 
    711  Fail:
    712    FT_FREE( gpos );
    713    FT_FREE( gpos_lookups_kerning );
    714    FT_FREE( use_lookup_table );
    715 
    716    /* If we don't have an explicit error code, set it to a generic value. */
    717    if ( !error )
    718      error = FT_THROW( Invalid_Table );
    719 
    720    goto Exit;
    721  }
    722 
    723 
    724  FT_LOCAL_DEF( void )
    725  tt_face_done_gpos( TT_Face  face )
    726  {
    727    FT_Stream  stream = face->root.stream;
    728    FT_Memory  memory = face->root.memory;
    729 
    730 
    731    FT_FRAME_RELEASE( face->gpos_table );
    732    FT_FREE( face->gpos_lookups_kerning );
    733  }
    734 
    735 
    736  /*********************************/
    737  /********                 ********/
    738  /********   GPOS access   ********/
    739  /********                 ********/
    740  /*********************************/
    741 
    742 
    743  static FT_Long
    744  tt_face_get_coverage_index( FT_Byte*  table,
    745                              FT_UInt   glyph_index )
    746  {
    747    FT_Byte*  p      = table;
    748    FT_UInt   format = FT_NEXT_USHORT( p );
    749    FT_UInt   count  = FT_NEXT_USHORT( p );
    750 
    751    FT_UInt  min, max;
    752 
    753 
    754    min = 0;
    755    max = count;
    756 
    757    if ( format == 1 )
    758    {
    759      while ( min < max )
    760      {
    761        FT_UInt  mid       = min + ( max - min ) / 2;
    762        FT_UInt  mid_index = FT_PEEK_USHORT( p + mid * 2 );
    763 
    764 
    765        if ( glyph_index > mid_index )
    766          min = mid + 1;
    767        else if ( glyph_index < mid_index )
    768          max = mid;
    769        else
    770          return mid;
    771      }
    772    }
    773    else
    774    {
    775      while ( min < max )
    776      {
    777        FT_UInt  mid          = min + ( max - min ) / 2;
    778        FT_UInt  startGlyphID = FT_PEEK_USHORT( p + mid * 6 );
    779        FT_UInt  endGlyphID   = FT_PEEK_USHORT( p + mid * 6 + 2 );
    780 
    781 
    782        if ( glyph_index > endGlyphID )
    783          min = mid + 1;
    784        else if ( glyph_index < startGlyphID )
    785          max = mid;
    786        else
    787        {
    788          FT_UInt  startCoverageIndex = FT_PEEK_USHORT( p + mid * 6 + 4 );
    789 
    790 
    791          return startCoverageIndex + glyph_index - startGlyphID;
    792        }
    793      }
    794    }
    795 
    796    return -1;
    797  }
    798 
    799 
    800  static FT_UInt
    801  tt_face_get_class( FT_Byte*  table,
    802                     FT_UInt   glyph_index )
    803  {
    804    FT_Byte*  p      = table;
    805    FT_UInt   format = FT_NEXT_USHORT( p );
    806 
    807 
    808    if ( format == 1 )
    809    {
    810      FT_UInt  startGlyphID = FT_NEXT_USHORT( p );
    811      FT_UInt  glyphCount   = FT_NEXT_USHORT( p );
    812 
    813 
    814      /* XXX: Is this modulo 65536 arithmetic? */
    815      if ( startGlyphID              <= glyph_index &&
    816           startGlyphID + glyphCount >= glyph_index )
    817        return FT_PEEK_USHORT( p + ( glyph_index - startGlyphID ) * 2 );
    818    }
    819    else
    820    {
    821      FT_UInt  count = FT_NEXT_USHORT( p );
    822 
    823      FT_UInt  min, max;
    824 
    825 
    826      min = 0;
    827      max = count;
    828 
    829      while ( min < max )
    830      {
    831        FT_UInt  mid          = min + ( max - min ) / 2;
    832        FT_UInt  startGlyphID = FT_PEEK_USHORT( p + mid * 6 );
    833        FT_UInt  endGlyphID   = FT_PEEK_USHORT( p + mid * 6 + 2 );
    834 
    835 
    836        if ( glyph_index > endGlyphID )
    837          min = mid + 1;
    838        else if ( glyph_index < startGlyphID )
    839          max = mid;
    840        else
    841          return FT_PEEK_USHORT( p + mid * 6 + 4 );
    842      }
    843    }
    844 
    845    return 0;
    846  }
    847 
    848 
    849  static FT_Bool
    850  tt_face_get_pair_pos1_kerning( FT_Byte*  table,
    851                                 FT_UInt   first_glyph,
    852                                 FT_UInt   second_glyph,
    853                                 FT_Int*   kerning )
    854  {
    855    FT_Byte*  coverage       = table + FT_PEEK_USHORT( table + 2 );
    856    FT_Long   coverage_index = tt_face_get_coverage_index( coverage,
    857                                                           first_glyph );
    858 
    859    FT_UInt   pair_set_offset;
    860    FT_Byte*  p;
    861    FT_UInt   count;
    862 
    863    FT_UInt  min, max;
    864 
    865 
    866    if ( coverage_index < 0 )
    867      return FALSE;
    868 
    869    pair_set_offset = FT_PEEK_USHORT( table + 10 + coverage_index * 2 );
    870    p               = table + pair_set_offset;
    871    count           = FT_NEXT_USHORT( p );
    872 
    873    min = 0;
    874    max = count;
    875 
    876    while ( min < max )
    877    {
    878      FT_UInt  mid       = min + ( max - min ) / 2;
    879      FT_UInt  mid_index = FT_PEEK_USHORT( p + mid * 4 );
    880 
    881 
    882      if ( second_glyph > mid_index )
    883        min = max + 1;
    884      else if ( second_glyph < mid_index )
    885        max = mid;
    886      else
    887      {
    888        *kerning = FT_PEEK_SHORT( p + mid * 4 + 2 );
    889 
    890        return TRUE;
    891      }
    892    }
    893 
    894    return FALSE;
    895  }
    896 
    897 
    898  static FT_Bool
    899  tt_face_get_pair_pos2_kerning( FT_Byte*  table,
    900                                 FT_UInt   first_glyph,
    901                                 FT_UInt   second_glyph,
    902                                 FT_Int*   kerning )
    903  {
    904    FT_Byte*  coverage       = table + FT_PEEK_USHORT( table + 2 );
    905    FT_Long   coverage_index = tt_face_get_coverage_index( coverage,
    906                                                           first_glyph );
    907 
    908    FT_Byte*  class_def1;
    909    FT_Byte*  class_def2;
    910    FT_UInt   first_class;
    911    FT_UInt   second_class;
    912    FT_UInt   class2Count;
    913 
    914 
    915    if ( coverage_index < 0 )
    916      return FALSE;
    917 
    918    class_def1 = table + FT_PEEK_USHORT( table + 8 );
    919    class_def2 = table + FT_PEEK_USHORT( table + 10 );
    920 
    921    class2Count = FT_PEEK_USHORT( table + 14 );
    922 
    923    first_class  = tt_face_get_class( class_def1, first_glyph );
    924    second_class = tt_face_get_class( class_def2, second_glyph );
    925 
    926    *kerning =
    927      FT_PEEK_SHORT( table + 16 +
    928                     ( first_class * class2Count + second_class ) * 2 );
    929 
    930    return TRUE;
    931  }
    932 
    933 
    934  FT_LOCAL_DEF( FT_Int )
    935  tt_face_get_gpos_kerning( TT_Face  face,
    936                            FT_UInt  first_glyph,
    937                            FT_UInt  second_glyph )
    938  {
    939    FT_Int  kerning = 0;
    940 
    941    FT_UInt  i;
    942 
    943 
    944    /* We only have `PairPos` subtables. */
    945    for ( i = 0; i < face->num_gpos_lookups_kerning; i++ )
    946    {
    947      FT_Byte*  subtable = face->gpos_table + face->gpos_lookups_kerning[i];
    948      FT_Byte*  p        = subtable;
    949 
    950      FT_UInt  format = FT_NEXT_USHORT( p );
    951 
    952 
    953      if ( format == 1 )
    954      {
    955        if ( tt_face_get_pair_pos1_kerning( subtable,
    956                                            first_glyph,
    957                                            second_glyph,
    958                                            &kerning ) )
    959          break;
    960      }
    961      else
    962      {
    963        if ( tt_face_get_pair_pos2_kerning( subtable,
    964                                            first_glyph,
    965                                            second_glyph,
    966                                            &kerning ) )
    967         break;
    968      }
    969    }
    970 
    971    return kerning;
    972  }
    973 
    974 #else /* !TT_CONFIG_OPTION_GPOS_KERNING */
    975 
    976  /* ANSI C doesn't like empty source files */
    977  typedef int  tt_gpos_dummy_;
    978 
    979 #endif /* !TT_CONFIG_OPTION_GPOS_KERNING */
    980 
    981 
    982 /* END */