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