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