1 /***************************************************************************/ 2 /* */ 3 /* ftlzw.c */ 4 /* */ 5 /* FreeType support for .Z compressed files. */ 6 /* */ 7 /* This optional component relies on NetBSD's zopen(). It should mainly */ 8 /* be used to parse compressed PCF fonts, as found with many X11 server */ 9 /* distributions. */ 10 /* */ 11 /* Copyright 2004-2018 by */ 12 /* Albert Chin-A-Young. */ 13 /* */ 14 /* based on code in `src/gzip/ftgzip.c' */ 15 /* */ 16 /* This file is part of the FreeType project, and may only be used, */ 17 /* modified, and distributed under the terms of the FreeType project */ 18 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 19 /* this file you indicate that you have read the license and */ 20 /* understand and accept it fully. */ 21 /* */ 22 /***************************************************************************/ 23 24 #include <ft2build.h> 25 #include FT_INTERNAL_MEMORY_H 26 #include FT_INTERNAL_STREAM_H 27 #include FT_INTERNAL_DEBUG_H 28 #include FT_LZW_H 29 #include FT_CONFIG_STANDARD_LIBRARY_H 30 31 32 #include FT_MODULE_ERRORS_H 33 34 #undef FTERRORS_H_ 35 36 #undef FT_ERR_PREFIX 37 #define FT_ERR_PREFIX LZW_Err_ 38 #define FT_ERR_BASE FT_Mod_Err_LZW 39 40 #include FT_ERRORS_H 41 42 43 #ifdef FT_CONFIG_OPTION_USE_LZW 44 45 #ifdef FT_CONFIG_OPTION_PIC 46 #error "lzw code does not support PIC yet" 47 #endif 48 49 #include "ftzopen.h" 50 51 52 /***************************************************************************/ 53 /***************************************************************************/ 54 /***** *****/ 55 /***** M E M O R Y M A N A G E M E N T *****/ 56 /***** *****/ 57 /***************************************************************************/ 58 /***************************************************************************/ 59 60 /***************************************************************************/ 61 /***************************************************************************/ 62 /***** *****/ 63 /***** F I L E D E S C R I P T O R *****/ 64 /***** *****/ 65 /***************************************************************************/ 66 /***************************************************************************/ 67 68 #define FT_LZW_BUFFER_SIZE 4096 69 70 typedef struct FT_LZWFileRec_ 71 { 72 FT_Stream source; /* parent/source stream */ 73 FT_Stream stream; /* embedding stream */ 74 FT_Memory memory; /* memory allocator */ 75 FT_LzwStateRec lzw; /* lzw decompressor state */ 76 77 FT_Byte buffer[FT_LZW_BUFFER_SIZE]; /* output buffer */ 78 FT_ULong pos; /* position in output */ 79 FT_Byte* cursor; 80 FT_Byte* limit; 81 82 } FT_LZWFileRec, *FT_LZWFile; 83 84 85 /* check and skip .Z header */ 86 static FT_Error 87 ft_lzw_check_header( FT_Stream stream ) 88 { 89 FT_Error error; 90 FT_Byte head[2]; 91 92 93 if ( FT_STREAM_SEEK( 0 ) || 94 FT_STREAM_READ( head, 2 ) ) 95 goto Exit; 96 97 /* head[0] && head[1] are the magic numbers */ 98 if ( head[0] != 0x1F || 99 head[1] != 0x9D ) 100 error = FT_THROW( Invalid_File_Format ); 101 102 Exit: 103 return error; 104 } 105 106 107 static FT_Error 108 ft_lzw_file_init( FT_LZWFile zip, 109 FT_Stream stream, 110 FT_Stream source ) 111 { 112 FT_LzwState lzw = &zip->lzw; 113 FT_Error error; 114 115 116 zip->stream = stream; 117 zip->source = source; 118 zip->memory = stream->memory; 119 120 zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE; 121 zip->cursor = zip->limit; 122 zip->pos = 0; 123 124 /* check and skip .Z header */ 125 error = ft_lzw_check_header( source ); 126 if ( error ) 127 goto Exit; 128 129 /* initialize internal lzw variable */ 130 ft_lzwstate_init( lzw, source ); 131 132 Exit: 133 return error; 134 } 135 136 137 static void 138 ft_lzw_file_done( FT_LZWFile zip ) 139 { 140 /* clear the rest */ 141 ft_lzwstate_done( &zip->lzw ); 142 143 zip->memory = NULL; 144 zip->source = NULL; 145 zip->stream = NULL; 146 } 147 148 149 static FT_Error 150 ft_lzw_file_reset( FT_LZWFile zip ) 151 { 152 FT_Stream stream = zip->source; 153 FT_Error error; 154 155 156 if ( !FT_STREAM_SEEK( 0 ) ) 157 { 158 ft_lzwstate_reset( &zip->lzw ); 159 160 zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE; 161 zip->cursor = zip->limit; 162 zip->pos = 0; 163 } 164 165 return error; 166 } 167 168 169 static FT_Error 170 ft_lzw_file_fill_output( FT_LZWFile zip ) 171 { 172 FT_LzwState lzw = &zip->lzw; 173 FT_ULong count; 174 FT_Error error = FT_Err_Ok; 175 176 177 zip->cursor = zip->buffer; 178 179 count = ft_lzwstate_io( lzw, zip->buffer, FT_LZW_BUFFER_SIZE ); 180 181 zip->limit = zip->cursor + count; 182 183 if ( count == 0 ) 184 error = FT_THROW( Invalid_Stream_Operation ); 185 186 return error; 187 } 188 189 190 /* fill output buffer; `count' must be <= FT_LZW_BUFFER_SIZE */ 191 static FT_Error 192 ft_lzw_file_skip_output( FT_LZWFile zip, 193 FT_ULong count ) 194 { 195 FT_Error error = FT_Err_Ok; 196 197 198 /* first, we skip what we can from the output buffer */ 199 { 200 FT_ULong delta = (FT_ULong)( zip->limit - zip->cursor ); 201 202 203 if ( delta >= count ) 204 delta = count; 205 206 zip->cursor += delta; 207 zip->pos += delta; 208 209 count -= delta; 210 } 211 212 /* next, we skip as many bytes remaining as possible */ 213 while ( count > 0 ) 214 { 215 FT_ULong delta = FT_LZW_BUFFER_SIZE; 216 FT_ULong numread; 217 218 219 if ( delta > count ) 220 delta = count; 221 222 numread = ft_lzwstate_io( &zip->lzw, NULL, delta ); 223 if ( numread < delta ) 224 { 225 /* not enough bytes */ 226 error = FT_THROW( Invalid_Stream_Operation ); 227 break; 228 } 229 230 zip->pos += delta; 231 count -= delta; 232 } 233 234 return error; 235 } 236 237 238 static FT_ULong 239 ft_lzw_file_io( FT_LZWFile zip, 240 FT_ULong pos, 241 FT_Byte* buffer, 242 FT_ULong count ) 243 { 244 FT_ULong result = 0; 245 FT_Error error; 246 247 248 /* seeking backwards. */ 249 if ( pos < zip->pos ) 250 { 251 /* If the new position is within the output buffer, simply */ 252 /* decrement pointers, otherwise we reset the stream completely! */ 253 if ( ( zip->pos - pos ) <= (FT_ULong)( zip->cursor - zip->buffer ) ) 254 { 255 zip->cursor -= zip->pos - pos; 256 zip->pos = pos; 257 } 258 else 259 { 260 error = ft_lzw_file_reset( zip ); 261 if ( error ) 262 goto Exit; 263 } 264 } 265 266 /* skip unwanted bytes */ 267 if ( pos > zip->pos ) 268 { 269 error = ft_lzw_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) ); 270 if ( error ) 271 goto Exit; 272 } 273 274 if ( count == 0 ) 275 goto Exit; 276 277 /* now read the data */ 278 for (;;) 279 { 280 FT_ULong delta; 281 282 283 delta = (FT_ULong)( zip->limit - zip->cursor ); 284 if ( delta >= count ) 285 delta = count; 286 287 FT_MEM_COPY( buffer + result, zip->cursor, delta ); 288 result += delta; 289 zip->cursor += delta; 290 zip->pos += delta; 291 292 count -= delta; 293 if ( count == 0 ) 294 break; 295 296 error = ft_lzw_file_fill_output( zip ); 297 if ( error ) 298 break; 299 } 300 301 Exit: 302 return result; 303 } 304 305 306 /***************************************************************************/ 307 /***************************************************************************/ 308 /***** *****/ 309 /***** L Z W E M B E D D I N G S T R E A M *****/ 310 /***** *****/ 311 /***************************************************************************/ 312 /***************************************************************************/ 313 314 static void 315 ft_lzw_stream_close( FT_Stream stream ) 316 { 317 FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer; 318 FT_Memory memory = stream->memory; 319 320 321 if ( zip ) 322 { 323 /* finalize lzw file descriptor */ 324 ft_lzw_file_done( zip ); 325 326 FT_FREE( zip ); 327 328 stream->descriptor.pointer = NULL; 329 } 330 } 331 332 333 static unsigned long 334 ft_lzw_stream_io( FT_Stream stream, 335 unsigned long offset, 336 unsigned char* buffer, 337 unsigned long count ) 338 { 339 FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer; 340 341 342 return ft_lzw_file_io( zip, offset, buffer, count ); 343 } 344 345 346 FT_EXPORT_DEF( FT_Error ) 347 FT_Stream_OpenLZW( FT_Stream stream, 348 FT_Stream source ) 349 { 350 FT_Error error; 351 FT_Memory memory; 352 FT_LZWFile zip = NULL; 353 354 355 if ( !stream || !source ) 356 { 357 error = FT_THROW( Invalid_Stream_Handle ); 358 goto Exit; 359 } 360 361 memory = source->memory; 362 363 /* 364 * Check the header right now; this prevents allocation of a huge 365 * LZWFile object (400 KByte of heap memory) if not necessary. 366 * 367 * Did I mention that you should never use .Z compressed font 368 * files? 369 */ 370 error = ft_lzw_check_header( source ); 371 if ( error ) 372 goto Exit; 373 374 FT_ZERO( stream ); 375 stream->memory = memory; 376 377 if ( !FT_NEW( zip ) ) 378 { 379 error = ft_lzw_file_init( zip, stream, source ); 380 if ( error ) 381 { 382 FT_FREE( zip ); 383 goto Exit; 384 } 385 386 stream->descriptor.pointer = zip; 387 } 388 389 stream->size = 0x7FFFFFFFL; /* don't know the real size! */ 390 stream->pos = 0; 391 stream->base = 0; 392 stream->read = ft_lzw_stream_io; 393 stream->close = ft_lzw_stream_close; 394 395 Exit: 396 return error; 397 } 398 399 400 #include "ftzopen.c" 401 402 403 #else /* !FT_CONFIG_OPTION_USE_LZW */ 404 405 406 FT_EXPORT_DEF( FT_Error ) 407 FT_Stream_OpenLZW( FT_Stream stream, 408 FT_Stream source ) 409 { 410 FT_UNUSED( stream ); 411 FT_UNUSED( source ); 412 413 return FT_THROW( Unimplemented_Feature ); 414 } 415 416 417 #endif /* !FT_CONFIG_OPTION_USE_LZW */ 418 419 420 /* END */ 421