1 /***************************************************************************/
5 /* FreeType support for .gz compressed files. */
7 /* This optional component relies on zlib. It should mainly be used to */
8 /* parse compressed PCF fonts, as found with many X11 server */
11 /* Copyright 2002-2018 by */
12 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
20 /***************************************************************************/
24 #include FT_INTERNAL_MEMORY_H
25 #include FT_INTERNAL_STREAM_H
26 #include FT_INTERNAL_DEBUG_H
28 #include FT_CONFIG_STANDARD_LIBRARY_H
31 #include FT_MODULE_ERRORS_H
36 #define FT_ERR_PREFIX Gzip_Err_
37 #define FT_ERR_BASE FT_Mod_Err_Gzip
42 #ifdef FT_CONFIG_OPTION_USE_ZLIB
44 #ifdef FT_CONFIG_OPTION_PIC
45 #error "gzip code does not support PIC yet"
48 #ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB
52 #else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
54 /* In this case, we include our own modified sources of the ZLib */
55 /* within the `gzip' 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 */
61 #ifndef USE_ZLIB_ZCALLOC
62 #define MY_ZCALLOC /* prevent all zcalloc() & zfree() in zutil.c */
65 /* Note that our `zlib.h' includes `ftzconf.h' instead of `zconf.h'; */
66 /* the main reason is that even a global `zlib.h' includes `zconf.h' */
69 /* #include "zconf.h" */
71 /* instead of the expected */
73 /* #include <zconf.h> */
75 /* so that configuration with `FT_CONFIG_OPTION_SYSTEM_ZLIB' might */
76 /* include the wrong `zconf.h' file, leading to errors. */
80 #define SLOW 1 /* we can't use asm-optimized sources here! */
82 #if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */
83 /* We disable the warning `conversion from XXX to YYY, */
84 /* possible loss of data' in order to compile cleanly with */
85 /* the maximum level of warnings: zlib is non-FreeType */
87 #pragma warning( push )
88 #pragma warning( disable : 4244 )
91 /* Urgh. `inflate_mask' must not be declared twice -- C++ doesn't like
92 this. We temporarily disable it and load all necessary header files. */
93 #define NO_INFLATE_MASK
99 #undef NO_INFLATE_MASK
101 /* infutil.c must be included before infcodes.c */
103 #include "inftrees.c"
105 #include "infcodes.c"
106 #include "infblock.c"
110 #if defined( _MSC_VER )
111 #pragma warning( pop )
114 #endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
117 /***************************************************************************/
118 /***************************************************************************/
120 /***** Z L I B M E M O R Y M A N A G E M E N T *****/
122 /***************************************************************************/
123 /***************************************************************************/
125 /* it is better to use FreeType memory routines instead of raw
129 ft_gzip_alloc( FT_Memory memory
,
133 FT_ULong sz
= (FT_ULong
)size
* items
;
138 (void)FT_ALLOC( p
, sz
);
144 ft_gzip_free( FT_Memory memory
,
147 FT_MEM_FREE( address
);
151 #if !defined( FT_CONFIG_OPTION_SYSTEM_ZLIB ) && !defined( USE_ZLIB_ZCALLOC )
154 zcalloc ( voidpf opaque
,
158 return ft_gzip_alloc( (FT_Memory
)opaque
, items
, size
);
162 zcfree( voidpf opaque
,
165 ft_gzip_free( (FT_Memory
)opaque
, ptr
);
168 #endif /* !SYSTEM_ZLIB && !USE_ZLIB_ZCALLOC */
171 /***************************************************************************/
172 /***************************************************************************/
174 /***** Z L I B F I L E D E S C R I P T O R *****/
176 /***************************************************************************/
177 /***************************************************************************/
179 #define FT_GZIP_BUFFER_SIZE 4096
181 typedef struct FT_GZipFileRec_
183 FT_Stream source
; /* parent/source stream */
184 FT_Stream stream
; /* embedding stream */
185 FT_Memory memory
; /* memory allocator */
186 z_stream zstream
; /* zlib input stream */
188 FT_ULong start
; /* starting position, after .gz header */
189 FT_Byte input
[FT_GZIP_BUFFER_SIZE
]; /* input read buffer */
191 FT_Byte buffer
[FT_GZIP_BUFFER_SIZE
]; /* output buffer */
192 FT_ULong pos
; /* position in output */
196 } FT_GZipFileRec
, *FT_GZipFile
;
200 #define FT_GZIP_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
201 #define FT_GZIP_HEAD_CRC 0x02 /* bit 1 set: header CRC present */
202 #define FT_GZIP_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
203 #define FT_GZIP_ORIG_NAME 0x08 /* bit 3 set: original file name present */
204 #define FT_GZIP_COMMENT 0x10 /* bit 4 set: file comment present */
205 #define FT_GZIP_RESERVED 0xE0 /* bits 5..7: reserved */
208 /* check and skip .gz header - we don't support `transparent' compression */
210 ft_gzip_check_header( FT_Stream stream
)
216 if ( FT_STREAM_SEEK( 0 ) ||
217 FT_STREAM_READ( head
, 4 ) )
220 /* head[0] && head[1] are the magic numbers; */
221 /* head[2] is the method, and head[3] the flags */
222 if ( head
[0] != 0x1F ||
224 head
[2] != Z_DEFLATED
||
225 (head
[3] & FT_GZIP_RESERVED
) )
227 error
= FT_THROW( Invalid_File_Format
);
231 /* skip time, xflags and os code */
232 (void)FT_STREAM_SKIP( 6 );
234 /* skip the extra field */
235 if ( head
[3] & FT_GZIP_EXTRA_FIELD
)
240 if ( FT_READ_USHORT_LE( len
) ||
241 FT_STREAM_SKIP( len
) )
245 /* skip original file name */
246 if ( head
[3] & FT_GZIP_ORIG_NAME
)
252 if ( FT_READ_BYTE( c
) )
259 /* skip .gz comment */
260 if ( head
[3] & FT_GZIP_COMMENT
)
266 if ( FT_READ_BYTE( c
) )
274 if ( head
[3] & FT_GZIP_HEAD_CRC
)
275 if ( FT_STREAM_SKIP( 2 ) )
284 ft_gzip_file_init( FT_GZipFile zip
,
288 z_stream
* zstream
= &zip
->zstream
;
289 FT_Error error
= FT_Err_Ok
;
292 zip
->stream
= stream
;
293 zip
->source
= source
;
294 zip
->memory
= stream
->memory
;
296 zip
->limit
= zip
->buffer
+ FT_GZIP_BUFFER_SIZE
;
297 zip
->cursor
= zip
->limit
;
300 /* check and skip .gz header */
304 error
= ft_gzip_check_header( stream
);
308 zip
->start
= FT_STREAM_POS();
311 /* initialize zlib -- there is no zlib header in the compressed stream */
312 zstream
->zalloc
= (alloc_func
)ft_gzip_alloc
;
313 zstream
->zfree
= (free_func
) ft_gzip_free
;
314 zstream
->opaque
= stream
->memory
;
316 zstream
->avail_in
= 0;
317 zstream
->next_in
= zip
->buffer
;
319 if ( inflateInit2( zstream
, -MAX_WBITS
) != Z_OK
||
321 error
= FT_THROW( Invalid_File_Format
);
329 ft_gzip_file_done( FT_GZipFile zip
)
331 z_stream
* zstream
= &zip
->zstream
;
334 inflateEnd( zstream
);
337 zstream
->zalloc
= NULL
;
338 zstream
->zfree
= NULL
;
339 zstream
->opaque
= NULL
;
340 zstream
->next_in
= NULL
;
341 zstream
->next_out
= NULL
;
342 zstream
->avail_in
= 0;
343 zstream
->avail_out
= 0;
352 ft_gzip_file_reset( FT_GZipFile zip
)
354 FT_Stream stream
= zip
->source
;
358 if ( !FT_STREAM_SEEK( zip
->start
) )
360 z_stream
* zstream
= &zip
->zstream
;
363 inflateReset( zstream
);
365 zstream
->avail_in
= 0;
366 zstream
->next_in
= zip
->input
;
367 zstream
->avail_out
= 0;
368 zstream
->next_out
= zip
->buffer
;
370 zip
->limit
= zip
->buffer
+ FT_GZIP_BUFFER_SIZE
;
371 zip
->cursor
= zip
->limit
;
380 ft_gzip_file_fill_input( FT_GZipFile zip
)
382 z_stream
* zstream
= &zip
->zstream
;
383 FT_Stream stream
= zip
->source
;
389 size
= stream
->read( stream
, stream
->pos
, zip
->input
,
390 FT_GZIP_BUFFER_SIZE
);
393 zip
->limit
= zip
->cursor
;
394 return FT_THROW( Invalid_Stream_Operation
);
399 size
= stream
->size
- stream
->pos
;
400 if ( size
> FT_GZIP_BUFFER_SIZE
)
401 size
= FT_GZIP_BUFFER_SIZE
;
405 zip
->limit
= zip
->cursor
;
406 return FT_THROW( Invalid_Stream_Operation
);
409 FT_MEM_COPY( zip
->input
, stream
->base
+ stream
->pos
, size
);
413 zstream
->next_in
= zip
->input
;
414 zstream
->avail_in
= size
;
421 ft_gzip_file_fill_output( FT_GZipFile zip
)
423 z_stream
* zstream
= &zip
->zstream
;
424 FT_Error error
= FT_Err_Ok
;
427 zip
->cursor
= zip
->buffer
;
428 zstream
->next_out
= zip
->cursor
;
429 zstream
->avail_out
= FT_GZIP_BUFFER_SIZE
;
431 while ( zstream
->avail_out
> 0 )
436 if ( zstream
->avail_in
== 0 )
438 error
= ft_gzip_file_fill_input( zip
);
443 err
= inflate( zstream
, Z_NO_FLUSH
);
445 if ( err
== Z_STREAM_END
)
447 zip
->limit
= zstream
->next_out
;
448 if ( zip
->limit
== zip
->cursor
)
449 error
= FT_THROW( Invalid_Stream_Operation
);
452 else if ( err
!= Z_OK
)
454 zip
->limit
= zip
->cursor
;
455 error
= FT_THROW( Invalid_Stream_Operation
);
464 /* fill output buffer; `count' must be <= FT_GZIP_BUFFER_SIZE */
466 ft_gzip_file_skip_output( FT_GZipFile zip
,
469 FT_Error error
= FT_Err_Ok
;
475 delta
= (FT_ULong
)( zip
->limit
- zip
->cursor
);
476 if ( delta
>= count
)
479 zip
->cursor
+= delta
;
486 error
= ft_gzip_file_fill_output( zip
);
496 ft_gzip_file_io( FT_GZipFile zip
,
505 /* Reset inflate stream if we're seeking backwards. */
506 /* Yes, that is not too efficient, but it saves memory :-) */
507 if ( pos
< zip
->pos
)
509 error
= ft_gzip_file_reset( zip
);
514 /* skip unwanted bytes */
515 if ( pos
> zip
->pos
)
517 error
= ft_gzip_file_skip_output( zip
, (FT_ULong
)( pos
- zip
->pos
) );
525 /* now read the data */
531 delta
= (FT_ULong
)( zip
->limit
- zip
->cursor
);
532 if ( delta
>= count
)
535 FT_MEM_COPY( buffer
, zip
->cursor
, delta
);
538 zip
->cursor
+= delta
;
545 error
= ft_gzip_file_fill_output( zip
);
555 /***************************************************************************/
556 /***************************************************************************/
558 /***** G Z E M B E D D I N G S T R E A M *****/
560 /***************************************************************************/
561 /***************************************************************************/
564 ft_gzip_stream_close( FT_Stream stream
)
566 FT_GZipFile zip
= (FT_GZipFile
)stream
->descriptor
.pointer
;
567 FT_Memory memory
= stream
->memory
;
572 /* finalize gzip file descriptor */
573 ft_gzip_file_done( zip
);
577 stream
->descriptor
.pointer
= NULL
;
581 FT_FREE( stream
->base
);
586 ft_gzip_stream_io( FT_Stream stream
,
587 unsigned long offset
,
588 unsigned char* buffer
,
589 unsigned long count
)
591 FT_GZipFile zip
= (FT_GZipFile
)stream
->descriptor
.pointer
;
594 return ft_gzip_file_io( zip
, offset
, buffer
, count
);
599 ft_gzip_get_uncompressed_size( FT_Stream stream
)
606 old_pos
= stream
->pos
;
607 if ( !FT_Stream_Seek( stream
, stream
->size
- 4 ) )
609 result
= FT_Stream_ReadULongLE( stream
, &error
);
613 (void)FT_Stream_Seek( stream
, old_pos
);
620 /* documentation is in ftgzip.h */
622 FT_EXPORT_DEF( FT_Error
)
623 FT_Stream_OpenGzip( FT_Stream stream
,
628 FT_GZipFile zip
= NULL
;
631 if ( !stream
|| !source
)
633 error
= FT_THROW( Invalid_Stream_Handle
);
637 memory
= source
->memory
;
640 * check the header right now; this prevents allocating un-necessary
641 * objects when we don't need them
643 error
= ft_gzip_check_header( source
);
648 stream
->memory
= memory
;
650 if ( !FT_QNEW( zip
) )
652 error
= ft_gzip_file_init( zip
, stream
, source
);
659 stream
->descriptor
.pointer
= zip
;
663 * We use the following trick to try to dramatically improve the
664 * performance while dealing with small files. If the original stream
665 * size is less than a certain threshold, we try to load the whole font
666 * file into memory. This saves us from using the 32KB buffer needed
667 * to inflate the file, plus the two 4KB intermediate input/output
668 * buffers used in the `FT_GZipFile' structure.
671 FT_ULong zip_size
= ft_gzip_get_uncompressed_size( source
);
674 if ( zip_size
!= 0 && zip_size
< 40 * 1024 )
676 FT_Byte
* zip_buff
= NULL
;
679 if ( !FT_ALLOC( zip_buff
, zip_size
) )
684 count
= ft_gzip_file_io( zip
, 0, zip_buff
, zip_size
);
685 if ( count
== zip_size
)
687 ft_gzip_file_done( zip
);
690 stream
->descriptor
.pointer
= NULL
;
692 stream
->size
= zip_size
;
694 stream
->base
= zip_buff
;
696 stream
->close
= ft_gzip_stream_close
;
701 ft_gzip_file_io( zip
, 0, NULL
, 0 );
708 stream
->size
= zip_size
;
710 stream
->size
= 0x7FFFFFFFL
; /* don't know the real size! */
715 stream
->read
= ft_gzip_stream_io
;
716 stream
->close
= ft_gzip_stream_close
;
723 /* documentation is in ftgzip.h */
725 FT_EXPORT_DEF( FT_Error
)
726 FT_Gzip_Uncompress( FT_Memory memory
,
728 FT_ULong
* output_len
,
729 const FT_Byte
* input
,
736 /* check for `input' delayed to `inflate' */
738 if ( !memory
|| ! output_len
|| !output
)
739 return FT_THROW( Invalid_Argument
);
741 /* this function is modeled after zlib's `uncompress' function */
743 stream
.next_in
= (Bytef
*)input
;
744 stream
.avail_in
= (uInt
)input_len
;
746 stream
.next_out
= output
;
747 stream
.avail_out
= (uInt
)*output_len
;
749 stream
.zalloc
= (alloc_func
)ft_gzip_alloc
;
750 stream
.zfree
= (free_func
) ft_gzip_free
;
751 stream
.opaque
= memory
;
753 err
= inflateInit2( &stream
, MAX_WBITS
);
755 return FT_THROW( Invalid_Argument
);
757 err
= inflate( &stream
, Z_FINISH
);
758 if ( err
!= Z_STREAM_END
)
760 inflateEnd( &stream
);
766 *output_len
= stream
.total_out
;
768 err
= inflateEnd( &stream
);
771 if ( err
== Z_MEM_ERROR
)
772 return FT_THROW( Out_Of_Memory
);
774 if ( err
== Z_BUF_ERROR
)
775 return FT_THROW( Array_Too_Large
);
777 if ( err
== Z_DATA_ERROR
)
778 return FT_THROW( Invalid_Table
);
784 #else /* !FT_CONFIG_OPTION_USE_ZLIB */
786 FT_EXPORT_DEF( FT_Error
)
787 FT_Stream_OpenGzip( FT_Stream stream
,
793 return FT_THROW( Unimplemented_Feature
);
797 FT_EXPORT_DEF( FT_Error
)
798 FT_Gzip_Uncompress( FT_Memory memory
,
800 FT_ULong
* output_len
,
801 const FT_Byte
* input
,
806 FT_UNUSED( output_len
);
808 FT_UNUSED( input_len
);
810 return FT_THROW( Unimplemented_Feature
);
813 #endif /* !FT_CONFIG_OPTION_USE_ZLIB */