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