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