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_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 | |