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