(root)/
freetype-2.13.2/
src/
truetype/
ttpload.c
       1  /****************************************************************************
       2   *
       3   * ttpload.c
       4   *
       5   *   TrueType-specific tables loader (body).
       6   *
       7   * Copyright (C) 1996-2023 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: %ld, 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 %ld\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 %d,\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 (%ld bytes) found for glyph index %d,\n",
     244                      pos2 - pos1, gindex ));
     245          FT_TRACE1(( "                     "
     246                      " truncating at the end of `glyf' table to %ld 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 %d,\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, %12ld 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, %12ld 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 */