tor-browser

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

sfwoff.c (12289B)


      1 /****************************************************************************
      2 *
      3 * sfwoff.c
      4 *
      5 *   WOFF format management (base).
      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 "sfwoff.h"
     20 #include <freetype/tttags.h>
     21 #include <freetype/internal/ftcalc.h>
     22 #include <freetype/internal/ftdebug.h>
     23 #include <freetype/internal/ftstream.h>
     24 #include <freetype/ftgzip.h>
     25 
     26 
     27 #ifdef FT_CONFIG_OPTION_USE_ZLIB
     28 
     29 
     30  /**************************************************************************
     31   *
     32   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     33   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     34   * messages during execution.
     35   */
     36 #undef  FT_COMPONENT
     37 #define FT_COMPONENT  sfwoff
     38 
     39 
     40 #define WRITE_USHORT( p, v )                \
     41          do                                \
     42          {                                 \
     43            *(p)++ = (FT_Byte)( (v) >> 8 ); \
     44            *(p)++ = (FT_Byte)( (v) >> 0 ); \
     45                                            \
     46          } while ( 0 )
     47 
     48 #define WRITE_ULONG( p, v )                  \
     49          do                                 \
     50          {                                  \
     51            *(p)++ = (FT_Byte)( (v) >> 24 ); \
     52            *(p)++ = (FT_Byte)( (v) >> 16 ); \
     53            *(p)++ = (FT_Byte)( (v) >>  8 ); \
     54            *(p)++ = (FT_Byte)( (v) >>  0 ); \
     55                                             \
     56          } while ( 0 )
     57 
     58 
     59  static void
     60  sfnt_stream_close( FT_Stream  stream )
     61  {
     62    FT_Memory  memory = stream->memory;
     63 
     64 
     65    FT_FREE( stream->base );
     66 
     67    stream->size  = 0;
     68    stream->close = NULL;
     69  }
     70 
     71 
     72  FT_COMPARE_DEF( int )
     73  compare_offsets( const void*  a,
     74                   const void*  b )
     75  {
     76    WOFF_Table  table1 = *(WOFF_Table*)a;
     77    WOFF_Table  table2 = *(WOFF_Table*)b;
     78 
     79    FT_ULong  offset1 = table1->Offset;
     80    FT_ULong  offset2 = table2->Offset;
     81 
     82 
     83    if ( offset1 > offset2 )
     84      return 1;
     85    else if ( offset1 < offset2 )
     86      return -1;
     87    else
     88      return 0;
     89  }
     90 
     91 
     92  /* Replace `face->root.stream' with a stream containing the extracted */
     93  /* SFNT of a WOFF font.                                               */
     94 
     95  FT_LOCAL_DEF( FT_Error )
     96  woff_open_font( FT_Stream  stream,
     97                  TT_Face    face )
     98  {
     99    FT_Memory       memory = stream->memory;
    100    FT_Error        error  = FT_Err_Ok;
    101 
    102    WOFF_HeaderRec  woff;
    103    WOFF_Table      tables  = NULL;
    104    WOFF_Table*     indices = NULL;
    105 
    106    FT_ULong        woff_offset;
    107 
    108    FT_Byte*        sfnt        = NULL;
    109    FT_Stream       sfnt_stream = NULL;
    110 
    111    FT_Byte*        sfnt_header;
    112    FT_ULong        sfnt_offset;
    113 
    114    FT_Int          nn;
    115    FT_Tag          old_tag = 0;
    116 
    117    static const FT_Frame_Field  woff_header_fields[] =
    118    {
    119 #undef  FT_STRUCTURE
    120 #define FT_STRUCTURE  WOFF_HeaderRec
    121 
    122      FT_FRAME_START( 44 ),
    123        FT_FRAME_ULONG ( signature ),
    124        FT_FRAME_ULONG ( flavor ),
    125        FT_FRAME_ULONG ( length ),
    126        FT_FRAME_USHORT( num_tables ),
    127        FT_FRAME_USHORT( reserved ),
    128        FT_FRAME_ULONG ( totalSfntSize ),
    129        FT_FRAME_USHORT( majorVersion ),
    130        FT_FRAME_USHORT( minorVersion ),
    131        FT_FRAME_ULONG ( metaOffset ),
    132        FT_FRAME_ULONG ( metaLength ),
    133        FT_FRAME_ULONG ( metaOrigLength ),
    134        FT_FRAME_ULONG ( privOffset ),
    135        FT_FRAME_ULONG ( privLength ),
    136      FT_FRAME_END
    137    };
    138 
    139 
    140    FT_ASSERT( stream == face->root.stream );
    141    FT_ASSERT( FT_STREAM_POS() == 0 );
    142 
    143    if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) )
    144      return error;
    145 
    146    /* Make sure we don't recurse back here or hit TTC code. */
    147    if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf )
    148      return FT_THROW( Invalid_Table );
    149 
    150    /* Miscellaneous checks. */
    151    if ( woff.length != stream->size                              ||
    152         woff.num_tables == 0                                     ||
    153         woff.num_tables >  0xFFFU                                ||
    154         44 + woff.num_tables * 20UL >= woff.length               ||
    155         12 + woff.num_tables * 16UL >= woff.totalSfntSize        ||
    156         ( woff.totalSfntSize & 3 ) != 0                          ||
    157         ( woff.metaOffset == 0 && ( woff.metaLength != 0     ||
    158                                     woff.metaOrigLength != 0 ) ) ||
    159         ( woff.metaLength != 0 && woff.metaOrigLength == 0 )     ||
    160         ( woff.privOffset == 0 && woff.privLength != 0 )         )
    161    {
    162      FT_ERROR(( "woff_font_open: invalid WOFF header\n" ));
    163      return FT_THROW( Invalid_Table );
    164    }
    165 
    166    /* Don't trust `totalSfntSize' before thorough checks. */
    167    if ( FT_QALLOC( sfnt, 12 ) || FT_NEW( sfnt_stream ) )
    168      goto Exit;
    169 
    170    sfnt_header = sfnt;
    171 
    172    /* Write sfnt header. */
    173    {
    174      FT_Int  entrySelector = FT_MSB( woff.num_tables );
    175      FT_Int  searchRange   = ( 1 << entrySelector ) * 16;
    176      FT_Int  rangeShift    = woff.num_tables * 16 - searchRange;
    177 
    178 
    179      WRITE_ULONG ( sfnt_header, woff.flavor );
    180      WRITE_USHORT( sfnt_header, woff.num_tables );
    181      WRITE_USHORT( sfnt_header, searchRange );
    182      WRITE_USHORT( sfnt_header, entrySelector );
    183      WRITE_USHORT( sfnt_header, rangeShift );
    184    }
    185 
    186    /* While the entries in the sfnt header must be sorted by the */
    187    /* tag value, the tables themselves are not.  We thus have to */
    188    /* sort them by offset and check that they don't overlap.     */
    189 
    190    if ( FT_QNEW_ARRAY( tables, woff.num_tables )  ||
    191         FT_QNEW_ARRAY( indices, woff.num_tables ) )
    192      goto Exit;
    193 
    194    FT_TRACE2(( "\n" ));
    195    FT_TRACE2(( "  tag    offset    compLen  origLen  checksum\n" ));
    196    FT_TRACE2(( "  -------------------------------------------\n" ));
    197 
    198    if ( FT_FRAME_ENTER( 20L * woff.num_tables ) )
    199      goto Exit;
    200 
    201    for ( nn = 0; nn < woff.num_tables; nn++ )
    202    {
    203      WOFF_Table  table = tables + nn;
    204 
    205      table->Tag        = FT_GET_TAG4();
    206      table->Offset     = FT_GET_ULONG();
    207      table->CompLength = FT_GET_ULONG();
    208      table->OrigLength = FT_GET_ULONG();
    209      table->CheckSum   = FT_GET_ULONG();
    210 
    211      FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx  %08lx\n",
    212                  (FT_Char)( table->Tag >> 24 ),
    213                  (FT_Char)( table->Tag >> 16 ),
    214                  (FT_Char)( table->Tag >> 8  ),
    215                  (FT_Char)( table->Tag       ),
    216                  table->Offset,
    217                  table->CompLength,
    218                  table->OrigLength,
    219                  table->CheckSum ));
    220 
    221      if ( table->Tag <= old_tag )
    222      {
    223        FT_FRAME_EXIT();
    224 
    225        FT_ERROR(( "woff_font_open: table tags are not sorted\n" ));
    226        error = FT_THROW( Invalid_Table );
    227        goto Exit;
    228      }
    229 
    230      old_tag     = table->Tag;
    231      indices[nn] = table;
    232    }
    233 
    234    FT_FRAME_EXIT();
    235 
    236    /* Sort by offset. */
    237 
    238    ft_qsort( indices,
    239              woff.num_tables,
    240              sizeof ( WOFF_Table ),
    241              compare_offsets );
    242 
    243    /* Check offsets and lengths. */
    244 
    245    woff_offset = 44 + woff.num_tables * 20L;
    246    sfnt_offset = 12 + woff.num_tables * 16L;
    247 
    248    for ( nn = 0; nn < woff.num_tables; nn++ )
    249    {
    250      WOFF_Table  table = indices[nn];
    251 
    252 
    253      if ( table->Offset != woff_offset                         ||
    254           table->CompLength > woff.length                      ||
    255           table->Offset > woff.length - table->CompLength      ||
    256           table->OrigLength > woff.totalSfntSize               ||
    257           sfnt_offset > woff.totalSfntSize - table->OrigLength ||
    258           table->CompLength > table->OrigLength                )
    259      {
    260        FT_ERROR(( "woff_font_open: invalid table offsets\n" ));
    261        error = FT_THROW( Invalid_Table );
    262        goto Exit;
    263      }
    264 
    265      table->OrigOffset = sfnt_offset;
    266 
    267      /* The offsets must be multiples of 4. */
    268      woff_offset += ( table->CompLength + 3 ) & ~3U;
    269      sfnt_offset += ( table->OrigLength + 3 ) & ~3U;
    270    }
    271 
    272    /*
    273     * Final checks!
    274     *
    275     * We don't decode and check the metadata block.
    276     * We don't check table checksums either.
    277     * But other than those, I think we implement all
    278     * `MUST' checks from the spec.
    279     */
    280 
    281    if ( woff.metaOffset )
    282    {
    283      if ( woff.metaOffset != woff_offset                  ||
    284           woff.metaOffset + woff.metaLength > woff.length )
    285      {
    286        FT_ERROR(( "woff_font_open:"
    287                   " invalid `metadata' offset or length\n" ));
    288        error = FT_THROW( Invalid_Table );
    289        goto Exit;
    290      }
    291 
    292      /* We have padding only ... */
    293      woff_offset += woff.metaLength;
    294    }
    295 
    296    if ( woff.privOffset )
    297    {
    298      /* ... if it isn't the last block. */
    299      woff_offset = ( woff_offset + 3 ) & ~3U;
    300 
    301      if ( woff.privOffset != woff_offset                  ||
    302           woff.privOffset + woff.privLength > woff.length )
    303      {
    304        FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" ));
    305        error = FT_THROW( Invalid_Table );
    306        goto Exit;
    307      }
    308 
    309      /* No padding for the last block. */
    310      woff_offset += woff.privLength;
    311    }
    312 
    313    if ( sfnt_offset != woff.totalSfntSize ||
    314         woff_offset != woff.length        )
    315    {
    316      FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" ));
    317      error = FT_THROW( Invalid_Table );
    318      goto Exit;
    319    }
    320 
    321    /* Now use `totalSfntSize'. */
    322    if ( FT_QREALLOC( sfnt, 12, woff.totalSfntSize ) )
    323      goto Exit;
    324 
    325    sfnt_header = sfnt + 12;
    326 
    327    /* Write the tables. */
    328 
    329    for ( nn = 0; nn < woff.num_tables; nn++ )
    330    {
    331      WOFF_Table  table = tables + nn;
    332 
    333 
    334      /* Write SFNT table entry. */
    335      WRITE_ULONG( sfnt_header, table->Tag );
    336      WRITE_ULONG( sfnt_header, table->CheckSum );
    337      WRITE_ULONG( sfnt_header, table->OrigOffset );
    338      WRITE_ULONG( sfnt_header, table->OrigLength );
    339 
    340      /* Write table data. */
    341      if ( FT_STREAM_SEEK( table->Offset )     ||
    342           FT_FRAME_ENTER( table->CompLength ) )
    343        goto Exit;
    344 
    345      if ( table->CompLength == table->OrigLength )
    346      {
    347        /* Uncompressed data; just copy. */
    348        ft_memcpy( sfnt + table->OrigOffset,
    349                   stream->cursor,
    350                   table->OrigLength );
    351      }
    352      else
    353      {
    354        /* Uncompress with zlib. */
    355        FT_ULong  output_len = table->OrigLength;
    356 
    357 
    358        error = FT_Gzip_Uncompress( memory,
    359                                    sfnt + table->OrigOffset, &output_len,
    360                                    stream->cursor, table->CompLength );
    361        if ( error )
    362          goto Exit1;
    363        if ( output_len != table->OrigLength )
    364        {
    365          FT_ERROR(( "woff_font_open: compressed table length mismatch\n" ));
    366          error = FT_THROW( Invalid_Table );
    367          goto Exit1;
    368        }
    369      }
    370 
    371      FT_FRAME_EXIT();
    372 
    373      /* We don't check whether the padding bytes in the WOFF file are     */
    374      /* actually '\0'.  For the output, however, we do set them properly. */
    375      sfnt_offset = table->OrigOffset + table->OrigLength;
    376      while ( sfnt_offset & 3 )
    377      {
    378        sfnt[sfnt_offset] = '\0';
    379        sfnt_offset++;
    380      }
    381    }
    382 
    383    /* Ok!  Finally ready.  Swap out stream and return. */
    384    FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize );
    385    sfnt_stream->memory = stream->memory;
    386    sfnt_stream->close  = sfnt_stream_close;
    387 
    388    FT_Stream_Free(
    389      face->root.stream,
    390      ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 );
    391 
    392    face->root.stream = sfnt_stream;
    393 
    394    face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
    395 
    396  Exit:
    397    FT_FREE( tables );
    398    FT_FREE( indices );
    399 
    400    if ( error )
    401    {
    402      FT_FREE( sfnt );
    403      FT_Stream_Close( sfnt_stream );
    404      FT_FREE( sfnt_stream );
    405    }
    406 
    407    return error;
    408 
    409  Exit1:
    410    FT_FRAME_EXIT();
    411    goto Exit;
    412  }
    413 
    414 
    415 #undef WRITE_USHORT
    416 #undef WRITE_ULONG
    417 
    418 #else /* !FT_CONFIG_OPTION_USE_ZLIB */
    419 
    420  /* ANSI C doesn't like empty source files */
    421  typedef int  sfwoff_dummy_;
    422 
    423 #endif /* !FT_CONFIG_OPTION_USE_ZLIB */
    424 
    425 
    426 /* END */