tor-browser

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

ttpload.c (16553B)


      1 /****************************************************************************
      2 *
      3 * ttpload.c
      4 *
      5 *   TrueType-specific tables loader (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/ftobjs.h>
     21 #include <freetype/internal/ftstream.h>
     22 #include <freetype/tttags.h>
     23 
     24 #include "ttpload.h"
     25 
     26 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
     27 #include "ttgxvar.h"
     28 #endif
     29 
     30 #include "tterrors.h"
     31 
     32 
     33  /**************************************************************************
     34   *
     35   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     36   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     37   * messages during execution.
     38   */
     39 #undef  FT_COMPONENT
     40 #define FT_COMPONENT  ttpload
     41 
     42 
     43  /**************************************************************************
     44   *
     45   * @Function:
     46   *   tt_face_load_loca
     47   *
     48   * @Description:
     49   *   Load the locations table.
     50   *
     51   * @InOut:
     52   *   face ::
     53   *     A handle to the target face object.
     54   *
     55   * @Input:
     56   *   stream ::
     57   *     The input stream.
     58   *
     59   * @Return:
     60   *   FreeType error code.  0 means success.
     61   */
     62  FT_LOCAL_DEF( FT_Error )
     63  tt_face_load_loca( TT_Face    face,
     64                     FT_Stream  stream )
     65  {
     66    FT_Error  error;
     67    FT_ULong  table_len;
     68    FT_Int    shift;
     69 
     70 
     71    /* we need the size of the `glyf' table for malformed `loca' tables */
     72    error = face->goto_table( face, TTAG_glyf, stream, &face->glyf_len );
     73 
     74    /* it is possible that a font doesn't have a glyf table at all */
     75    /* or its size is zero                                         */
     76    if ( FT_ERR_EQ( error, Table_Missing ) )
     77    {
     78      face->glyf_len    = 0;
     79      face->glyf_offset = 0;
     80    }
     81    else if ( error )
     82      goto Exit;
     83    else
     84    {
     85 #ifdef FT_CONFIG_OPTION_INCREMENTAL
     86      if ( face->root.internal->incremental_interface )
     87        face->glyf_offset = 0;
     88      else
     89 #endif
     90        face->glyf_offset = FT_STREAM_POS();
     91    }
     92 
     93    FT_TRACE2(( "Locations " ));
     94    error = face->goto_table( face, TTAG_loca, stream, &table_len );
     95    if ( error )
     96    {
     97      error = FT_THROW( Locations_Missing );
     98      goto Exit;
     99    }
    100 
    101    shift = face->header.Index_To_Loc_Format != 0 ? 2 : 1;
    102 
    103    if ( table_len > 0x10000UL << shift )
    104    {
    105      FT_TRACE2(( "table too large\n" ));
    106      table_len = 0x10000UL << shift;
    107    }
    108 
    109    face->num_locations = table_len >> shift;
    110 
    111    if ( face->num_locations != (FT_ULong)face->root.num_glyphs + 1 )
    112    {
    113      FT_TRACE2(( "glyph count mismatch!  loca: %lu, maxp: %ld\n",
    114                  face->num_locations - 1, face->root.num_glyphs ));
    115 
    116      /* we only handle the case where `maxp' gives a larger value */
    117      if ( face->num_locations < (FT_ULong)face->root.num_glyphs + 1 )
    118      {
    119        FT_ULong  new_loca_len =
    120                    ( (FT_ULong)face->root.num_glyphs + 1 ) << shift;
    121 
    122        TT_Table  entry = face->dir_tables;
    123        TT_Table  limit = entry + face->num_tables;
    124 
    125        FT_Long  pos   = (FT_Long)FT_STREAM_POS();
    126        FT_Long  dist  = 0x7FFFFFFFL;
    127        FT_Bool  found = 0;
    128 
    129 
    130        /* compute the distance to next table in font file */
    131        for ( ; entry < limit; entry++ )
    132        {
    133          FT_Long  diff = (FT_Long)entry->Offset - pos;
    134 
    135 
    136          if ( diff > 0 && diff < dist )
    137          {
    138            dist  = diff;
    139            found = 1;
    140          }
    141        }
    142 
    143        if ( !found )
    144        {
    145          /* `loca' is the last table */
    146          dist = (FT_Long)stream->size - pos;
    147        }
    148 
    149        if ( new_loca_len <= (FT_ULong)dist )
    150        {
    151          face->num_locations = (FT_ULong)face->root.num_glyphs + 1;
    152          table_len           = new_loca_len;
    153 
    154          FT_TRACE2(( "adjusting num_locations to %lu\n",
    155                      face->num_locations ));
    156        }
    157        else
    158        {
    159          face->root.num_glyphs = face->num_locations
    160                                    ? (FT_Long)face->num_locations - 1 : 0;
    161 
    162          FT_TRACE2(( "adjusting num_glyphs to %ld\n",
    163                      face->root.num_glyphs ));
    164        }
    165      }
    166    }
    167 
    168    /*
    169     * Extract the frame.  We don't need to decompress it since
    170     * we are able to parse it directly.
    171     */
    172    if ( FT_FRAME_EXTRACT( table_len, face->glyph_locations ) )
    173      goto Exit;
    174 
    175    FT_TRACE2(( "loaded\n" ));
    176 
    177  Exit:
    178    return error;
    179  }
    180 
    181 
    182  FT_LOCAL_DEF( FT_ULong )
    183  tt_face_get_location( FT_Face    face,   /* TT_Face */
    184                        FT_UInt    gindex,
    185                        FT_ULong  *asize )
    186  {
    187    TT_Face   ttface = (TT_Face)face;
    188    FT_ULong  pos1, pos2;
    189    FT_Byte*  p;
    190    FT_Byte*  p_limit;
    191 
    192 
    193    pos1 = pos2 = 0;
    194 
    195    if ( gindex < ttface->num_locations )
    196    {
    197      if ( ttface->header.Index_To_Loc_Format != 0 )
    198      {
    199        p       = ttface->glyph_locations + gindex * 4;
    200        p_limit = ttface->glyph_locations + ttface->num_locations * 4;
    201 
    202        pos1 = FT_NEXT_ULONG( p );
    203        pos2 = pos1;
    204 
    205        if ( p + 4 <= p_limit )
    206          pos2 = FT_NEXT_ULONG( p );
    207      }
    208      else
    209      {
    210        p       = ttface->glyph_locations + gindex * 2;
    211        p_limit = ttface->glyph_locations + ttface->num_locations * 2;
    212 
    213        pos1 = FT_NEXT_USHORT( p );
    214        pos2 = pos1;
    215 
    216        if ( p + 2 <= p_limit )
    217          pos2 = FT_NEXT_USHORT( p );
    218 
    219        pos1 <<= 1;
    220        pos2 <<= 1;
    221      }
    222    }
    223 
    224    /* Check broken location data. */
    225    if ( pos1 > ttface->glyf_len )
    226    {
    227      FT_TRACE1(( "tt_face_get_location:"
    228                  " too large offset (0x%08lx) found for glyph index %u,\n",
    229                  pos1, gindex ));
    230      FT_TRACE1(( "                     "
    231                  " exceeding the end of `glyf' table (0x%08lx)\n",
    232                  ttface->glyf_len ));
    233      *asize = 0;
    234      return 0;
    235    }
    236 
    237    if ( pos2 > ttface->glyf_len )
    238    {
    239      /* We try to sanitize the last `loca' entry. */
    240      if ( gindex == ttface->num_locations - 2 )
    241      {
    242        FT_TRACE1(( "tt_face_get_location:"
    243                    " too large size (%lu bytes) found for glyph index %u,\n",
    244                    pos2 - pos1, gindex ));
    245        FT_TRACE1(( "                     "
    246                    " truncating at the end of `glyf' table to %lu bytes\n",
    247                    ttface->glyf_len - pos1 ));
    248        pos2 = ttface->glyf_len;
    249      }
    250      else
    251      {
    252        FT_TRACE1(( "tt_face_get_location:"
    253                    " too large offset (0x%08lx) found for glyph index %u,\n",
    254                    pos2, gindex + 1 ));
    255        FT_TRACE1(( "                     "
    256                    " exceeding the end of `glyf' table (0x%08lx)\n",
    257                    ttface->glyf_len ));
    258        *asize = 0;
    259        return 0;
    260      }
    261    }
    262 
    263    /* The `loca' table must be ordered; it refers to the length of */
    264    /* an entry as the difference between the current and the next  */
    265    /* position.  However, there do exist (malformed) fonts which   */
    266    /* don't obey this rule, so we are only able to provide an      */
    267    /* upper bound for the size.                                    */
    268    /*                                                              */
    269    /* We get (intentionally) a wrong, non-zero result in case the  */
    270    /* `glyf' table is missing.                                     */
    271    if ( pos2 >= pos1 )
    272      *asize = (FT_ULong)( pos2 - pos1 );
    273    else
    274      *asize = (FT_ULong)( ttface->glyf_len - pos1 );
    275 
    276    return pos1;
    277  }
    278 
    279 
    280  FT_LOCAL_DEF( void )
    281  tt_face_done_loca( TT_Face  face )
    282  {
    283    FT_Stream  stream = face->root.stream;
    284 
    285 
    286    FT_FRAME_RELEASE( face->glyph_locations );
    287    face->num_locations = 0;
    288  }
    289 
    290 
    291 
    292  /**************************************************************************
    293   *
    294   * @Function:
    295   *   tt_face_load_cvt
    296   *
    297   * @Description:
    298   *   Load the control value table into a face object.
    299   *
    300   * @InOut:
    301   *   face ::
    302   *     A handle to the target face object.
    303   *
    304   * @Input:
    305   *   stream ::
    306   *     A handle to the input stream.
    307   *
    308   * @Return:
    309   *   FreeType error code.  0 means success.
    310   */
    311  FT_LOCAL_DEF( FT_Error )
    312  tt_face_load_cvt( TT_Face    face,
    313                    FT_Stream  stream )
    314  {
    315 #ifdef TT_USE_BYTECODE_INTERPRETER
    316 
    317    FT_Error   error;
    318    FT_Memory  memory = stream->memory;
    319    FT_ULong   table_len;
    320 
    321 
    322    FT_TRACE2(( "CVT " ));
    323 
    324    error = face->goto_table( face, TTAG_cvt, stream, &table_len );
    325    if ( error )
    326    {
    327      FT_TRACE2(( "is missing\n" ));
    328 
    329      face->cvt_size = 0;
    330      face->cvt      = NULL;
    331      error          = FT_Err_Ok;
    332 
    333      goto Exit;
    334    }
    335 
    336    face->cvt_size = table_len / 2;
    337 
    338    if ( FT_QNEW_ARRAY( face->cvt, face->cvt_size ) )
    339      goto Exit;
    340 
    341    if ( FT_FRAME_ENTER( face->cvt_size * 2L ) )
    342      goto Exit;
    343 
    344    {
    345      FT_Int32*  cur   = face->cvt;
    346      FT_Int32*  limit = cur + face->cvt_size;
    347 
    348 
    349      for ( ; cur < limit; cur++ )
    350        *cur = FT_GET_SHORT() * 64;
    351    }
    352 
    353    FT_FRAME_EXIT();
    354    FT_TRACE2(( "loaded\n" ));
    355 
    356 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    357    if ( face->doblend )
    358      error = tt_face_vary_cvt( face, stream );
    359 #endif
    360 
    361  Exit:
    362    return error;
    363 
    364 #else /* !TT_USE_BYTECODE_INTERPRETER */
    365 
    366    FT_UNUSED( face   );
    367    FT_UNUSED( stream );
    368 
    369    return FT_Err_Ok;
    370 
    371 #endif
    372  }
    373 
    374 
    375  /**************************************************************************
    376   *
    377   * @Function:
    378   *   tt_face_load_fpgm
    379   *
    380   * @Description:
    381   *   Load the font program.
    382   *
    383   * @InOut:
    384   *   face ::
    385   *     A handle to the target face object.
    386   *
    387   * @Input:
    388   *   stream ::
    389   *     A handle to the input stream.
    390   *
    391   * @Return:
    392   *   FreeType error code.  0 means success.
    393   */
    394  FT_LOCAL_DEF( FT_Error )
    395  tt_face_load_fpgm( TT_Face    face,
    396                     FT_Stream  stream )
    397  {
    398 #ifdef TT_USE_BYTECODE_INTERPRETER
    399 
    400    FT_Error  error;
    401    FT_ULong  table_len;
    402 
    403 
    404    FT_TRACE2(( "Font program " ));
    405 
    406    /* The font program is optional */
    407    error = face->goto_table( face, TTAG_fpgm, stream, &table_len );
    408    if ( error )
    409    {
    410      face->font_program      = NULL;
    411      face->font_program_size = 0;
    412      error                   = FT_Err_Ok;
    413 
    414      FT_TRACE2(( "is missing\n" ));
    415    }
    416    else
    417    {
    418      face->font_program_size = table_len;
    419      if ( FT_FRAME_EXTRACT( table_len, face->font_program ) )
    420        goto Exit;
    421 
    422      FT_TRACE2(( "loaded, %12lu bytes\n", face->font_program_size ));
    423    }
    424 
    425  Exit:
    426    return error;
    427 
    428 #else /* !TT_USE_BYTECODE_INTERPRETER */
    429 
    430    FT_UNUSED( face   );
    431    FT_UNUSED( stream );
    432 
    433    return FT_Err_Ok;
    434 
    435 #endif
    436  }
    437 
    438 
    439  /**************************************************************************
    440   *
    441   * @Function:
    442   *   tt_face_load_prep
    443   *
    444   * @Description:
    445   *   Load the cvt program.
    446   *
    447   * @InOut:
    448   *   face ::
    449   *     A handle to the target face object.
    450   *
    451   * @Input:
    452   *   stream ::
    453   *     A handle to the input stream.
    454   *
    455   * @Return:
    456   *   FreeType error code.  0 means success.
    457   */
    458  FT_LOCAL_DEF( FT_Error )
    459  tt_face_load_prep( TT_Face    face,
    460                     FT_Stream  stream )
    461  {
    462 #ifdef TT_USE_BYTECODE_INTERPRETER
    463 
    464    FT_Error  error;
    465    FT_ULong  table_len;
    466 
    467 
    468    FT_TRACE2(( "Prep program " ));
    469 
    470    error = face->goto_table( face, TTAG_prep, stream, &table_len );
    471    if ( error )
    472    {
    473      face->cvt_program      = NULL;
    474      face->cvt_program_size = 0;
    475      error                  = FT_Err_Ok;
    476 
    477      FT_TRACE2(( "is missing\n" ));
    478    }
    479    else
    480    {
    481      face->cvt_program_size = table_len;
    482      if ( FT_FRAME_EXTRACT( table_len, face->cvt_program ) )
    483        goto Exit;
    484 
    485      FT_TRACE2(( "loaded, %12lu bytes\n", face->cvt_program_size ));
    486    }
    487 
    488  Exit:
    489    return error;
    490 
    491 #else /* !TT_USE_BYTECODE_INTERPRETER */
    492 
    493    FT_UNUSED( face   );
    494    FT_UNUSED( stream );
    495 
    496    return FT_Err_Ok;
    497 
    498 #endif
    499  }
    500 
    501 
    502  FT_COMPARE_DEF( int )
    503  compare_ppem( const void*  a,
    504                const void*  b )
    505  {
    506    return **(FT_Byte**)a - **(FT_Byte**)b;
    507  }
    508 
    509 
    510  /**************************************************************************
    511   *
    512   * @Function:
    513   *   tt_face_load_hdmx
    514   *
    515   * @Description:
    516   *   Load the `hdmx' table into the face object.
    517   *
    518   * @Input:
    519   *   face ::
    520   *     A handle to the target face object.
    521   *
    522   *   stream ::
    523   *     A handle to the input stream.
    524   *
    525   * @Return:
    526   *   FreeType error code.  0 means success.
    527   */
    528 
    529  FT_LOCAL_DEF( FT_Error )
    530  tt_face_load_hdmx( TT_Face    face,
    531                     FT_Stream  stream )
    532  {
    533    FT_Error   error;
    534    FT_Memory  memory = stream->memory;
    535    FT_UInt    nn, num_records;
    536    FT_ULong   table_size, record_size;
    537    FT_Byte*   p;
    538    FT_Byte*   limit;
    539 
    540 
    541    /* this table is optional */
    542    error = face->goto_table( face, TTAG_hdmx, stream, &table_size );
    543    if ( error || table_size < 8 )
    544      return FT_Err_Ok;
    545 
    546    if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) )
    547      goto Exit;
    548 
    549    p     = face->hdmx_table;
    550    limit = p + table_size;
    551 
    552    /* Given that `hdmx' tables are losing its importance (for example, */
    553    /* variation fonts introduced in OpenType 1.8 must not have this    */
    554    /* table) we no longer test for a correct `version' field.          */
    555    p          += 2;
    556    num_records = FT_NEXT_USHORT( p );
    557    record_size = FT_NEXT_ULONG( p );
    558 
    559    /* There are at least two fonts, HANNOM-A and HANNOM-B version */
    560    /* 2.0 (2005), which get this wrong: The upper two bytes of    */
    561    /* the size value are set to 0xFF instead of 0x00.  We catch   */
    562    /* and fix this.                                               */
    563 
    564    if ( record_size >= 0xFFFF0000UL )
    565      record_size &= 0xFFFFU;
    566 
    567    FT_TRACE2(( "Hdmx " ));
    568 
    569    /* The limit for `num_records' is a heuristic value. */
    570    if ( num_records > 255 || num_records == 0 )
    571    {
    572      FT_TRACE2(( "with unreasonable %u records rejected\n", num_records ));
    573      goto Fail;
    574    }
    575 
    576    /* Out-of-spec tables are rejected.  The record size must be */
    577    /* equal to the number of glyphs + 2 + 32-bit padding.       */
    578    if ( (FT_Long)record_size != ( ( face->root.num_glyphs + 2 + 3 ) & ~3 ) )
    579    {
    580      FT_TRACE2(( "with record size off by %ld bytes rejected\n",
    581                  (FT_Long)record_size -
    582                    ( ( face->root.num_glyphs + 2 + 3 ) & ~3 ) ));
    583      goto Fail;
    584    }
    585 
    586    if ( FT_QNEW_ARRAY( face->hdmx_records, num_records ) )
    587      goto Fail;
    588 
    589    for ( nn = 0; nn < num_records; nn++ )
    590    {
    591      if ( p + record_size > limit )
    592        break;
    593      face->hdmx_records[nn] = p;
    594      p                     += record_size;
    595    }
    596 
    597    /* The records must be already sorted by ppem but it does not */
    598    /* hurt to make sure so that the binary search works later.   */
    599    ft_qsort( face->hdmx_records, nn, sizeof ( FT_Byte* ), compare_ppem );
    600 
    601    face->hdmx_record_count = nn;
    602    face->hdmx_table_size   = table_size;
    603    face->hdmx_record_size  = record_size;
    604 
    605    FT_TRACE2(( "%ux%lu loaded\n", num_records, record_size ));
    606 
    607  Exit:
    608    return error;
    609 
    610  Fail:
    611    FT_FRAME_RELEASE( face->hdmx_table );
    612    face->hdmx_table_size = 0;
    613    goto Exit;
    614  }
    615 
    616 
    617  FT_LOCAL_DEF( void )
    618  tt_face_free_hdmx( TT_Face  face )
    619  {
    620    FT_Stream  stream = face->root.stream;
    621    FT_Memory  memory = stream->memory;
    622 
    623 
    624    FT_FREE( face->hdmx_records );
    625    FT_FRAME_RELEASE( face->hdmx_table );
    626  }
    627 
    628 
    629  /**************************************************************************
    630   *
    631   * Return the advance width table for a given pixel size if it is found
    632   * in the font's `hdmx' table (if any).  The records must be sorted for
    633   * the binary search to work properly.
    634   */
    635  FT_LOCAL_DEF( FT_Byte* )
    636  tt_face_get_device_metrics( TT_Face  face,
    637                              FT_UInt  ppem,
    638                              FT_UInt  gindex )
    639  {
    640    FT_UInt   min    = 0;
    641    FT_UInt   max    = face->hdmx_record_count;
    642    FT_UInt   mid;
    643    FT_Byte*  result = NULL;
    644 
    645 
    646    while ( min < max )
    647    {
    648      mid = ( min + max ) >> 1;
    649 
    650      if ( face->hdmx_records[mid][0] > ppem )
    651        max = mid;
    652      else if ( face->hdmx_records[mid][0] < ppem )
    653        min = mid + 1;
    654      else
    655      {
    656        result = face->hdmx_records[mid] + 2 + gindex;
    657        break;
    658      }
    659    }
    660 
    661    return result;
    662  }
    663 
    664 
    665 /* END */