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