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-2015 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 #undef  FT_ERR_PREFIX
36 #define FT_ERR_PREFIX  Gzip_Err_
37 #define FT_ERR_BASE    FT_Mod_Err_Gzip
38 
39 #include FT_ERRORS_H
40 
41 
42 #ifdef FT_CONFIG_OPTION_USE_ZLIB
43 
44 #ifdef FT_CONFIG_OPTION_PIC
45 #error "gzip code does not support PIC yet"
46 #endif
47 
48 #ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB
49 
50 #include <zlib.h>
51 
52 #else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
53 
54  /* In this case, we include our own modified sources of the ZLib    */
55  /* within the "ftgzip" component.  The modifications were necessary */
56  /* to #include all files without conflicts, as well as preventing   */
57  /* the definition of "extern" functions that may cause linking      */
58  /* conflicts when a program is linked with both FreeType and the    */
59  /* original ZLib.                                                   */
60 
61 #ifndef USE_ZLIB_ZCALLOC
62 #define MY_ZCALLOC /* prevent all zcalloc() & zfree() in zutils.c */
63 #endif
64 
65 #include "zlib.h"
66 
67 #undef  SLOW
68 #define SLOW  1  /* we can't use asm-optimized sources here! */
69 
70 #if defined( _MSC_VER )      /* Visual C++ (and Intel C++)   */
71   /* We disable the warning `conversion from XXX to YYY,     */
72   /* possible loss of data' in order to compile cleanly with */
73   /* the maximum level of warnings: zlib is non-FreeType     */
74   /* code.                                                   */
75 #pragma warning( push )
76 #pragma warning( disable : 4244 )
77 #endif /* _MSC_VER */
78 
79   /* Urgh.  `inflate_mask' must not be declared twice -- C++ doesn't like
80      this.  We temporarily disable it and load all necessary header files. */
81 #define NO_INFLATE_MASK
82 #include "zutil.h"
83 #include "inftrees.h"
84 #include "infblock.h"
85 #include "infcodes.h"
86 #include "infutil.h"
87 #undef  NO_INFLATE_MASK
88 
89   /* infutil.c must be included before infcodes.c */
90 #include "zutil.c"
91 #include "inftrees.c"
92 #include "infutil.c"
93 #include "infcodes.c"
94 #include "infblock.c"
95 #include "inflate.c"
96 #include "adler32.c"
97 
98 #if defined( _MSC_VER )
99 #pragma warning( pop )
100 #endif
101 
102 #endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
103 
104 
105 /***************************************************************************/
106 /***************************************************************************/
107 /*****                                                                 *****/
108 /*****            Z L I B   M E M O R Y   M A N A G E M E N T          *****/
109 /*****                                                                 *****/
110 /***************************************************************************/
111 /***************************************************************************/
112 
113   /* it is better to use FreeType memory routines instead of raw
114      'malloc/free' */
115 
116   static voidpf
ft_gzip_alloc(FT_Memory memory,uInt items,uInt size)117   ft_gzip_alloc( FT_Memory  memory,
118                  uInt       items,
119                  uInt       size )
120   {
121     FT_ULong    sz = (FT_ULong)size * items;
122     FT_Error    error;
123     FT_Pointer  p  = NULL;
124 
125 
126     (void)FT_ALLOC( p, sz );
127     return p;
128   }
129 
130 
131   static void
ft_gzip_free(FT_Memory memory,voidpf address)132   ft_gzip_free( FT_Memory  memory,
133                 voidpf     address )
134   {
135     FT_MEM_FREE( address );
136   }
137 
138 
139 #if !defined( FT_CONFIG_OPTION_SYSTEM_ZLIB ) && !defined( USE_ZLIB_ZCALLOC )
140 
141   local voidpf
zcalloc(voidpf opaque,unsigned items,unsigned size)142   zcalloc ( voidpf    opaque,
143             unsigned  items,
144             unsigned  size )
145   {
146     return ft_gzip_alloc( (FT_Memory)opaque, items, size );
147   }
148 
149   local void
zcfree(voidpf opaque,voidpf ptr)150   zcfree( voidpf  opaque,
151           voidpf  ptr )
152   {
153     ft_gzip_free( (FT_Memory)opaque, ptr );
154   }
155 
156 #endif /* !SYSTEM_ZLIB && !USE_ZLIB_ZCALLOC */
157 
158 
159 /***************************************************************************/
160 /***************************************************************************/
161 /*****                                                                 *****/
162 /*****               Z L I B   F I L E   D E S C R I P T O R           *****/
163 /*****                                                                 *****/
164 /***************************************************************************/
165 /***************************************************************************/
166 
167 #define FT_GZIP_BUFFER_SIZE  4096
168 
169   typedef struct  FT_GZipFileRec_
170   {
171     FT_Stream  source;         /* parent/source stream        */
172     FT_Stream  stream;         /* embedding stream            */
173     FT_Memory  memory;         /* memory allocator            */
174     z_stream   zstream;        /* zlib input stream           */
175 
176     FT_ULong   start;          /* starting position, after .gz header */
177     FT_Byte    input[FT_GZIP_BUFFER_SIZE];   /* input read buffer  */
178 
179     FT_Byte    buffer[FT_GZIP_BUFFER_SIZE];  /* output buffer      */
180     FT_ULong   pos;                          /* position in output */
181     FT_Byte*   cursor;
182     FT_Byte*   limit;
183 
184   } FT_GZipFileRec, *FT_GZipFile;
185 
186 
187   /* gzip flag byte */
188 #define FT_GZIP_ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
189 #define FT_GZIP_HEAD_CRC     0x02 /* bit 1 set: header CRC present */
190 #define FT_GZIP_EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
191 #define FT_GZIP_ORIG_NAME    0x08 /* bit 3 set: original file name present */
192 #define FT_GZIP_COMMENT      0x10 /* bit 4 set: file comment present */
193 #define FT_GZIP_RESERVED     0xE0 /* bits 5..7: reserved */
194 
195 
196   /* check and skip .gz header - we don't support `transparent' compression */
197   static FT_Error
ft_gzip_check_header(FT_Stream stream)198   ft_gzip_check_header( FT_Stream  stream )
199   {
200     FT_Error  error;
201     FT_Byte   head[4];
202 
203 
204     if ( FT_STREAM_SEEK( 0 )       ||
205          FT_STREAM_READ( head, 4 ) )
206       goto Exit;
207 
208     /* head[0] && head[1] are the magic numbers;    */
209     /* head[2] is the method, and head[3] the flags */
210     if ( head[0] != 0x1F              ||
211          head[1] != 0x8B              ||
212          head[2] != Z_DEFLATED        ||
213         (head[3] & FT_GZIP_RESERVED)  )
214     {
215       error = FT_THROW( Invalid_File_Format );
216       goto Exit;
217     }
218 
219     /* skip time, xflags and os code */
220     (void)FT_STREAM_SKIP( 6 );
221 
222     /* skip the extra field */
223     if ( head[3] & FT_GZIP_EXTRA_FIELD )
224     {
225       FT_UInt  len;
226 
227 
228       if ( FT_READ_USHORT_LE( len ) ||
229            FT_STREAM_SKIP( len )    )
230         goto Exit;
231     }
232 
233     /* skip original file name */
234     if ( head[3] & FT_GZIP_ORIG_NAME )
235       for (;;)
236       {
237         FT_UInt  c;
238 
239 
240         if ( FT_READ_BYTE( c ) )
241           goto Exit;
242 
243         if ( c == 0 )
244           break;
245       }
246 
247     /* skip .gz comment */
248     if ( head[3] & FT_GZIP_COMMENT )
249       for (;;)
250       {
251         FT_UInt  c;
252 
253 
254         if ( FT_READ_BYTE( c ) )
255           goto Exit;
256 
257         if ( c == 0 )
258           break;
259       }
260 
261     /* skip CRC */
262     if ( head[3] & FT_GZIP_HEAD_CRC )
263       if ( FT_STREAM_SKIP( 2 ) )
264         goto Exit;
265 
266   Exit:
267     return error;
268   }
269 
270 
271   static FT_Error
ft_gzip_file_init(FT_GZipFile zip,FT_Stream stream,FT_Stream source)272   ft_gzip_file_init( FT_GZipFile  zip,
273                      FT_Stream    stream,
274                      FT_Stream    source )
275   {
276     z_stream*  zstream = &zip->zstream;
277     FT_Error   error   = FT_Err_Ok;
278 
279 
280     zip->stream = stream;
281     zip->source = source;
282     zip->memory = stream->memory;
283 
284     zip->limit  = zip->buffer + FT_GZIP_BUFFER_SIZE;
285     zip->cursor = zip->limit;
286     zip->pos    = 0;
287 
288     /* check and skip .gz header */
289     {
290       stream = source;
291 
292       error = ft_gzip_check_header( stream );
293       if ( error )
294         goto Exit;
295 
296       zip->start = FT_STREAM_POS();
297     }
298 
299     /* initialize zlib -- there is no zlib header in the compressed stream */
300     zstream->zalloc = (alloc_func)ft_gzip_alloc;
301     zstream->zfree  = (free_func) ft_gzip_free;
302     zstream->opaque = stream->memory;
303 
304     zstream->avail_in = 0;
305     zstream->next_in  = zip->buffer;
306 
307     if ( inflateInit2( zstream, -MAX_WBITS ) != Z_OK ||
308          zstream->next_in == NULL                     )
309       error = FT_THROW( Invalid_File_Format );
310 
311   Exit:
312     return error;
313   }
314 
315 
316   static void
ft_gzip_file_done(FT_GZipFile zip)317   ft_gzip_file_done( FT_GZipFile  zip )
318   {
319     z_stream*  zstream = &zip->zstream;
320 
321 
322     inflateEnd( zstream );
323 
324     /* clear the rest */
325     zstream->zalloc    = NULL;
326     zstream->zfree     = NULL;
327     zstream->opaque    = NULL;
328     zstream->next_in   = NULL;
329     zstream->next_out  = NULL;
330     zstream->avail_in  = 0;
331     zstream->avail_out = 0;
332 
333     zip->memory = NULL;
334     zip->source = NULL;
335     zip->stream = NULL;
336   }
337 
338 
339   static FT_Error
ft_gzip_file_reset(FT_GZipFile zip)340   ft_gzip_file_reset( FT_GZipFile  zip )
341   {
342     FT_Stream  stream = zip->source;
343     FT_Error   error;
344 
345 
346     if ( !FT_STREAM_SEEK( zip->start ) )
347     {
348       z_stream*  zstream = &zip->zstream;
349 
350 
351       inflateReset( zstream );
352 
353       zstream->avail_in  = 0;
354       zstream->next_in   = zip->input;
355       zstream->avail_out = 0;
356       zstream->next_out  = zip->buffer;
357 
358       zip->limit  = zip->buffer + FT_GZIP_BUFFER_SIZE;
359       zip->cursor = zip->limit;
360       zip->pos    = 0;
361     }
362 
363     return error;
364   }
365 
366 
367   static FT_Error
ft_gzip_file_fill_input(FT_GZipFile zip)368   ft_gzip_file_fill_input( FT_GZipFile  zip )
369   {
370     z_stream*  zstream = &zip->zstream;
371     FT_Stream  stream  = zip->source;
372     FT_ULong   size;
373 
374 
375     if ( stream->read )
376     {
377       size = stream->read( stream, stream->pos, zip->input,
378                            FT_GZIP_BUFFER_SIZE );
379       if ( size == 0 )
380         return FT_THROW( Invalid_Stream_Operation );
381     }
382     else
383     {
384       size = stream->size - stream->pos;
385       if ( size > FT_GZIP_BUFFER_SIZE )
386         size = FT_GZIP_BUFFER_SIZE;
387 
388       if ( size == 0 )
389         return FT_THROW( Invalid_Stream_Operation );
390 
391       FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
392     }
393     stream->pos += size;
394 
395     zstream->next_in  = zip->input;
396     zstream->avail_in = size;
397 
398     return FT_Err_Ok;
399   }
400 
401 
402   static FT_Error
ft_gzip_file_fill_output(FT_GZipFile zip)403   ft_gzip_file_fill_output( FT_GZipFile  zip )
404   {
405     z_stream*  zstream = &zip->zstream;
406     FT_Error   error   = FT_Err_Ok;
407 
408 
409     zip->cursor        = zip->buffer;
410     zstream->next_out  = zip->cursor;
411     zstream->avail_out = FT_GZIP_BUFFER_SIZE;
412 
413     while ( zstream->avail_out > 0 )
414     {
415       int  err;
416 
417 
418       if ( zstream->avail_in == 0 )
419       {
420         error = ft_gzip_file_fill_input( zip );
421         if ( error )
422           break;
423       }
424 
425       err = inflate( zstream, Z_NO_FLUSH );
426 
427       if ( err == Z_STREAM_END )
428       {
429         zip->limit = zstream->next_out;
430         if ( zip->limit == zip->cursor )
431           error = FT_THROW( Invalid_Stream_Operation );
432         break;
433       }
434       else if ( err != Z_OK )
435       {
436         error = FT_THROW( Invalid_Stream_Operation );
437         break;
438       }
439     }
440 
441     return error;
442   }
443 
444 
445   /* fill output buffer; `count' must be <= FT_GZIP_BUFFER_SIZE */
446   static FT_Error
ft_gzip_file_skip_output(FT_GZipFile zip,FT_ULong count)447   ft_gzip_file_skip_output( FT_GZipFile  zip,
448                             FT_ULong     count )
449   {
450     FT_Error  error = FT_Err_Ok;
451     FT_ULong  delta;
452 
453 
454     for (;;)
455     {
456       delta = (FT_ULong)( zip->limit - zip->cursor );
457       if ( delta >= count )
458         delta = count;
459 
460       zip->cursor += delta;
461       zip->pos    += delta;
462 
463       count -= delta;
464       if ( count == 0 )
465         break;
466 
467       error = ft_gzip_file_fill_output( zip );
468       if ( error )
469         break;
470     }
471 
472     return error;
473   }
474 
475 
476   static FT_ULong
ft_gzip_file_io(FT_GZipFile zip,FT_ULong pos,FT_Byte * buffer,FT_ULong count)477   ft_gzip_file_io( FT_GZipFile  zip,
478                    FT_ULong     pos,
479                    FT_Byte*     buffer,
480                    FT_ULong     count )
481   {
482     FT_ULong  result = 0;
483     FT_Error  error;
484 
485 
486     /* Reset inflate stream if we're seeking backwards.        */
487     /* Yes, that is not too efficient, but it saves memory :-) */
488     if ( pos < zip->pos )
489     {
490       error = ft_gzip_file_reset( zip );
491       if ( error )
492         goto Exit;
493     }
494 
495     /* skip unwanted bytes */
496     if ( pos > zip->pos )
497     {
498       error = ft_gzip_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
499       if ( error )
500         goto Exit;
501     }
502 
503     if ( count == 0 )
504       goto Exit;
505 
506     /* now read the data */
507     for (;;)
508     {
509       FT_ULong  delta;
510 
511 
512       delta = (FT_ULong)( zip->limit - zip->cursor );
513       if ( delta >= count )
514         delta = count;
515 
516       FT_MEM_COPY( buffer, zip->cursor, delta );
517       buffer      += delta;
518       result      += delta;
519       zip->cursor += delta;
520       zip->pos    += delta;
521 
522       count -= delta;
523       if ( count == 0 )
524         break;
525 
526       error = ft_gzip_file_fill_output( zip );
527       if ( error )
528         break;
529     }
530 
531   Exit:
532     return result;
533   }
534 
535 
536 /***************************************************************************/
537 /***************************************************************************/
538 /*****                                                                 *****/
539 /*****               G Z   E M B E D D I N G   S T R E A M             *****/
540 /*****                                                                 *****/
541 /***************************************************************************/
542 /***************************************************************************/
543 
544   static void
ft_gzip_stream_close(FT_Stream stream)545   ft_gzip_stream_close( FT_Stream  stream )
546   {
547     FT_GZipFile  zip    = (FT_GZipFile)stream->descriptor.pointer;
548     FT_Memory    memory = stream->memory;
549 
550 
551     if ( zip )
552     {
553       /* finalize gzip file descriptor */
554       ft_gzip_file_done( zip );
555 
556       FT_FREE( zip );
557 
558       stream->descriptor.pointer = NULL;
559     }
560   }
561 
562 
563   static FT_ULong
ft_gzip_stream_io(FT_Stream stream,FT_ULong pos,FT_Byte * buffer,FT_ULong count)564   ft_gzip_stream_io( FT_Stream  stream,
565                      FT_ULong   pos,
566                      FT_Byte*   buffer,
567                      FT_ULong   count )
568   {
569     FT_GZipFile  zip = (FT_GZipFile)stream->descriptor.pointer;
570 
571 
572     return ft_gzip_file_io( zip, pos, buffer, count );
573   }
574 
575 
576   static FT_ULong
ft_gzip_get_uncompressed_size(FT_Stream stream)577   ft_gzip_get_uncompressed_size( FT_Stream  stream )
578   {
579     FT_Error  error;
580     FT_ULong  old_pos;
581     FT_ULong  result = 0;
582 
583 
584     old_pos = stream->pos;
585     if ( !FT_Stream_Seek( stream, stream->size - 4 ) )
586     {
587       result = FT_Stream_ReadULong( stream, &error );
588       if ( error )
589         result = 0;
590 
591       (void)FT_Stream_Seek( stream, old_pos );
592     }
593 
594     return result;
595   }
596 
597 
598   /* documentation is in ftgzip.h */
599 
600   FT_EXPORT_DEF( FT_Error )
FT_Stream_OpenGzip(FT_Stream stream,FT_Stream source)601   FT_Stream_OpenGzip( FT_Stream  stream,
602                       FT_Stream  source )
603   {
604     FT_Error     error;
605     FT_Memory    memory;
606     FT_GZipFile  zip = NULL;
607 
608 
609     if ( !stream || !source )
610     {
611       error = FT_THROW( Invalid_Stream_Handle );
612       goto Exit;
613     }
614 
615     memory = source->memory;
616 
617     /*
618      *  check the header right now; this prevents allocating un-necessary
619      *  objects when we don't need them
620      */
621     error = ft_gzip_check_header( source );
622     if ( error )
623       goto Exit;
624 
625     FT_ZERO( stream );
626     stream->memory = memory;
627 
628     if ( !FT_QNEW( zip ) )
629     {
630       error = ft_gzip_file_init( zip, stream, source );
631       if ( error )
632       {
633         FT_FREE( zip );
634         goto Exit;
635       }
636 
637       stream->descriptor.pointer = zip;
638     }
639 
640     /*
641      *  We use the following trick to try to dramatically improve the
642      *  performance while dealing with small files.  If the original stream
643      *  size is less than a certain threshold, we try to load the whole font
644      *  file into memory.  This saves us from using the 32KB buffer needed
645      *  to inflate the file, plus the two 4KB intermediate input/output
646      *  buffers used in the `FT_GZipFile' structure.
647      */
648     {
649       FT_ULong  zip_size = ft_gzip_get_uncompressed_size( source );
650 
651 
652       if ( zip_size != 0 && zip_size < 40 * 1024 )
653       {
654         FT_Byte*  zip_buff = NULL;
655 
656 
657         if ( !FT_ALLOC( zip_buff, zip_size ) )
658         {
659           FT_ULong  count;
660 
661 
662           count = ft_gzip_file_io( zip, 0, zip_buff, zip_size );
663           if ( count == zip_size )
664           {
665             ft_gzip_file_done( zip );
666             FT_FREE( zip );
667 
668             stream->descriptor.pointer = NULL;
669 
670             stream->size  = zip_size;
671             stream->pos   = 0;
672             stream->base  = zip_buff;
673             stream->read  = NULL;
674             stream->close = ft_gzip_stream_close;
675 
676             goto Exit;
677           }
678 
679           ft_gzip_file_io( zip, 0, NULL, 0 );
680           FT_FREE( zip_buff );
681         }
682         error = FT_Err_Ok;
683       }
684     }
685 
686     stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
687     stream->pos   = 0;
688     stream->base  = 0;
689     stream->read  = ft_gzip_stream_io;
690     stream->close = ft_gzip_stream_close;
691 
692   Exit:
693     return error;
694   }
695 
696 
697   /* documentation is in ftgzip.h */
698 
699   FT_EXPORT_DEF( FT_Error )
FT_Gzip_Uncompress(FT_Memory memory,FT_Byte * output,FT_ULong * output_len,const FT_Byte * input,FT_ULong input_len)700   FT_Gzip_Uncompress( FT_Memory       memory,
701                       FT_Byte*        output,
702                       FT_ULong*       output_len,
703                       const FT_Byte*  input,
704                       FT_ULong        input_len )
705   {
706     z_stream  stream;
707     int       err;
708 
709 
710     /* check for `input' delayed to `inflate' */
711 
712     if ( !memory || ! output_len || !output )
713       return FT_THROW( Invalid_Argument );
714 
715     /* this function is modeled after zlib's `uncompress' function */
716 
717     stream.next_in  = (Bytef*)input;
718     stream.avail_in = (uInt)input_len;
719 
720     stream.next_out  = output;
721     stream.avail_out = (uInt)*output_len;
722 
723     stream.zalloc = (alloc_func)ft_gzip_alloc;
724     stream.zfree  = (free_func) ft_gzip_free;
725     stream.opaque = memory;
726 
727     err = inflateInit2( &stream, MAX_WBITS );
728     if ( err != Z_OK )
729       return FT_THROW( Invalid_Argument );
730 
731     err = inflate( &stream, Z_FINISH );
732     if ( err != Z_STREAM_END )
733     {
734       inflateEnd( &stream );
735       if ( err == Z_OK )
736         err = Z_BUF_ERROR;
737     }
738     else
739     {
740       *output_len = stream.total_out;
741 
742       err = inflateEnd( &stream );
743     }
744 
745     if ( err == Z_MEM_ERROR )
746       return FT_THROW( Out_Of_Memory );
747 
748     if ( err == Z_BUF_ERROR )
749       return FT_THROW( Array_Too_Large );
750 
751     if ( err == Z_DATA_ERROR )
752       return FT_THROW( Invalid_Table );
753 
754     return FT_Err_Ok;
755   }
756 
757 
758 #else /* !FT_CONFIG_OPTION_USE_ZLIB */
759 
760   FT_EXPORT_DEF( FT_Error )
FT_Stream_OpenGzip(FT_Stream stream,FT_Stream source)761   FT_Stream_OpenGzip( FT_Stream  stream,
762                       FT_Stream  source )
763   {
764     FT_UNUSED( stream );
765     FT_UNUSED( source );
766 
767     return FT_THROW( Unimplemented_Feature );
768   }
769 
770 
771   FT_EXPORT_DEF( FT_Error )
FT_Gzip_Uncompress(FT_Memory memory,FT_Byte * output,FT_ULong * output_len,const FT_Byte * input,FT_ULong input_len)772   FT_Gzip_Uncompress( FT_Memory       memory,
773                       FT_Byte*        output,
774                       FT_ULong*       output_len,
775                       const FT_Byte*  input,
776                       FT_ULong        input_len )
777   {
778     FT_UNUSED( memory );
779     FT_UNUSED( output );
780     FT_UNUSED( output_len );
781     FT_UNUSED( input );
782     FT_UNUSED( input_len );
783 
784     return FT_THROW( Unimplemented_Feature );
785   }
786 
787 #endif /* !FT_CONFIG_OPTION_USE_ZLIB */
788 
789 
790 /* END */
791