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