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