• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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