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