source: trunk/poppler/freetype2/src/gzip/ftgzip.c @ 269

Last change on this file since 269 was 269, checked in by Eugene Romanenko, 13 years ago

PDF plugin: freetype library updated to version 2.3.9

File size: 17.6 KB
Line 
1/***************************************************************************/
2/*                                                                         */
3/*  ftgzip.c                                                               */
4/*                                                                         */
5/*    FreeType support for .gz compressed files.                           */
6/*                                                                         */
7/*  This optional component relies on zlib.  It should mainly be used to   */
8/*  parse compressed PCF fonts, as found with many X11 server              */
9/*  distributions.                                                         */
10/*                                                                         */
11/*  Copyright 2002, 2003, 2004, 2005, 2006, 2009 by                        */
12/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
13/*                                                                         */
14/*  This file is part of the FreeType project, and may only be used,       */
15/*  modified, and distributed under the terms of the FreeType project      */
16/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
17/*  this file you indicate that you have read the license and              */
18/*  understand and accept it fully.                                        */
19/*                                                                         */
20/***************************************************************************/
21
22
23#include <ft2build.h>
24#include FT_INTERNAL_MEMORY_H
25#include FT_INTERNAL_STREAM_H
26#include FT_INTERNAL_DEBUG_H
27#include FT_GZIP_H
28#include FT_CONFIG_STANDARD_LIBRARY_H
29
30
31#include FT_MODULE_ERRORS_H
32
33#undef __FTERRORS_H__
34
35#define FT_ERR_PREFIX  Gzip_Err_
36#define FT_ERR_BASE    FT_Mod_Err_Gzip
37
38#include FT_ERRORS_H
39
40
41#ifdef FT_CONFIG_OPTION_USE_ZLIB
42
43#ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB
44
45#include <zlib.h>
46
47#else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
48
49 /* In this case, we include our own modified sources of the ZLib    */
50 /* within the "ftgzip" component.  The modifications were necessary */
51 /* to #include all files without conflicts, as well as preventing   */
52 /* the definition of "extern" functions that may cause linking      */
53 /* conflicts when a program is linked with both FreeType and the    */
54 /* original ZLib.                                                   */
55
56#define NO_DUMMY_DECL
57#define MY_ZCALLOC
58
59#include "zlib.h"
60
61#undef  SLOW
62#define SLOW  1  /* we can't use asm-optimized sources here! */
63
64  /* Urgh.  `inflate_mask' must not be declared twice -- C++ doesn't like
65     this.  We temporarily disable it and load all necessary header files. */
66#define NO_INFLATE_MASK
67#include "zutil.h"
68#include "inftrees.h"
69#include "infblock.h"
70#include "infcodes.h"
71#include "infutil.h"
72#undef  NO_INFLATE_MASK
73
74  /* infutil.c must be included before infcodes.c */
75#include "zutil.c"
76#include "inftrees.c"
77#include "infutil.c"
78#include "infcodes.c"
79#include "infblock.c"
80#include "inflate.c"
81#include "adler32.c"
82
83#endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
84
85
86/***************************************************************************/
87/***************************************************************************/
88/*****                                                                 *****/
89/*****            Z L I B   M E M O R Y   M A N A G E M E N T          *****/
90/*****                                                                 *****/
91/***************************************************************************/
92/***************************************************************************/
93
94  /* it is better to use FreeType memory routines instead of raw
95     'malloc/free' */
96
97  static voidpf
98  ft_gzip_alloc( FT_Memory  memory,
99                 uInt       items,
100                 uInt       size )
101  {
102    FT_ULong    sz = (FT_ULong)size * items;
103    FT_Error    error;
104    FT_Pointer  p;
105
106
107    (void)FT_ALLOC( p, sz );
108    return p;
109  }
110
111
112  static void
113  ft_gzip_free( FT_Memory  memory,
114                voidpf     address )
115  {
116    FT_MEM_FREE( address );
117  }
118
119
120#ifndef FT_CONFIG_OPTION_SYSTEM_ZLIB
121
122  local voidpf
123  zcalloc ( voidpf    opaque,
124            unsigned  items,
125            unsigned  size )
126  {
127    return ft_gzip_alloc( (FT_Memory)opaque, items, size );
128  }
129
130  local void
131  zcfree( voidpf  opaque,
132          voidpf  ptr )
133  {
134    ft_gzip_free( (FT_Memory)opaque, ptr );
135  }
136
137#endif /* !SYSTEM_ZLIB */
138
139
140/***************************************************************************/
141/***************************************************************************/
142/*****                                                                 *****/
143/*****               Z L I B   F I L E   D E S C R I P T O R           *****/
144/*****                                                                 *****/
145/***************************************************************************/
146/***************************************************************************/
147
148#define FT_GZIP_BUFFER_SIZE  4096
149
150  typedef struct  FT_GZipFileRec_
151  {
152    FT_Stream  source;         /* parent/source stream        */
153    FT_Stream  stream;         /* embedding stream            */
154    FT_Memory  memory;         /* memory allocator            */
155    z_stream   zstream;        /* zlib input stream           */
156
157    FT_ULong   start;          /* starting position, after .gz header */
158    FT_Byte    input[FT_GZIP_BUFFER_SIZE];   /* input read buffer  */
159
160    FT_Byte    buffer[FT_GZIP_BUFFER_SIZE];  /* output buffer      */
161    FT_ULong   pos;                          /* position in output */
162    FT_Byte*   cursor;
163    FT_Byte*   limit;
164
165  } FT_GZipFileRec, *FT_GZipFile;
166
167
168  /* gzip flag byte */
169#define FT_GZIP_ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
170#define FT_GZIP_HEAD_CRC     0x02 /* bit 1 set: header CRC present */
171#define FT_GZIP_EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
172#define FT_GZIP_ORIG_NAME    0x08 /* bit 3 set: original file name present */
173#define FT_GZIP_COMMENT      0x10 /* bit 4 set: file comment present */
174#define FT_GZIP_RESERVED     0xE0 /* bits 5..7: reserved */
175
176
177  /* check and skip .gz header - we don't support `transparent' compression */
178  static FT_Error
179  ft_gzip_check_header( FT_Stream  stream )
180  {
181    FT_Error  error;
182    FT_Byte   head[4];
183
184
185    if ( FT_STREAM_SEEK( 0 )       ||
186         FT_STREAM_READ( head, 4 ) )
187      goto Exit;
188
189    /* head[0] && head[1] are the magic numbers;    */
190    /* head[2] is the method, and head[3] the flags */
191    if ( head[0] != 0x1f              ||
192         head[1] != 0x8b              ||
193         head[2] != Z_DEFLATED        ||
194        (head[3] & FT_GZIP_RESERVED)  )
195    {
196      error = Gzip_Err_Invalid_File_Format;
197      goto Exit;
198    }
199
200    /* skip time, xflags and os code */
201    (void)FT_STREAM_SKIP( 6 );
202
203    /* skip the extra field */
204    if ( head[3] & FT_GZIP_EXTRA_FIELD )
205    {
206      FT_UInt  len;
207
208
209      if ( FT_READ_USHORT_LE( len ) ||
210           FT_STREAM_SKIP( len )    )
211        goto Exit;
212    }
213
214    /* skip original file name */
215    if ( head[3] & FT_GZIP_ORIG_NAME )
216      for (;;)
217      {
218        FT_UInt  c;
219
220
221        if ( FT_READ_BYTE( c ) )
222          goto Exit;
223
224        if ( c == 0 )
225          break;
226      }
227
228    /* skip .gz comment */
229    if ( head[3] & FT_GZIP_COMMENT )
230      for (;;)
231      {
232        FT_UInt  c;
233
234
235        if ( FT_READ_BYTE( c ) )
236          goto Exit;
237
238        if ( c == 0 )
239          break;
240      }
241
242    /* skip CRC */
243    if ( head[3] & FT_GZIP_HEAD_CRC )
244      if ( FT_STREAM_SKIP( 2 ) )
245        goto Exit;
246
247  Exit:
248    return error;
249  }
250
251
252  static FT_Error
253  ft_gzip_file_init( FT_GZipFile  zip,
254                     FT_Stream    stream,
255                     FT_Stream    source )
256  {
257    z_stream*  zstream = &zip->zstream;
258    FT_Error   error   = Gzip_Err_Ok;
259
260
261    zip->stream = stream;
262    zip->source = source;
263    zip->memory = stream->memory;
264
265    zip->limit  = zip->buffer + FT_GZIP_BUFFER_SIZE;
266    zip->cursor = zip->limit;
267    zip->pos    = 0;
268
269    /* check and skip .gz header */
270    {
271      stream = source;
272
273      error = ft_gzip_check_header( stream );
274      if ( error )
275        goto Exit;
276
277      zip->start = FT_STREAM_POS();
278    }
279
280    /* initialize zlib -- there is no zlib header in the compressed stream */
281    zstream->zalloc = (alloc_func)ft_gzip_alloc;
282    zstream->zfree  = (free_func) ft_gzip_free;
283    zstream->opaque = stream->memory;
284
285    zstream->avail_in = 0;
286    zstream->next_in  = zip->buffer;
287
288    if ( inflateInit2( zstream, -MAX_WBITS ) != Z_OK ||
289         zstream->next_in == NULL                     )
290      error = Gzip_Err_Invalid_File_Format;
291
292  Exit:
293    return error;
294  }
295
296
297  static void
298  ft_gzip_file_done( FT_GZipFile  zip )
299  {
300    z_stream*  zstream = &zip->zstream;
301
302
303    inflateEnd( zstream );
304
305    /* clear the rest */
306    zstream->zalloc    = NULL;
307    zstream->zfree     = NULL;
308    zstream->opaque    = NULL;
309    zstream->next_in   = NULL;
310    zstream->next_out  = NULL;
311    zstream->avail_in  = 0;
312    zstream->avail_out = 0;
313
314    zip->memory = NULL;
315    zip->source = NULL;
316    zip->stream = NULL;
317  }
318
319
320  static FT_Error
321  ft_gzip_file_reset( FT_GZipFile  zip )
322  {
323    FT_Stream  stream = zip->source;
324    FT_Error   error;
325
326
327    if ( !FT_STREAM_SEEK( zip->start ) )
328    {
329      z_stream*  zstream = &zip->zstream;
330
331
332      inflateReset( zstream );
333
334      zstream->avail_in  = 0;
335      zstream->next_in   = zip->input;
336      zstream->avail_out = 0;
337      zstream->next_out  = zip->buffer;
338
339      zip->limit  = zip->buffer + FT_GZIP_BUFFER_SIZE;
340      zip->cursor = zip->limit;
341      zip->pos    = 0;
342    }
343
344    return error;
345  }
346
347
348  static FT_Error
349  ft_gzip_file_fill_input( FT_GZipFile  zip )
350  {
351    z_stream*  zstream = &zip->zstream;
352    FT_Stream  stream  = zip->source;
353    FT_ULong   size;
354
355
356    if ( stream->read )
357    {
358      size = stream->read( stream, stream->pos, zip->input,
359                           FT_GZIP_BUFFER_SIZE );
360      if ( size == 0 )
361        return Gzip_Err_Invalid_Stream_Operation;
362    }
363    else
364    {
365      size = stream->size - stream->pos;
366      if ( size > FT_GZIP_BUFFER_SIZE )
367        size = FT_GZIP_BUFFER_SIZE;
368
369      if ( size == 0 )
370        return Gzip_Err_Invalid_Stream_Operation;
371
372      FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
373    }
374    stream->pos += size;
375
376    zstream->next_in  = zip->input;
377    zstream->avail_in = size;
378
379    return Gzip_Err_Ok;
380  }
381
382
383  static FT_Error
384  ft_gzip_file_fill_output( FT_GZipFile  zip )
385  {
386    z_stream*  zstream = &zip->zstream;
387    FT_Error   error   = 0;
388
389
390    zip->cursor        = zip->buffer;
391    zstream->next_out  = zip->cursor;
392    zstream->avail_out = FT_GZIP_BUFFER_SIZE;
393
394    while ( zstream->avail_out > 0 )
395    {
396      int  err;
397
398
399      if ( zstream->avail_in == 0 )
400      {
401        error = ft_gzip_file_fill_input( zip );
402        if ( error )
403          break;
404      }
405
406      err = inflate( zstream, Z_NO_FLUSH );
407
408      if ( err == Z_STREAM_END )
409      {
410        zip->limit = zstream->next_out;
411        if ( zip->limit == zip->cursor )
412          error = Gzip_Err_Invalid_Stream_Operation;
413        break;
414      }
415      else if ( err != Z_OK )
416      {
417        error = Gzip_Err_Invalid_Stream_Operation;
418        break;
419      }
420    }
421
422    return error;
423  }
424
425
426  /* fill output buffer; `count' must be <= FT_GZIP_BUFFER_SIZE */
427  static FT_Error
428  ft_gzip_file_skip_output( FT_GZipFile  zip,
429                            FT_ULong     count )
430  {
431    FT_Error  error = Gzip_Err_Ok;
432    FT_ULong  delta;
433
434
435    for (;;)
436    {
437      delta = (FT_ULong)( zip->limit - zip->cursor );
438      if ( delta >= count )
439        delta = count;
440
441      zip->cursor += delta;
442      zip->pos    += delta;
443
444      count -= delta;
445      if ( count == 0 )
446        break;
447
448      error = ft_gzip_file_fill_output( zip );
449      if ( error )
450        break;
451    }
452
453    return error;
454  }
455
456
457  static FT_ULong
458  ft_gzip_file_io( FT_GZipFile  zip,
459                   FT_ULong     pos,
460                   FT_Byte*     buffer,
461                   FT_ULong     count )
462  {
463    FT_ULong  result = 0;
464    FT_Error  error;
465
466
467    /* Reset inflate stream if we're seeking backwards.        */
468    /* Yes, that is not too efficient, but it saves memory :-) */
469    if ( pos < zip->pos )
470    {
471      error = ft_gzip_file_reset( zip );
472      if ( error )
473        goto Exit;
474    }
475
476    /* skip unwanted bytes */
477    if ( pos > zip->pos )
478    {
479      error = ft_gzip_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
480      if ( error )
481        goto Exit;
482    }
483
484    if ( count == 0 )
485      goto Exit;
486
487    /* now read the data */
488    for (;;)
489    {
490      FT_ULong  delta;
491
492
493      delta = (FT_ULong)( zip->limit - zip->cursor );
494      if ( delta >= count )
495        delta = count;
496
497      FT_MEM_COPY( buffer, zip->cursor, delta );
498      buffer      += delta;
499      result      += delta;
500      zip->cursor += delta;
501      zip->pos    += delta;
502
503      count -= delta;
504      if ( count == 0 )
505        break;
506
507      error = ft_gzip_file_fill_output( zip );
508      if ( error )
509        break;
510    }
511
512  Exit:
513    return result;
514  }
515
516
517/***************************************************************************/
518/***************************************************************************/
519/*****                                                                 *****/
520/*****               G Z   E M B E D D I N G   S T R E A M             *****/
521/*****                                                                 *****/
522/***************************************************************************/
523/***************************************************************************/
524
525  static void
526  ft_gzip_stream_close( FT_Stream  stream )
527  {
528    FT_GZipFile  zip    = (FT_GZipFile)stream->descriptor.pointer;
529    FT_Memory    memory = stream->memory;
530
531
532    if ( zip )
533    {
534      /* finalize gzip file descriptor */
535      ft_gzip_file_done( zip );
536
537      FT_FREE( zip );
538
539      stream->descriptor.pointer = NULL;
540    }
541  }
542
543
544  static FT_ULong
545  ft_gzip_stream_io( FT_Stream  stream,
546                     FT_ULong   pos,
547                     FT_Byte*   buffer,
548                     FT_ULong   count )
549  {
550    FT_GZipFile  zip = (FT_GZipFile)stream->descriptor.pointer;
551
552
553    return ft_gzip_file_io( zip, pos, buffer, count );
554  }
555
556
557  static FT_ULong
558  ft_gzip_get_uncompressed_size( FT_Stream  stream )
559  {
560    FT_Error  error;
561    FT_ULong  old_pos;
562    FT_ULong  result = 0;
563
564
565    old_pos = stream->pos;
566    if ( !FT_Stream_Seek( stream, stream->size - 4 ) )
567    {
568      result = (FT_ULong)FT_Stream_ReadLong( stream, &error );
569      if ( error )
570        result = 0;
571
572      (void)FT_Stream_Seek( stream, old_pos );
573    }
574
575    return result;
576  }
577
578
579  FT_EXPORT_DEF( FT_Error )
580  FT_Stream_OpenGzip( FT_Stream  stream,
581                      FT_Stream  source )
582  {
583    FT_Error     error;
584    FT_Memory    memory = source->memory;
585    FT_GZipFile  zip;
586
587
588    /*
589     *  check the header right now; this prevents allocating un-necessary
590     *  objects when we don't need them
591     */
592    error = ft_gzip_check_header( source );
593    if ( error )
594      goto Exit;
595
596    FT_ZERO( stream );
597    stream->memory = memory;
598
599    if ( !FT_QNEW( zip ) )
600    {
601      error = ft_gzip_file_init( zip, stream, source );
602      if ( error )
603      {
604        FT_FREE( zip );
605        goto Exit;
606      }
607
608      stream->descriptor.pointer = zip;
609    }
610
611    /*
612     *  We use the following trick to try to dramatically improve the
613     *  performance while dealing with small files.  If the original stream
614     *  size is less than a certain threshold, we try to load the whole font
615     *  file into memory.  This saves us from using the 32KB buffer needed
616     *  to inflate the file, plus the two 4KB intermediate input/output
617     *  buffers used in the `FT_GZipFile' structure.
618     */
619    {
620      FT_ULong  zip_size = ft_gzip_get_uncompressed_size( source );
621
622
623      if ( zip_size != 0 && zip_size < 40 * 1024 )
624      {
625        FT_Byte*  zip_buff;
626
627
628        if ( !FT_ALLOC( zip_buff, zip_size ) )
629        {
630          FT_ULong  count;
631
632
633          count = ft_gzip_file_io( zip, 0, zip_buff, zip_size );
634          if ( count == zip_size )
635          {
636            ft_gzip_file_done( zip );
637            FT_FREE( zip );
638
639            stream->descriptor.pointer = NULL;
640
641            stream->size  = zip_size;
642            stream->pos   = 0;
643            stream->base  = zip_buff;
644            stream->read  = NULL;
645            stream->close = ft_gzip_stream_close;
646
647            goto Exit;
648          }
649
650          ft_gzip_file_io( zip, 0, NULL, 0 );
651          FT_FREE( zip_buff );
652        }
653        error = 0;
654      }
655    }
656
657    stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
658    stream->pos   = 0;
659    stream->base  = 0;
660    stream->read  = ft_gzip_stream_io;
661    stream->close = ft_gzip_stream_close;
662
663  Exit:
664    return error;
665  }
666
667#else  /* !FT_CONFIG_OPTION_USE_ZLIB */
668
669  FT_EXPORT_DEF( FT_Error )
670  FT_Stream_OpenGzip( FT_Stream  stream,
671                      FT_Stream  source )
672  {
673    FT_UNUSED( stream );
674    FT_UNUSED( source );
675
676    return Gzip_Err_Unimplemented_Feature;
677  }
678
679#endif /* !FT_CONFIG_OPTION_USE_ZLIB */
680
681
682/* END */
Note: See TracBrowser for help on using the repository browser.