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-2023 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_QNEW( 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 = NULL;
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 */
416