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