ftlzw.c revision 6ea2054fd2ec7a6d1f4beb9dee44379bb25730a7
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 by                                                      */
12/*  Albert Chin-A-Young.                                                   */
13/*                                                                         */
14/*  Based on code in src/gzip/ftgzip.c, Copyright 2004 by                  */
15/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
16/*                                                                         */
17/*  This file is part of the FreeType project, and may only be used,       */
18/*  modified, and distributed under the terms of the FreeType project      */
19/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
20/*  this file you indicate that you have read the license and              */
21/*  understand and accept it fully.                                        */
22/*                                                                         */
23/***************************************************************************/
24
25#include <ft2build.h>
26#include FT_INTERNAL_MEMORY_H
27#include FT_INTERNAL_STREAM_H
28#include FT_INTERNAL_DEBUG_H
29#include <string.h>
30#include <stdio.h>
31#include "zopen.h"
32
33
34#include FT_MODULE_ERRORS_H
35
36#undef __FTERRORS_H__
37
38#define FT_ERR_PREFIX  LZW_Err_
39#define FT_ERR_BASE    FT_Mod_Err_LZW
40
41#include FT_ERRORS_H
42
43
44#ifdef FT_CONFIG_OPTION_USE_LZW
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    s_zstate_t  zstream;        /* lzw input stream            */
71
72    FT_ULong    start;          /* starting position, after .Z header */
73    FT_Byte     input[FT_LZW_BUFFER_SIZE];  /* input buffer */
74
75    FT_Byte     buffer[FT_LZW_BUFFER_SIZE]; /* output buffer */
76    FT_ULong    pos;            /* position in output          */
77    FT_Byte*    cursor;
78    FT_Byte*    limit;
79
80  } FT_LZWFileRec, *FT_LZWFile;
81
82
83  /* check and skip .Z header */
84  static FT_Error
85  ft_lzw_check_header( FT_Stream  stream )
86  {
87    FT_Error  error;
88    FT_Byte   head[2];
89
90
91    if ( FT_STREAM_SEEK( 0 )       ||
92         FT_STREAM_READ( head, 2 ) )
93      goto Exit;
94
95    /* head[0] && head[1] are the magic numbers     */
96    if ( head[0] != 0x1f ||
97         head[1] != 0x9d )
98      error = LZW_Err_Invalid_File_Format;
99
100  Exit:
101    return error;
102  }
103
104
105  static FT_Error
106  ft_lzw_file_init( FT_LZWFile  zip,
107                    FT_Stream   stream,
108                    FT_Stream   source )
109  {
110    s_zstate_t*  zstream = &zip->zstream;
111    FT_Error     error   = LZW_Err_Ok;
112
113
114    zip->stream = stream;
115    zip->source = source;
116    zip->memory = stream->memory;
117
118    zip->limit  = zip->buffer + FT_LZW_BUFFER_SIZE;
119    zip->cursor = zip->limit;
120    zip->pos    = 0;
121
122    /* check and skip .Z header */
123    {
124      stream = source;
125
126      error = ft_lzw_check_header( source );
127      if ( error )
128        goto Exit;
129
130      zip->start = FT_STREAM_POS();
131    }
132
133    /* initialize internal lzw variable */
134    zinit( zstream );
135
136    zstream->avail_in    = 0;
137    zstream->next_in     = zip->buffer;
138    zstream->zs_in_count = source->size - 2;
139
140    if ( zstream->next_in == NULL )
141      error = LZW_Err_Invalid_File_Format;
142
143  Exit:
144    return error;
145  }
146
147
148  static void
149  ft_lzw_file_done( FT_LZWFile  zip )
150  {
151    s_zstate_t*  zstream = &zip->zstream;
152
153
154    /* clear the rest */
155    zstream->next_in   = NULL;
156    zstream->next_out  = NULL;
157    zstream->avail_in  = 0;
158    zstream->avail_out = 0;
159    zstream->total_in  = 0;
160    zstream->total_out = 0;
161
162    zip->memory = NULL;
163    zip->source = NULL;
164    zip->stream = NULL;
165  }
166
167
168  static FT_Error
169  ft_lzw_file_reset( FT_LZWFile  zip )
170  {
171    FT_Stream  stream = zip->source;
172    FT_Error   error;
173
174
175    if ( !FT_STREAM_SEEK( zip->start ) )
176    {
177      s_zstate_t*  zstream = &zip->zstream;
178
179
180      zinit( zstream );
181
182      zstream->avail_in    = 0;
183      zstream->next_in     = zip->input;
184      zstream->total_in    = 0;
185      zstream->avail_out   = 0;
186      zstream->next_out    = zip->buffer;
187      zstream->total_out   = 0;
188      zstream->zs_in_count = zip->source->size - 2;
189
190      zip->limit  = zip->buffer + FT_LZW_BUFFER_SIZE;
191      zip->cursor = zip->limit;
192      zip->pos    = 0;
193    }
194
195    return error;
196  }
197
198
199  static FT_Error
200  ft_lzw_file_fill_input( FT_LZWFile  zip )
201  {
202    s_zstate_t*  zstream = &zip->zstream;
203    FT_Stream    stream  = zip->source;
204    FT_ULong     size;
205
206
207    if ( stream->read )
208    {
209      size = stream->read( stream, stream->pos, zip->input,
210                           FT_LZW_BUFFER_SIZE );
211      if ( size == 0 )
212        return LZW_Err_Invalid_Stream_Operation;
213    }
214    else
215    {
216      size = stream->size - stream->pos;
217      if ( size > FT_LZW_BUFFER_SIZE )
218        size = FT_LZW_BUFFER_SIZE;
219
220      if ( size == 0 )
221        return LZW_Err_Invalid_Stream_Operation;
222
223      FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
224    }
225    stream->pos += size;
226
227    zstream->next_in  = zip->input;
228    zstream->avail_in = size;
229
230    return LZW_Err_Ok;
231  }
232
233
234
235  static FT_Error
236  ft_lzw_file_fill_output( FT_LZWFile  zip )
237  {
238    s_zstate_t*  zstream = &zip->zstream;
239    FT_Error     error   = 0;
240
241
242    zip->cursor        = zip->buffer;
243    zstream->next_out  = zip->cursor;
244    zstream->avail_out = FT_LZW_BUFFER_SIZE;
245
246    while ( zstream->avail_out > 0 )
247    {
248      int  num_read = 0;
249
250
251      if ( zstream->avail_in == 0 )
252      {
253        error = ft_lzw_file_fill_input( zip );
254        if ( error )
255          break;
256      }
257
258      num_read = zread( zstream );
259
260      if ( num_read == -1 && zstream->zs_in_count == 0 )
261      {
262        zip->limit = zstream->next_out;
263        if ( zip->limit == zip->cursor )
264          error = LZW_Err_Invalid_Stream_Operation;
265        break;
266      }
267      else if ( num_read == -1 )
268        break;
269      else
270        zstream->avail_out -= num_read;
271    }
272
273    return error;
274  }
275
276
277  /* fill output buffer; `count' must be <= FT_LZW_BUFFER_SIZE */
278  static FT_Error
279  ft_lzw_file_skip_output( FT_LZWFile  zip,
280                           FT_ULong    count )
281  {
282    FT_Error  error = LZW_Err_Ok;
283    FT_ULong  delta;
284
285
286    for (;;)
287    {
288      delta = (FT_ULong)( zip->limit - zip->cursor );
289      if ( delta >= count )
290        delta = count;
291
292      zip->cursor += delta;
293      zip->pos    += delta;
294
295      count -= delta;
296      if ( count == 0 )
297        break;
298
299      error = ft_lzw_file_fill_output( zip );
300      if ( error )
301        break;
302    }
303
304    return error;
305  }
306
307
308  static FT_ULong
309  ft_lzw_file_io( FT_LZWFile  zip,
310                  FT_ULong    pos,
311                  FT_Byte*    buffer,
312                  FT_ULong    count )
313  {
314    FT_ULong  result = 0;
315    FT_Error  error;
316
317
318    /* Teset inflate stream if we're seeking backwards.        */
319    /* Yes, that is not too efficient, but it saves memory :-) */
320    if ( pos < zip->pos )
321    {
322      error = ft_lzw_file_reset( zip );
323      if ( error )
324        goto Exit;
325    }
326
327    /* skip unwanted bytes */
328    if ( pos > zip->pos )
329    {
330      error = ft_lzw_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
331      if ( error )
332        goto Exit;
333    }
334
335    if ( count == 0 )
336      goto Exit;
337
338    /* now read the data */
339    for (;;)
340    {
341      FT_ULong  delta;
342
343
344      delta = (FT_ULong)( zip->limit - zip->cursor );
345      if ( delta >= count )
346        delta = count;
347
348      FT_MEM_COPY( buffer, zip->cursor, delta );
349      buffer      += delta;
350      result      += delta;
351      zip->cursor += delta;
352      zip->pos    += delta;
353
354      count -= delta;
355      if ( count == 0 )
356        break;
357
358      error = ft_lzw_file_fill_output( zip );
359      if ( error )
360        break;
361    }
362
363  Exit:
364    return result;
365  }
366
367
368/***************************************************************************/
369/***************************************************************************/
370/*****                                                                 *****/
371/*****            L Z W   E M B E D D I N G   S T R E A M              *****/
372/*****                                                                 *****/
373/***************************************************************************/
374/***************************************************************************/
375
376  static void
377  ft_lzw_stream_close( FT_Stream  stream )
378  {
379    FT_LZWFile  zip    = (FT_LZWFile)stream->descriptor.pointer;
380    FT_Memory   memory = stream->memory;
381
382
383    if ( zip )
384    {
385      /* finalize lzw file descriptor */
386      ft_lzw_file_done( zip );
387
388      FT_FREE( zip );
389
390      stream->descriptor.pointer = NULL;
391    }
392  }
393
394
395  static FT_ULong
396  ft_lzw_stream_io( FT_Stream  stream,
397                    FT_ULong   pos,
398                    FT_Byte*   buffer,
399                    FT_ULong   count )
400  {
401    FT_LZWFile  zip = (FT_LZWFile)stream->descriptor.pointer;
402
403
404    return ft_lzw_file_io( zip, pos, buffer, count );
405  }
406
407
408  FT_EXPORT_DEF( FT_Error )
409  FT_Stream_OpenLZW( FT_Stream  stream,
410                     FT_Stream  source )
411  {
412    FT_Error    error;
413    FT_Memory   memory = source->memory;
414    FT_LZWFile  zip;
415
416
417    FT_ZERO( stream );
418    stream->memory = memory;
419
420    if ( !FT_NEW( zip ) )
421    {
422      error = ft_lzw_file_init( zip, stream, source );
423      if ( error )
424      {
425        FT_FREE( zip );
426        goto Exit;
427      }
428
429      stream->descriptor.pointer = zip;
430    }
431
432    stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
433    stream->pos   = 0;
434    stream->base  = 0;
435    stream->read  = ft_lzw_stream_io;
436    stream->close = ft_lzw_stream_close;
437
438  Exit:
439    return error;
440  }
441
442#include "zopen.c"
443
444
445#else  /* !FT_CONFIG_OPTION_USE_LZW */
446
447
448  FT_EXPORT_DEF( FT_Error )
449  FT_Stream_OpenLZW( FT_Stream  stream,
450                      FT_Stream  source )
451  {
452    FT_UNUSED( stream );
453    FT_UNUSED( source );
454
455    return LZW_Err_Unimplemented_Feature;
456  }
457
458
459#endif /* !FT_CONFIG_OPTION_USE_LZW */
460
461
462/* END */
463