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