ftgzip.c

Go to the documentation of this file.
00001 /***************************************************************************/
00002 /*                                                                         */
00003 /*  ftgzip.c                                                               */
00004 /*                                                                         */
00005 /*    FreeType support for .gz compressed files.                           */
00006 /*                                                                         */
00007 /*  This optional component relies on zlib.  It should mainly be used to   */
00008 /*  parse compressed PCF fonts, as found with many X11 server              */
00009 /*  distributions.                                                         */
00010 /*                                                                         */
00011 /*  Copyright 2002, 2003, 2004, 2005, 2006, 2009 by                        */
00012 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
00013 /*                                                                         */
00014 /*  This file is part of the FreeType project, and may only be used,       */
00015 /*  modified, and distributed under the terms of the FreeType project      */
00016 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
00017 /*  this file you indicate that you have read the license and              */
00018 /*  understand and accept it fully.                                        */
00019 /*                                                                         */
00020 /***************************************************************************/
00021 
00022 
00023 #include <ft2build.h>
00024 #include FT_INTERNAL_MEMORY_H
00025 #include FT_INTERNAL_STREAM_H
00026 #include FT_INTERNAL_DEBUG_H
00027 #include FT_GZIP_H
00028 #include FT_CONFIG_STANDARD_LIBRARY_H
00029 
00030 
00031 #include FT_MODULE_ERRORS_H
00032 
00033 #undef __FTERRORS_H__
00034 
00035 #define FT_ERR_PREFIX  Gzip_Err_
00036 #define FT_ERR_BASE    FT_Mod_Err_Gzip
00037 
00038 #include FT_ERRORS_H
00039 
00040 
00041 #ifdef FT_CONFIG_OPTION_USE_ZLIB
00042 
00043 #ifdef FT_CONFIG_OPTION_PIC
00044 #error "gzip code does not support PIC yet"
00045 #endif 
00046 
00047 #ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB
00048 
00049 #include <zlib.h>
00050 
00051 #else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
00052 
00053  /* In this case, we include our own modified sources of the ZLib    */
00054  /* within the "ftgzip" component.  The modifications were necessary */
00055  /* to #include all files without conflicts, as well as preventing   */
00056  /* the definition of "extern" functions that may cause linking      */
00057  /* conflicts when a program is linked with both FreeType and the    */
00058  /* original ZLib.                                                   */
00059 
00060 #define NO_DUMMY_DECL
00061 #ifndef USE_ZLIB_ZCALLOC
00062 #define MY_ZCALLOC /* prevent all zcalloc() & zfree() in zutils.c */
00063 #endif
00064 
00065 #include "zlib.h"
00066 
00067 #undef  SLOW
00068 #define SLOW  1  /* we can't use asm-optimized sources here! */
00069 
00070   /* Urgh.  `inflate_mask' must not be declared twice -- C++ doesn't like
00071      this.  We temporarily disable it and load all necessary header files. */
00072 #define NO_INFLATE_MASK
00073 #include "zutil.h"
00074 #include "inftrees.h"
00075 #include "infblock.h"
00076 #include "infcodes.h"
00077 #include "infutil.h"
00078 #undef  NO_INFLATE_MASK
00079 
00080   /* infutil.c must be included before infcodes.c */
00081 #include "zutil.c"
00082 #include "inftrees.c"
00083 #include "infutil.c"
00084 #include "infcodes.c"
00085 #include "infblock.c"
00086 #include "inflate.c"
00087 #include "adler32.c"
00088 
00089 #endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
00090 
00091 
00092 /***************************************************************************/
00093 /***************************************************************************/
00094 /*****                                                                 *****/
00095 /*****            Z L I B   M E M O R Y   M A N A G E M E N T          *****/
00096 /*****                                                                 *****/
00097 /***************************************************************************/
00098 /***************************************************************************/
00099 
00100   /* it is better to use FreeType memory routines instead of raw
00101      'malloc/free' */
00102 
00103   static voidpf
00104   ft_gzip_alloc( FT_Memory  memory,
00105                  uInt       items,
00106                  uInt       size )
00107   {
00108     FT_ULong    sz = (FT_ULong)size * items;
00109     FT_Error    error;
00110     FT_Pointer  p;
00111 
00112 
00113     (void)FT_ALLOC( p, sz );
00114     return p;
00115   }
00116 
00117 
00118   static void
00119   ft_gzip_free( FT_Memory  memory,
00120                 voidpf     address )
00121   {
00122     FT_MEM_FREE( address );
00123   }
00124 
00125 
00126 #if !defined( FT_CONFIG_OPTION_SYSTEM_ZLIB ) && !defined( USE_ZLIB_ZCALLOC )
00127 
00128   local voidpf
00129   zcalloc ( voidpf    opaque,
00130             unsigned  items,
00131             unsigned  size )
00132   {
00133     return ft_gzip_alloc( (FT_Memory)opaque, items, size );
00134   }
00135 
00136   local void
00137   zcfree( voidpf  opaque,
00138           voidpf  ptr )
00139   {
00140     ft_gzip_free( (FT_Memory)opaque, ptr );
00141   }
00142 
00143 #endif /* !SYSTEM_ZLIB && !USE_ZLIB_ZCALLOC */
00144 
00145 
00146 /***************************************************************************/
00147 /***************************************************************************/
00148 /*****                                                                 *****/
00149 /*****               Z L I B   F I L E   D E S C R I P T O R           *****/
00150 /*****                                                                 *****/
00151 /***************************************************************************/
00152 /***************************************************************************/
00153 
00154 #define FT_GZIP_BUFFER_SIZE  4096
00155 
00156   typedef struct  FT_GZipFileRec_
00157   {
00158     FT_Stream  source;         /* parent/source stream        */
00159     FT_Stream  stream;         /* embedding stream            */
00160     FT_Memory  memory;         /* memory allocator            */
00161     z_stream   zstream;        /* zlib input stream           */
00162 
00163     FT_ULong   start;          /* starting position, after .gz header */
00164     FT_Byte    input[FT_GZIP_BUFFER_SIZE];   /* input read buffer  */
00165 
00166     FT_Byte    buffer[FT_GZIP_BUFFER_SIZE];  /* output buffer      */
00167     FT_ULong   pos;                          /* position in output */
00168     FT_Byte*   cursor;
00169     FT_Byte*   limit;
00170 
00171   } FT_GZipFileRec, *FT_GZipFile;
00172 
00173 
00174   /* gzip flag byte */
00175 #define FT_GZIP_ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
00176 #define FT_GZIP_HEAD_CRC     0x02 /* bit 1 set: header CRC present */
00177 #define FT_GZIP_EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
00178 #define FT_GZIP_ORIG_NAME    0x08 /* bit 3 set: original file name present */
00179 #define FT_GZIP_COMMENT      0x10 /* bit 4 set: file comment present */
00180 #define FT_GZIP_RESERVED     0xE0 /* bits 5..7: reserved */
00181 
00182 
00183   /* check and skip .gz header - we don't support `transparent' compression */
00184   static FT_Error
00185   ft_gzip_check_header( FT_Stream  stream )
00186   {
00187     FT_Error  error;
00188     FT_Byte   head[4];
00189 
00190 
00191     if ( FT_STREAM_SEEK( 0 )       ||
00192          FT_STREAM_READ( head, 4 ) )
00193       goto Exit;
00194 
00195     /* head[0] && head[1] are the magic numbers;    */
00196     /* head[2] is the method, and head[3] the flags */
00197     if ( head[0] != 0x1f              ||
00198          head[1] != 0x8b              ||
00199          head[2] != Z_DEFLATED        ||
00200         (head[3] & FT_GZIP_RESERVED)  )
00201     {
00202       error = Gzip_Err_Invalid_File_Format;
00203       goto Exit;
00204     }
00205 
00206     /* skip time, xflags and os code */
00207     (void)FT_STREAM_SKIP( 6 );
00208 
00209     /* skip the extra field */
00210     if ( head[3] & FT_GZIP_EXTRA_FIELD )
00211     {
00212       FT_UInt  len;
00213 
00214 
00215       if ( FT_READ_USHORT_LE( len ) ||
00216            FT_STREAM_SKIP( len )    )
00217         goto Exit;
00218     }
00219 
00220     /* skip original file name */
00221     if ( head[3] & FT_GZIP_ORIG_NAME )
00222       for (;;)
00223       {
00224         FT_UInt  c;
00225 
00226 
00227         if ( FT_READ_BYTE( c ) )
00228           goto Exit;
00229 
00230         if ( c == 0 )
00231           break;
00232       }
00233 
00234     /* skip .gz comment */
00235     if ( head[3] & FT_GZIP_COMMENT )
00236       for (;;)
00237       {
00238         FT_UInt  c;
00239 
00240 
00241         if ( FT_READ_BYTE( c ) )
00242           goto Exit;
00243 
00244         if ( c == 0 )
00245           break;
00246       }
00247 
00248     /* skip CRC */
00249     if ( head[3] & FT_GZIP_HEAD_CRC )
00250       if ( FT_STREAM_SKIP( 2 ) )
00251         goto Exit;
00252 
00253   Exit:
00254     return error;
00255   }
00256 
00257 
00258   static FT_Error
00259   ft_gzip_file_init( FT_GZipFile  zip,
00260                      FT_Stream    stream,
00261                      FT_Stream    source )
00262   {
00263     z_stream*  zstream = &zip->zstream;
00264     FT_Error   error   = Gzip_Err_Ok;
00265 
00266 
00267     zip->stream = stream;
00268     zip->source = source;
00269     zip->memory = stream->memory;
00270 
00271     zip->limit  = zip->buffer + FT_GZIP_BUFFER_SIZE;
00272     zip->cursor = zip->limit;
00273     zip->pos    = 0;
00274 
00275     /* check and skip .gz header */
00276     {
00277       stream = source;
00278 
00279       error = ft_gzip_check_header( stream );
00280       if ( error )
00281         goto Exit;
00282 
00283       zip->start = FT_STREAM_POS();
00284     }
00285 
00286     /* initialize zlib -- there is no zlib header in the compressed stream */
00287     zstream->zalloc = (alloc_func)ft_gzip_alloc;
00288     zstream->zfree  = (free_func) ft_gzip_free;
00289     zstream->opaque = stream->memory;
00290 
00291     zstream->avail_in = 0;
00292     zstream->next_in  = zip->buffer;
00293 
00294     if ( inflateInit2( zstream, -MAX_WBITS ) != Z_OK ||
00295          zstream->next_in == NULL                     )
00296       error = Gzip_Err_Invalid_File_Format;
00297 
00298   Exit:
00299     return error;
00300   }
00301 
00302 
00303   static void
00304   ft_gzip_file_done( FT_GZipFile  zip )
00305   {
00306     z_stream*  zstream = &zip->zstream;
00307 
00308 
00309     inflateEnd( zstream );
00310 
00311     /* clear the rest */
00312     zstream->zalloc    = NULL;
00313     zstream->zfree     = NULL;
00314     zstream->opaque    = NULL;
00315     zstream->next_in   = NULL;
00316     zstream->next_out  = NULL;
00317     zstream->avail_in  = 0;
00318     zstream->avail_out = 0;
00319 
00320     zip->memory = NULL;
00321     zip->source = NULL;
00322     zip->stream = NULL;
00323   }
00324 
00325 
00326   static FT_Error
00327   ft_gzip_file_reset( FT_GZipFile  zip )
00328   {
00329     FT_Stream  stream = zip->source;
00330     FT_Error   error;
00331 
00332 
00333     if ( !FT_STREAM_SEEK( zip->start ) )
00334     {
00335       z_stream*  zstream = &zip->zstream;
00336 
00337 
00338       inflateReset( zstream );
00339 
00340       zstream->avail_in  = 0;
00341       zstream->next_in   = zip->input;
00342       zstream->avail_out = 0;
00343       zstream->next_out  = zip->buffer;
00344 
00345       zip->limit  = zip->buffer + FT_GZIP_BUFFER_SIZE;
00346       zip->cursor = zip->limit;
00347       zip->pos    = 0;
00348     }
00349 
00350     return error;
00351   }
00352 
00353 
00354   static FT_Error
00355   ft_gzip_file_fill_input( FT_GZipFile  zip )
00356   {
00357     z_stream*  zstream = &zip->zstream;
00358     FT_Stream  stream  = zip->source;
00359     FT_ULong   size;
00360 
00361 
00362     if ( stream->read )
00363     {
00364       size = stream->read( stream, stream->pos, zip->input,
00365                            FT_GZIP_BUFFER_SIZE );
00366       if ( size == 0 )
00367         return Gzip_Err_Invalid_Stream_Operation;
00368     }
00369     else
00370     {
00371       size = stream->size - stream->pos;
00372       if ( size > FT_GZIP_BUFFER_SIZE )
00373         size = FT_GZIP_BUFFER_SIZE;
00374 
00375       if ( size == 0 )
00376         return Gzip_Err_Invalid_Stream_Operation;
00377 
00378       FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
00379     }
00380     stream->pos += size;
00381 
00382     zstream->next_in  = zip->input;
00383     zstream->avail_in = size;
00384 
00385     return Gzip_Err_Ok;
00386   }
00387 
00388 
00389   static FT_Error
00390   ft_gzip_file_fill_output( FT_GZipFile  zip )
00391   {
00392     z_stream*  zstream = &zip->zstream;
00393     FT_Error   error   = 0;
00394 
00395 
00396     zip->cursor        = zip->buffer;
00397     zstream->next_out  = zip->cursor;
00398     zstream->avail_out = FT_GZIP_BUFFER_SIZE;
00399 
00400     while ( zstream->avail_out > 0 )
00401     {
00402       int  err;
00403 
00404 
00405       if ( zstream->avail_in == 0 )
00406       {
00407         error = ft_gzip_file_fill_input( zip );
00408         if ( error )
00409           break;
00410       }
00411 
00412       err = inflate( zstream, Z_NO_FLUSH );
00413 
00414       if ( err == Z_STREAM_END )
00415       {
00416         zip->limit = zstream->next_out;
00417         if ( zip->limit == zip->cursor )
00418           error = Gzip_Err_Invalid_Stream_Operation;
00419         break;
00420       }
00421       else if ( err != Z_OK )
00422       {
00423         error = Gzip_Err_Invalid_Stream_Operation;
00424         break;
00425       }
00426     }
00427 
00428     return error;
00429   }
00430 
00431 
00432   /* fill output buffer; `count' must be <= FT_GZIP_BUFFER_SIZE */
00433   static FT_Error
00434   ft_gzip_file_skip_output( FT_GZipFile  zip,
00435                             FT_ULong     count )
00436   {
00437     FT_Error  error = Gzip_Err_Ok;
00438     FT_ULong  delta;
00439 
00440 
00441     for (;;)
00442     {
00443       delta = (FT_ULong)( zip->limit - zip->cursor );
00444       if ( delta >= count )
00445         delta = count;
00446 
00447       zip->cursor += delta;
00448       zip->pos    += delta;
00449 
00450       count -= delta;
00451       if ( count == 0 )
00452         break;
00453 
00454       error = ft_gzip_file_fill_output( zip );
00455       if ( error )
00456         break;
00457     }
00458 
00459     return error;
00460   }
00461 
00462 
00463   static FT_ULong
00464   ft_gzip_file_io( FT_GZipFile  zip,
00465                    FT_ULong     pos,
00466                    FT_Byte*     buffer,
00467                    FT_ULong     count )
00468   {
00469     FT_ULong  result = 0;
00470     FT_Error  error;
00471 
00472 
00473     /* Reset inflate stream if we're seeking backwards.        */
00474     /* Yes, that is not too efficient, but it saves memory :-) */
00475     if ( pos < zip->pos )
00476     {
00477       error = ft_gzip_file_reset( zip );
00478       if ( error )
00479         goto Exit;
00480     }
00481 
00482     /* skip unwanted bytes */
00483     if ( pos > zip->pos )
00484     {
00485       error = ft_gzip_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
00486       if ( error )
00487         goto Exit;
00488     }
00489 
00490     if ( count == 0 )
00491       goto Exit;
00492 
00493     /* now read the data */
00494     for (;;)
00495     {
00496       FT_ULong  delta;
00497 
00498 
00499       delta = (FT_ULong)( zip->limit - zip->cursor );
00500       if ( delta >= count )
00501         delta = count;
00502 
00503       FT_MEM_COPY( buffer, zip->cursor, delta );
00504       buffer      += delta;
00505       result      += delta;
00506       zip->cursor += delta;
00507       zip->pos    += delta;
00508 
00509       count -= delta;
00510       if ( count == 0 )
00511         break;
00512 
00513       error = ft_gzip_file_fill_output( zip );
00514       if ( error )
00515         break;
00516     }
00517 
00518   Exit:
00519     return result;
00520   }
00521 
00522 
00523 /***************************************************************************/
00524 /***************************************************************************/
00525 /*****                                                                 *****/
00526 /*****               G Z   E M B E D D I N G   S T R E A M             *****/
00527 /*****                                                                 *****/
00528 /***************************************************************************/
00529 /***************************************************************************/
00530 
00531   static void
00532   ft_gzip_stream_close( FT_Stream  stream )
00533   {
00534     FT_GZipFile  zip    = (FT_GZipFile)stream->descriptor.pointer;
00535     FT_Memory    memory = stream->memory;
00536 
00537 
00538     if ( zip )
00539     {
00540       /* finalize gzip file descriptor */
00541       ft_gzip_file_done( zip );
00542 
00543       FT_FREE( zip );
00544 
00545       stream->descriptor.pointer = NULL;
00546     }
00547   }
00548 
00549 
00550   static FT_ULong
00551   ft_gzip_stream_io( FT_Stream  stream,
00552                      FT_ULong   pos,
00553                      FT_Byte*   buffer,
00554                      FT_ULong   count )
00555   {
00556     FT_GZipFile  zip = (FT_GZipFile)stream->descriptor.pointer;
00557 
00558 
00559     return ft_gzip_file_io( zip, pos, buffer, count );
00560   }
00561 
00562 
00563   static FT_ULong
00564   ft_gzip_get_uncompressed_size( FT_Stream  stream )
00565   {
00566     FT_Error  error;
00567     FT_ULong  old_pos;
00568     FT_ULong  result = 0;
00569 
00570 
00571     old_pos = stream->pos;
00572     if ( !FT_Stream_Seek( stream, stream->size - 4 ) )
00573     {
00574       result = (FT_ULong)FT_Stream_ReadLong( stream, &error );
00575       if ( error )
00576         result = 0;
00577 
00578       (void)FT_Stream_Seek( stream, old_pos );
00579     }
00580 
00581     return result;
00582   }
00583 
00584 
00585   FT_EXPORT_DEF( FT_Error )
00586   FT_Stream_OpenGzip( FT_Stream  stream,
00587                       FT_Stream  source )
00588   {
00589     FT_Error     error;
00590     FT_Memory    memory = source->memory;
00591     FT_GZipFile  zip;
00592 
00593 
00594     /*
00595      *  check the header right now; this prevents allocating un-necessary
00596      *  objects when we don't need them
00597      */
00598     error = ft_gzip_check_header( source );
00599     if ( error )
00600       goto Exit;
00601 
00602     FT_ZERO( stream );
00603     stream->memory = memory;
00604 
00605     if ( !FT_QNEW( zip ) )
00606     {
00607       error = ft_gzip_file_init( zip, stream, source );
00608       if ( error )
00609       {
00610         FT_FREE( zip );
00611         goto Exit;
00612       }
00613 
00614       stream->descriptor.pointer = zip;
00615     }
00616 
00617     /*
00618      *  We use the following trick to try to dramatically improve the
00619      *  performance while dealing with small files.  If the original stream
00620      *  size is less than a certain threshold, we try to load the whole font
00621      *  file into memory.  This saves us from using the 32KB buffer needed
00622      *  to inflate the file, plus the two 4KB intermediate input/output
00623      *  buffers used in the `FT_GZipFile' structure.
00624      */
00625     {
00626       FT_ULong  zip_size = ft_gzip_get_uncompressed_size( source );
00627 
00628 
00629       if ( zip_size != 0 && zip_size < 40 * 1024 )
00630       {
00631         FT_Byte*  zip_buff;
00632 
00633 
00634         if ( !FT_ALLOC( zip_buff, zip_size ) )
00635         {
00636           FT_ULong  count;
00637 
00638 
00639           count = ft_gzip_file_io( zip, 0, zip_buff, zip_size );
00640           if ( count == zip_size )
00641           {
00642             ft_gzip_file_done( zip );
00643             FT_FREE( zip );
00644 
00645             stream->descriptor.pointer = NULL;
00646 
00647             stream->size  = zip_size;
00648             stream->pos   = 0;
00649             stream->base  = zip_buff;
00650             stream->read  = NULL;
00651             stream->close = ft_gzip_stream_close;
00652 
00653             goto Exit;
00654           }
00655 
00656           ft_gzip_file_io( zip, 0, NULL, 0 );
00657           FT_FREE( zip_buff );
00658         }
00659         error = 0;
00660       }
00661     }
00662 
00663     stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
00664     stream->pos   = 0;
00665     stream->base  = 0;
00666     stream->read  = ft_gzip_stream_io;
00667     stream->close = ft_gzip_stream_close;
00668 
00669   Exit:
00670     return error;
00671   }
00672 
00673 #else  /* !FT_CONFIG_OPTION_USE_ZLIB */
00674 
00675   FT_EXPORT_DEF( FT_Error )
00676   FT_Stream_OpenGzip( FT_Stream  stream,
00677                       FT_Stream  source )
00678   {
00679     FT_UNUSED( stream );
00680     FT_UNUSED( source );
00681 
00682     return Gzip_Err_Unimplemented_Feature;
00683   }
00684 
00685 #endif /* !FT_CONFIG_OPTION_USE_ZLIB */
00686 
00687 
00688 /* END */

Generated on Tue Jul 5 14:13:47 2011 for ROOT_528-00b_version by  doxygen 1.5.1