tor-browser

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

afgsub.c (15870B)


      1 /****************************************************************************
      2 *
      3 * afgsub.c
      4 *
      5 *   Auto-fitter routines to parse the GSUB table (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 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
     19 
     20 
     21 #include <freetype/freetype.h>
     22 #include <freetype/tttables.h>
     23 #include <freetype/tttags.h>
     24 
     25 #include <freetype/internal/ftstream.h>
     26 
     27 #include "afglobal.h"
     28 #include "afgsub.h"
     29 #include "aftypes.h"
     30 
     31 
     32  /*********************************/
     33  /********                 ********/
     34  /******** GSUB validation ********/
     35  /********                 ********/
     36  /*********************************/
     37 
     38 
     39  static FT_Bool
     40  af_validate_coverage( FT_Byte*  table,
     41                        FT_Byte*  table_limit,
     42                        FT_UInt  *num_glyphs )
     43  {
     44    FT_UInt  format;
     45 
     46    FT_Byte*  p     = table;
     47    FT_UInt   count = 0;
     48 
     49 
     50    if ( table_limit < p + 4 )
     51      return FALSE;
     52 
     53    format = FT_NEXT_USHORT( p );
     54    if ( format == 1 )
     55    {
     56      FT_UInt  glyphCount = FT_NEXT_USHORT( p );
     57 
     58 
     59      /* We don't validate glyph IDs. */
     60      if ( table_limit < p + glyphCount * 2 )
     61        return FALSE;
     62 
     63      count += glyphCount;
     64    }
     65    else if ( format == 2 )
     66    {
     67      FT_UInt   rangeCount = FT_NEXT_USHORT( p );
     68      FT_Byte*  limit      = p + rangeCount * 6;
     69 
     70 
     71      if ( table_limit < limit )
     72        return FALSE;
     73 
     74      while ( p < limit )
     75      {
     76        FT_UInt  startGlyphID = FT_NEXT_USHORT( p );
     77        FT_UInt  endGlyphID   = FT_NEXT_USHORT( p );
     78 
     79 
     80        if ( startGlyphID > endGlyphID )
     81          return FALSE;
     82 
     83        count += endGlyphID - startGlyphID + 1;
     84 
     85        /* We don't validate coverage indices. */
     86        p += 2;
     87      }
     88    }
     89    else
     90      return FALSE;
     91 
     92    if ( num_glyphs )
     93      *num_glyphs = count;
     94 
     95    return TRUE;
     96  }
     97 
     98 
     99  static FT_Bool
    100  af_validate_single_subst1( FT_Byte*  table,
    101                             FT_Byte*  table_limit )
    102  {
    103    FT_Byte*  coverage;
    104 
    105 
    106    /* Subtable format is already checked. */
    107 
    108    /* The four bytes for the coverage table offset */
    109    /* and the glyph ID delta are already checked.  */
    110    coverage = table + FT_PEEK_USHORT( table + 2 );
    111    if ( !af_validate_coverage( coverage, table_limit, NULL ) )
    112      return FALSE;
    113 
    114    /* We don't validate glyph IDs. */
    115 
    116    return TRUE;
    117  }
    118 
    119 
    120  static FT_Bool
    121  af_validate_single_subst2( FT_Byte*  table,
    122                             FT_Byte*  table_limit )
    123  {
    124    FT_Byte*  coverage;
    125    FT_UInt   glyphCount;
    126    FT_UInt   num_glyphs;
    127 
    128    /* Subtable format is already checked. */
    129    FT_Byte*  p = table + 2;
    130 
    131 
    132    /* The four bytes for the coverage table offset */
    133    /* and `glyphCount` are already checked.        */
    134    coverage = table + FT_NEXT_USHORT( p );
    135    if ( !af_validate_coverage( coverage, table_limit, &num_glyphs ) )
    136      return FALSE;
    137 
    138    glyphCount = FT_NEXT_USHORT( p );
    139    /* We don't validate glyph IDs. */
    140    if ( table_limit < p + glyphCount * 2 )
    141      return FALSE;
    142 
    143    if ( glyphCount != num_glyphs )
    144      return FALSE;
    145 
    146    return TRUE;
    147  }
    148 
    149 
    150  static FT_Bool
    151  af_validate_alternate( FT_Byte*  table,
    152                         FT_Byte*  table_limit )
    153  {
    154    FT_Byte*  coverage;
    155    FT_UInt   alternateSetCount;
    156    FT_UInt   num_glyphs;
    157 
    158    /* Subtable format is already checked. */
    159    FT_Byte*  p = table + 2;
    160    FT_Byte*  limit;
    161 
    162 
    163    /* The four bytes for the coverage table offset */
    164    /* and `alternateSetCount` are already checked. */
    165    coverage = table + FT_NEXT_USHORT( p );
    166    if ( !af_validate_coverage( coverage, table_limit, &num_glyphs ) )
    167      return FALSE;
    168 
    169    alternateSetCount = FT_NEXT_USHORT( p );
    170    limit             = p + alternateSetCount * 2;
    171    if ( table_limit < limit )
    172      return FALSE;
    173 
    174    if ( alternateSetCount != num_glyphs )
    175      return FALSE;
    176 
    177    while ( p < limit )
    178    {
    179      FT_Byte*  alternate_set;
    180      FT_UInt   glyphCount;
    181 
    182 
    183      alternate_set = table + FT_NEXT_USHORT( p );
    184      if ( table_limit < alternate_set + 2 )
    185        return FALSE;
    186 
    187      glyphCount = FT_PEEK_USHORT( alternate_set );
    188      /* We don't validate glyph IDs. */
    189      if ( table_limit < alternate_set + 2 + glyphCount * 2 )
    190        return FALSE;
    191    }
    192 
    193    return TRUE;
    194  }
    195 
    196 
    197  /* Validate 'SingleSubst' and 'AlternateSubst' lookup tables. */
    198  static FT_Bool
    199  af_validate_lookup_table( FT_Byte*  table,
    200                            FT_Byte*  table_limit )
    201  {
    202    FT_UInt  lookupType;
    203    FT_UInt  real_lookupType = 0;
    204    FT_UInt  subtableCount;
    205 
    206    FT_Byte*  p = table;
    207    FT_Byte*  limit;
    208 
    209 
    210    if ( table_limit < p + 6 )
    211      return FALSE;
    212 
    213    lookupType = FT_NEXT_USHORT( p );
    214 
    215    p += 2; /* Skip `lookupFlag`. */
    216 
    217    subtableCount = FT_NEXT_USHORT( p );
    218    limit         = p + subtableCount * 2;
    219    if ( table_limit < limit )
    220      return FALSE;
    221 
    222    while ( p < limit )
    223    {
    224      FT_Byte*  subtable = table + FT_NEXT_USHORT( p );
    225      FT_UInt   format;
    226 
    227 
    228      if ( lookupType == 7 )
    229      {
    230        /* Substitution extension. */
    231        FT_Byte*  q = subtable;
    232 
    233 
    234        if ( table_limit < q + 8 )
    235          return FALSE;
    236 
    237        if ( FT_NEXT_USHORT( q ) != 1 ) /* format */
    238          return FALSE;
    239 
    240        if ( real_lookupType == 0 )
    241          real_lookupType = FT_NEXT_USHORT( q );
    242        else if ( real_lookupType != FT_NEXT_USHORT( q ) )
    243          return FALSE;
    244 
    245        subtable += FT_PEEK_ULONG( q );
    246      }
    247      else
    248        real_lookupType = lookupType;
    249 
    250      /* Ensure the first six bytes of all subtable formats. */
    251      if ( table_limit < subtable + 6 )
    252        return FALSE;
    253 
    254      format = FT_PEEK_USHORT( subtable );
    255 
    256      if ( real_lookupType == 1 )
    257      {
    258        if ( format == 1 )
    259        {
    260          if ( !af_validate_single_subst1( subtable, table_limit ) )
    261            return FALSE;
    262        }
    263        else if ( format == 2 )
    264        {
    265          if ( !af_validate_single_subst2( subtable, table_limit ) )
    266            return FALSE;
    267        }
    268        else
    269          return FALSE;
    270      }
    271      else if ( real_lookupType == 3 )
    272      {
    273        if ( format == 1 )
    274        {
    275          if ( !af_validate_alternate( subtable, table_limit ) )
    276            return FALSE;
    277        }
    278        else
    279          return FALSE;
    280      }
    281      else
    282        return FALSE;
    283    }
    284 
    285    return TRUE;
    286  }
    287 
    288 
    289  FT_LOCAL_DEF( void )
    290  af_parse_gsub( AF_FaceGlobals  globals )
    291  {
    292    FT_Error  error = FT_Err_Ok;
    293 
    294    FT_Face    face   = globals->face;
    295    FT_Memory  memory = face->memory;
    296 
    297    FT_ULong  gsub_length;
    298    FT_Byte*  gsub;
    299    FT_Byte*  gsub_limit;
    300 
    301    FT_UInt32*  gsub_lookups_single_alternate;
    302 
    303    FT_UInt   lookupListOffset;
    304    FT_Byte*  lookup_list;
    305    FT_UInt   lookupCount;
    306 
    307    FT_UInt  idx;
    308 
    309    FT_Byte*  p;
    310    FT_Byte*  limit;
    311 
    312 
    313    globals->gsub                          = NULL;
    314    globals->gsub_lookups_single_alternate = NULL;
    315 
    316    /* No error if we can't load or parse GSUB data. */
    317 
    318    gsub                          = NULL;
    319    gsub_lookups_single_alternate = NULL;
    320 
    321    gsub_length = 0;
    322    if ( FT_Load_Sfnt_Table( face, TTAG_GSUB, 0, NULL, &gsub_length ) )
    323      goto Fail;
    324 
    325    if ( FT_QALLOC( gsub, gsub_length ) )
    326      goto Fail;
    327 
    328    if ( FT_Load_Sfnt_Table( face, TTAG_GSUB, 0, gsub, &gsub_length ) )
    329      goto Fail;
    330 
    331    if ( gsub_length < 10 )
    332      goto Fail;
    333 
    334    lookupListOffset = FT_PEEK_USHORT( gsub + 8 );
    335    if ( gsub_length < lookupListOffset + 2 )
    336      goto Fail;
    337 
    338    lookupCount = FT_PEEK_USHORT( gsub + lookupListOffset );
    339    if ( gsub_length < lookupListOffset + 2 + lookupCount * 2 )
    340      goto Fail;
    341 
    342    if ( FT_NEW_ARRAY( gsub_lookups_single_alternate, lookupCount ) )
    343      goto Fail;
    344 
    345    gsub_limit  = gsub + gsub_length;
    346    lookup_list = gsub + lookupListOffset;
    347    p           = lookup_list + 2;
    348    limit       = p + lookupCount * 2;
    349    idx         = 0;
    350    while ( p < limit )
    351    {
    352      FT_UInt  lookupOffset = FT_NEXT_USHORT( p );
    353 
    354 
    355      if ( af_validate_lookup_table( lookup_list + lookupOffset,
    356                                     gsub_limit ) )
    357      {
    358        /* We store offsets relative to the start of the GSUB table. */
    359        gsub_lookups_single_alternate[idx] = lookupListOffset + lookupOffset;
    360      }
    361 
    362      idx++;
    363    }
    364 
    365    globals->gsub                          = gsub;
    366    globals->gsub_lookups_single_alternate = gsub_lookups_single_alternate;
    367 
    368    return;
    369 
    370  Fail:
    371    FT_FREE( gsub );
    372    FT_FREE( gsub_lookups_single_alternate );
    373  }
    374 
    375 
    376  /*********************************/
    377  /********                 ********/
    378  /********   GSUB access   ********/
    379  /********                 ********/
    380  /*********************************/
    381 
    382 
    383  static FT_UInt
    384  af_coverage_format( FT_Byte*  coverage )
    385  {
    386    return FT_PEEK_USHORT( coverage );
    387  }
    388 
    389 
    390  static FT_Byte*
    391  af_coverage_start( FT_Byte*  coverage )
    392  {
    393    return coverage + 4;
    394  }
    395 
    396 
    397  static FT_Byte*
    398  af_coverage_limit( FT_Byte*  coverage )
    399  {
    400    if ( af_coverage_format( coverage ) == 1 )
    401    {
    402      FT_UInt  glyphCount = FT_PEEK_USHORT( coverage + 2 );
    403 
    404 
    405      return af_coverage_start( coverage ) + glyphCount * 2;
    406    }
    407    else
    408    {
    409      FT_UInt  rangeCount = FT_PEEK_USHORT( coverage + 2 );
    410 
    411 
    412      return af_coverage_start( coverage ) + rangeCount * 6;
    413    }
    414  }
    415 
    416 
    417  typedef struct AF_CoverageIteratorRec_*  AF_CoverageIterator;
    418 
    419  typedef struct  AF_CoverageIteratorRec_
    420  {
    421    FT_UInt  format;
    422 
    423    FT_Byte*  p;
    424    FT_Byte*  limit;
    425 
    426    FT_UInt16  glyph;
    427    FT_UInt16  glyph_limit;
    428 
    429  } AF_CoverageIteratorRec;
    430 
    431 
    432  static FT_Bool
    433  af_coverage_iterator( AF_CoverageIterator  iter,
    434                        FT_UInt16*           glyph )
    435  {
    436    if ( iter->p >= iter->limit )
    437      return FALSE;
    438 
    439    if ( iter->format == 1 )
    440      *glyph = FT_NEXT_USHORT( iter->p );
    441    else
    442    {
    443      if ( iter->glyph > iter->glyph_limit )
    444      {
    445        iter->glyph       = FT_NEXT_USHORT( iter->p );
    446        iter->glyph_limit = FT_NEXT_USHORT( iter->p );
    447 
    448        iter->p += 2;
    449      }
    450 
    451      *glyph = iter->glyph++;
    452    }
    453 
    454    return TRUE;
    455  }
    456 
    457 
    458  static AF_CoverageIteratorRec
    459  af_coverage_iterator_init( FT_Byte*  coverage )
    460  {
    461    AF_CoverageIteratorRec  iterator;
    462 
    463 
    464    iterator.format      = af_coverage_format( coverage );
    465    iterator.p           = af_coverage_start( coverage );
    466    iterator.limit       = af_coverage_limit( coverage );
    467    iterator.glyph       = 1;
    468    iterator.glyph_limit = 0;
    469 
    470    return iterator;
    471  }
    472 
    473 
    474  /*
    475    Because we merge all single and alternate substitution mappings into
    476    one, large hash, we need the possibility to have multiple glyphs as
    477    values.  We utilize that we have 32bit integers but only 16bit glyph
    478    indices, using the following scheme.
    479 
    480    If glyph G maps to a single substitute S, the entry in the map is
    481 
    482      G  ->  S
    483 
    484    If glyph G maps to multiple substitutes S1, S2, ..., Sn, we do
    485 
    486      G                    ->  S1 + ((n - 1) << 16)
    487      G + (1 << 16)        ->  S2
    488      G + (2 << 16)        ->  S3
    489      ...
    490      G + ((n - 1) << 16)  ->  Sn
    491  */
    492  static FT_Error
    493  af_hash_insert( FT_UInt16  glyph,
    494                  FT_UInt16  substitute,
    495                  FT_Hash    map,
    496                  FT_Memory  memory )
    497  {
    498    FT_Error  error;
    499 
    500    size_t*  value = ft_hash_num_lookup( glyph, map );
    501 
    502 
    503    if ( !value )
    504    {
    505      error = ft_hash_num_insert( glyph, substitute, map, memory );
    506      if ( error )
    507        return error;
    508    }
    509    else
    510    {
    511      /* Get number of substitutes, increased by one... */
    512      FT_UInt  mask = ( (FT_UInt)*value & 0xFFFF0000U ) + 0x10000U;
    513 
    514 
    515      /* ... which becomes the new key mask. */
    516      error = ft_hash_num_insert( (FT_Int)( glyph | mask ),
    517                                  substitute,
    518                                  map,
    519                                  memory );
    520      if ( error )
    521        return error;
    522 
    523      /* Update number of substitutes. */
    524      *value += 0x10000U;
    525    }
    526 
    527    return FT_Err_Ok;
    528  }
    529 
    530 
    531  static FT_Error
    532  af_map_single_subst1( FT_Hash    map,
    533                        FT_Byte*   table,
    534                        FT_Memory  memory )
    535  {
    536    FT_Error  error;
    537 
    538    FT_Byte*  coverage     = table + FT_PEEK_USHORT( table + 2 );
    539    FT_UInt   deltaGlyphID = FT_PEEK_USHORT( table + 4 );
    540 
    541    AF_CoverageIteratorRec  iterator = af_coverage_iterator_init( coverage );
    542 
    543    FT_UInt16  glyph;
    544 
    545 
    546    while ( af_coverage_iterator( &iterator, &glyph ) )
    547    {
    548      /* `deltaGlyphID` requires modulo 65536 arithmetic. */
    549      FT_UInt16  subst = (FT_UInt16)( ( glyph + deltaGlyphID ) % 0x10000U );
    550 
    551 
    552      error = af_hash_insert( glyph, subst, map, memory );
    553      if ( error )
    554        return error;
    555    }
    556 
    557    return FT_Err_Ok;
    558  }
    559 
    560 
    561  static FT_Error
    562  af_map_single_subst2( FT_Hash    map,
    563                        FT_Byte*   table,
    564                        FT_Memory  memory )
    565  {
    566    FT_Error  error;
    567 
    568    FT_Byte*  coverage = table + FT_PEEK_USHORT( table + 2 );
    569 
    570    AF_CoverageIteratorRec  iterator = af_coverage_iterator_init( coverage );
    571 
    572    FT_UInt16  glyph;
    573    FT_Byte*   p = table + 6;
    574 
    575 
    576    while ( af_coverage_iterator( &iterator, &glyph ) )
    577    {
    578      FT_UInt16  subst = FT_NEXT_USHORT( p );
    579 
    580 
    581      error = af_hash_insert( glyph, subst, map, memory );
    582      if ( error )
    583        return error;
    584    }
    585 
    586    return FT_Err_Ok;
    587  }
    588 
    589 
    590  static FT_Error
    591  af_map_alternate( FT_Hash    map,
    592                    FT_Byte*   table,
    593                    FT_Memory  memory )
    594  {
    595    FT_Error  error;
    596 
    597    FT_Byte*  coverage = table + FT_PEEK_USHORT( table + 2 );
    598 
    599    AF_CoverageIteratorRec  iterator = af_coverage_iterator_init( coverage );
    600 
    601    FT_UInt16  glyph;
    602    FT_Byte*   p = table + 6;
    603 
    604 
    605    while ( af_coverage_iterator( &iterator, &glyph ) )
    606    {
    607      FT_Byte*  alternate_set = table + FT_NEXT_USHORT( p );
    608 
    609      FT_Byte*  q          = alternate_set;
    610      FT_UInt   glyphCount = FT_NEXT_USHORT( q );
    611 
    612      FT_UInt  i;
    613 
    614 
    615      for ( i = 0; i < glyphCount; i++ )
    616      {
    617        FT_UInt16  subst = FT_NEXT_USHORT( q );
    618 
    619 
    620        error = af_hash_insert( glyph, subst, map, memory );
    621        if ( error )
    622          return error;
    623      }
    624    }
    625 
    626    return FT_Err_Ok;
    627  }
    628 
    629 
    630  /* Map 'SingleSubst' and 'AlternateSubst' lookup tables. */
    631  FT_LOCAL_DEF( FT_Error )
    632  af_map_lookup( AF_FaceGlobals  globals,
    633                 FT_Hash         map,
    634                 FT_UInt32       lookup_offset )
    635  {
    636    FT_Face    face   = globals->face;
    637    FT_Memory  memory = face->memory;
    638 
    639    FT_Byte*  table = globals->gsub + lookup_offset;
    640 
    641    FT_UInt  lookupType    = FT_PEEK_USHORT( table );
    642    FT_UInt  subtableCount = FT_PEEK_USHORT( table + 4 );
    643 
    644    FT_Byte*  p     = table + 6;
    645    FT_Byte*  limit = p + subtableCount * 2;
    646 
    647 
    648    while ( p < limit )
    649    {
    650      FT_Error  error;
    651 
    652      FT_UInt  real_lookupType = lookupType;
    653 
    654      FT_Byte*  subtable = table + FT_NEXT_USHORT( p );
    655 
    656 
    657      if ( lookupType == 7 )
    658      {
    659        FT_Byte*  q = subtable + 2;
    660 
    661 
    662        real_lookupType = FT_NEXT_USHORT( q );
    663        subtable       += FT_PEEK_ULONG( q );
    664      }
    665 
    666      if ( real_lookupType == 1 )
    667      {
    668        FT_UInt  format = FT_PEEK_USHORT( subtable );
    669 
    670 
    671        error = ( format == 1 )
    672                  ? af_map_single_subst1( map, subtable, memory )
    673                  : af_map_single_subst2( map, subtable, memory );
    674      }
    675      else
    676        error = af_map_alternate( map, subtable, memory );
    677 
    678      if ( error )
    679        return error;
    680    }
    681 
    682    return FT_Err_Ok;
    683  }
    684 
    685 
    686 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
    687 
    688 /* ANSI C doesn't like empty source files */
    689 typedef int  afgsub_dummy_;
    690 
    691 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
    692 
    693 /* END */