Imported Upstream version 2.10.4
[platform/upstream/freetype2.git] / src / lzw / ftlzw.c
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
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
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
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
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
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
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
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
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
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 )
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 )
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 */