(root)/
freetype-2.13.2/
src/
lzw/
ftlzw.c
       1  /****************************************************************************
       2   *
       3   * ftlzw.c
       4   *
       5   *   FreeType support for .Z compressed files.
       6   *
       7   * This optional component relies on NetBSD's zopen().  It should mainly
       8   * be used to parse compressed PCF fonts, as found with many X11 server
       9   * distributions.
      10   *
      11   * Copyright (C) 2004-2023 by
      12   * Albert Chin-A-Young.
      13   *
      14   * based on code in `src/gzip/ftgzip.c'
      15   *
      16   * This file is part of the FreeType project, and may only be used,
      17   * modified, and distributed under the terms of the FreeType project
      18   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
      19   * this file you indicate that you have read the license and
      20   * understand and accept it fully.
      21   *
      22   */
      23  
      24  #include <freetype/internal/ftmemory.h>
      25  #include <freetype/internal/ftstream.h>
      26  #include <freetype/internal/ftdebug.h>
      27  #include <freetype/ftlzw.h>
      28  #include FT_CONFIG_STANDARD_LIBRARY_H
      29  
      30  
      31  #include <freetype/ftmoderr.h>
      32  
      33  #undef FTERRORS_H_
      34  
      35  #undef  FT_ERR_PREFIX
      36  #define FT_ERR_PREFIX  LZW_Err_
      37  #define FT_ERR_BASE    FT_Mod_Err_LZW
      38  
      39  #include <freetype/fterrors.h>
      40  
      41  
      42  #ifdef FT_CONFIG_OPTION_USE_LZW
      43  
      44  #include "ftzopen.h"
      45  
      46  
      47  /***************************************************************************/
      48  /***************************************************************************/
      49  /*****                                                                 *****/
      50  /*****                  M E M O R Y   M A N A G E M E N T              *****/
      51  /*****                                                                 *****/
      52  /***************************************************************************/
      53  /***************************************************************************/
      54  
      55  /***************************************************************************/
      56  /***************************************************************************/
      57  /*****                                                                 *****/
      58  /*****                   F I L E   D E S C R I P T O R                 *****/
      59  /*****                                                                 *****/
      60  /***************************************************************************/
      61  /***************************************************************************/
      62  
      63  #define FT_LZW_BUFFER_SIZE  4096
      64  
      65    typedef struct  FT_LZWFileRec_
      66    {
      67      FT_Stream       source;         /* parent/source stream        */
      68      FT_Stream       stream;         /* embedding stream            */
      69      FT_Memory       memory;         /* memory allocator            */
      70      FT_LzwStateRec  lzw;            /* lzw decompressor state      */
      71  
      72      FT_Byte         buffer[FT_LZW_BUFFER_SIZE]; /* output buffer      */
      73      FT_ULong        pos;                        /* position in output */
      74      FT_Byte*        cursor;
      75      FT_Byte*        limit;
      76  
      77    } FT_LZWFileRec, *FT_LZWFile;
      78  
      79  
      80    /* check and skip .Z header */
      81    static FT_Error
      82    ft_lzw_check_header( FT_Stream  stream )
      83    {
      84      FT_Error  error;
      85      FT_Byte   head[2];
      86  
      87  
      88      if ( FT_STREAM_SEEK( 0 )       ||
      89           FT_STREAM_READ( head, 2 ) )
      90        goto Exit;
      91  
      92      /* head[0] && head[1] are the magic numbers */
      93      if ( head[0] != 0x1F ||
      94           head[1] != 0x9D )
      95        error = FT_THROW( Invalid_File_Format );
      96  
      97    Exit:
      98      return error;
      99    }
     100  
     101  
     102    static FT_Error
     103    ft_lzw_file_init( FT_LZWFile  zip,
     104                      FT_Stream   stream,
     105                      FT_Stream   source )
     106    {
     107      FT_LzwState  lzw   = &zip->lzw;
     108      FT_Error     error;
     109  
     110  
     111      zip->stream = stream;
     112      zip->source = source;
     113      zip->memory = stream->memory;
     114  
     115      zip->limit  = zip->buffer + FT_LZW_BUFFER_SIZE;
     116      zip->cursor = zip->limit;
     117      zip->pos    = 0;
     118  
     119      /* check and skip .Z header */
     120      error = ft_lzw_check_header( source );
     121      if ( error )
     122        goto Exit;
     123  
     124      /* initialize internal lzw variable */
     125      ft_lzwstate_init( lzw, source );
     126  
     127    Exit:
     128      return error;
     129    }
     130  
     131  
     132    static void
     133    ft_lzw_file_done( FT_LZWFile  zip )
     134    {
     135      /* clear the rest */
     136      ft_lzwstate_done( &zip->lzw );
     137  
     138      zip->memory = NULL;
     139      zip->source = NULL;
     140      zip->stream = NULL;
     141    }
     142  
     143  
     144    static FT_Error
     145    ft_lzw_file_reset( FT_LZWFile  zip )
     146    {
     147      FT_Stream  stream = zip->source;
     148      FT_Error   error;
     149  
     150  
     151      if ( !FT_STREAM_SEEK( 0 ) )
     152      {
     153        ft_lzwstate_reset( &zip->lzw );
     154  
     155        zip->limit  = zip->buffer + FT_LZW_BUFFER_SIZE;
     156        zip->cursor = zip->limit;
     157        zip->pos    = 0;
     158      }
     159  
     160      return error;
     161    }
     162  
     163  
     164    static FT_Error
     165    ft_lzw_file_fill_output( FT_LZWFile  zip )
     166    {
     167      FT_LzwState  lzw = &zip->lzw;
     168      FT_ULong     count;
     169      FT_Error     error = FT_Err_Ok;
     170  
     171  
     172      zip->cursor = zip->buffer;
     173  
     174      count = ft_lzwstate_io( lzw, zip->buffer, FT_LZW_BUFFER_SIZE );
     175  
     176      zip->limit = zip->cursor + count;
     177  
     178      if ( count == 0 )
     179        error = FT_THROW( Invalid_Stream_Operation );
     180  
     181      return error;
     182    }
     183  
     184  
     185    /* fill output buffer; `count' must be <= FT_LZW_BUFFER_SIZE */
     186    static FT_Error
     187    ft_lzw_file_skip_output( FT_LZWFile  zip,
     188                             FT_ULong    count )
     189    {
     190      FT_Error  error = FT_Err_Ok;
     191  
     192  
     193      /* first, we skip what we can from the output buffer */
     194      {
     195        FT_ULong  delta = (FT_ULong)( zip->limit - zip->cursor );
     196  
     197  
     198        if ( delta >= count )
     199          delta = count;
     200  
     201        zip->cursor += delta;
     202        zip->pos    += delta;
     203  
     204        count -= delta;
     205      }
     206  
     207      /* next, we skip as many bytes remaining as possible */
     208      while ( count > 0 )
     209      {
     210        FT_ULong  delta = FT_LZW_BUFFER_SIZE;
     211        FT_ULong  numread;
     212  
     213  
     214        if ( delta > count )
     215          delta = count;
     216  
     217        numread = ft_lzwstate_io( &zip->lzw, NULL, delta );
     218        if ( numread < delta )
     219        {
     220          /* not enough bytes */
     221          error = FT_THROW( Invalid_Stream_Operation );
     222          break;
     223        }
     224  
     225        zip->pos += delta;
     226        count    -= delta;
     227      }
     228  
     229      return error;
     230    }
     231  
     232  
     233    static FT_ULong
     234    ft_lzw_file_io( FT_LZWFile  zip,
     235                    FT_ULong    pos,
     236                    FT_Byte*    buffer,
     237                    FT_ULong    count )
     238    {
     239      FT_ULong  result = 0;
     240      FT_Error  error;
     241  
     242  
     243      /* seeking backwards. */
     244      if ( pos < zip->pos )
     245      {
     246        /* If the new position is within the output buffer, simply       */
     247        /* decrement pointers, otherwise we reset the stream completely! */
     248        if ( ( zip->pos - pos ) <= (FT_ULong)( zip->cursor - zip->buffer ) )
     249        {
     250          zip->cursor -= zip->pos - pos;
     251          zip->pos     = pos;
     252        }
     253        else
     254        {
     255          error = ft_lzw_file_reset( zip );
     256          if ( error )
     257            goto Exit;
     258        }
     259      }
     260  
     261      /* skip unwanted bytes */
     262      if ( pos > zip->pos )
     263      {
     264        error = ft_lzw_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
     265        if ( error )
     266          goto Exit;
     267      }
     268  
     269      if ( count == 0 )
     270        goto Exit;
     271  
     272      /* now read the data */
     273      for (;;)
     274      {
     275        FT_ULong  delta;
     276  
     277  
     278        delta = (FT_ULong)( zip->limit - zip->cursor );
     279        if ( delta >= count )
     280          delta = count;
     281  
     282        FT_MEM_COPY( buffer + result, zip->cursor, delta );
     283        result      += delta;
     284        zip->cursor += delta;
     285        zip->pos    += delta;
     286  
     287        count -= delta;
     288        if ( count == 0 )
     289          break;
     290  
     291        error = ft_lzw_file_fill_output( zip );
     292        if ( error )
     293          break;
     294      }
     295  
     296    Exit:
     297      return result;
     298    }
     299  
     300  
     301  /***************************************************************************/
     302  /***************************************************************************/
     303  /*****                                                                 *****/
     304  /*****            L Z W   E M B E D D I N G   S T R E A M              *****/
     305  /*****                                                                 *****/
     306  /***************************************************************************/
     307  /***************************************************************************/
     308  
     309    static void
     310    ft_lzw_stream_close( FT_Stream  stream )
     311    {
     312      FT_LZWFile  zip    = (FT_LZWFile)stream->descriptor.pointer;
     313      FT_Memory   memory = stream->memory;
     314  
     315  
     316      if ( zip )
     317      {
     318        /* finalize lzw file descriptor */
     319        ft_lzw_file_done( zip );
     320  
     321        FT_FREE( zip );
     322  
     323        stream->descriptor.pointer = NULL;
     324      }
     325    }
     326  
     327  
     328    static unsigned long
     329    ft_lzw_stream_io( FT_Stream       stream,
     330                      unsigned long   offset,
     331                      unsigned char*  buffer,
     332                      unsigned long   count )
     333    {
     334      FT_LZWFile  zip = (FT_LZWFile)stream->descriptor.pointer;
     335  
     336  
     337      return ft_lzw_file_io( zip, offset, buffer, count );
     338    }
     339  
     340  
     341    FT_EXPORT_DEF( FT_Error )
     342    FT_Stream_OpenLZW( FT_Stream  stream,
     343                       FT_Stream  source )
     344    {
     345      FT_Error    error;
     346      FT_Memory   memory;
     347      FT_LZWFile  zip = NULL;
     348  
     349  
     350      if ( !stream || !source )
     351      {
     352        error = FT_THROW( Invalid_Stream_Handle );
     353        goto Exit;
     354      }
     355  
     356      memory = source->memory;
     357  
     358      /*
     359       * Check the header right now; this prevents allocation of a huge
     360       * LZWFile object (400 KByte of heap memory) if not necessary.
     361       *
     362       * Did I mention that you should never use .Z compressed font
     363       * files?
     364       */
     365      error = ft_lzw_check_header( source );
     366      if ( error )
     367        goto Exit;
     368  
     369      FT_ZERO( stream );
     370      stream->memory = memory;
     371  
     372      if ( !FT_QNEW( zip ) )
     373      {
     374        error = ft_lzw_file_init( zip, stream, source );
     375        if ( error )
     376        {
     377          FT_FREE( zip );
     378          goto Exit;
     379        }
     380  
     381        stream->descriptor.pointer = zip;
     382      }
     383  
     384      stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
     385      stream->pos   = 0;
     386      stream->base  = NULL;
     387      stream->read  = ft_lzw_stream_io;
     388      stream->close = ft_lzw_stream_close;
     389  
     390    Exit:
     391      return error;
     392    }
     393  
     394  
     395  #include "ftzopen.c"
     396  
     397  
     398  #else  /* !FT_CONFIG_OPTION_USE_LZW */
     399  
     400  
     401    FT_EXPORT_DEF( FT_Error )
     402    FT_Stream_OpenLZW( FT_Stream  stream,
     403                       FT_Stream  source )
     404    {
     405      FT_UNUSED( stream );
     406      FT_UNUSED( source );
     407  
     408      return FT_THROW( Unimplemented_Feature );
     409    }
     410  
     411  
     412  #endif /* !FT_CONFIG_OPTION_USE_LZW */
     413  
     414  
     415  /* END */