1 | /**************************************************************************** |
2 | * |
3 | * t42parse.c |
4 | * |
5 | * Type 42 font parser (body). |
6 | * |
7 | * Copyright (C) 2002-2023 by |
8 | * Roberto Alameda. |
9 | * |
10 | * This file is part of the FreeType project, and may only be used, |
11 | * modified, and distributed under the terms of the FreeType project |
12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
13 | * this file you indicate that you have read the license and |
14 | * understand and accept it fully. |
15 | * |
16 | */ |
17 | |
18 | |
19 | #include "t42parse.h" |
20 | #include "t42error.h" |
21 | #include <freetype/internal/ftdebug.h> |
22 | #include <freetype/internal/ftstream.h> |
23 | #include <freetype/internal/psaux.h> |
24 | |
25 | |
26 | /************************************************************************** |
27 | * |
28 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
29 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
30 | * messages during execution. |
31 | */ |
32 | #undef FT_COMPONENT |
33 | #define FT_COMPONENT t42 |
34 | |
35 | |
36 | static void |
37 | t42_parse_font_matrix( FT_Face face, |
38 | void* loader_ ); |
39 | static void |
40 | t42_parse_encoding( FT_Face face, |
41 | void* loader_ ); |
42 | |
43 | static void |
44 | t42_parse_charstrings( FT_Face face, |
45 | void* loader_ ); |
46 | |
47 | static void |
48 | t42_parse_sfnts( FT_Face face, |
49 | void* loader_ ); |
50 | |
51 | |
52 | /* as Type42 fonts have no Private dict, */ |
53 | /* we set the last argument of T1_FIELD_XXX to 0 */ |
54 | static const |
55 | T1_FieldRec t42_keywords[] = |
56 | { |
57 | |
58 | #undef FT_STRUCTURE |
59 | #define FT_STRUCTURE T1_FontInfo |
60 | #undef T1CODE |
61 | #define T1CODE T1_FIELD_LOCATION_FONT_INFO |
62 | |
63 | T1_FIELD_STRING( "version" , version, 0 ) |
64 | T1_FIELD_STRING( "Notice" , notice, 0 ) |
65 | T1_FIELD_STRING( "FullName" , full_name, 0 ) |
66 | T1_FIELD_STRING( "FamilyName" , family_name, 0 ) |
67 | T1_FIELD_STRING( "Weight" , weight, 0 ) |
68 | T1_FIELD_NUM ( "ItalicAngle" , italic_angle, 0 ) |
69 | T1_FIELD_BOOL ( "isFixedPitch" , is_fixed_pitch, 0 ) |
70 | T1_FIELD_NUM ( "UnderlinePosition" , underline_position, 0 ) |
71 | T1_FIELD_NUM ( "UnderlineThickness" , underline_thickness, 0 ) |
72 | |
73 | #undef FT_STRUCTURE |
74 | #define FT_STRUCTURE PS_FontExtraRec |
75 | #undef T1CODE |
76 | #define T1CODE T1_FIELD_LOCATION_FONT_EXTRA |
77 | |
78 | T1_FIELD_NUM ( "FSType" , fs_type, 0 ) |
79 | |
80 | #undef FT_STRUCTURE |
81 | #define FT_STRUCTURE T1_FontRec |
82 | #undef T1CODE |
83 | #define T1CODE T1_FIELD_LOCATION_FONT_DICT |
84 | |
85 | T1_FIELD_KEY ( "FontName" , font_name, 0 ) |
86 | T1_FIELD_NUM ( "PaintType" , paint_type, 0 ) |
87 | T1_FIELD_NUM ( "FontType" , font_type, 0 ) |
88 | T1_FIELD_FIXED( "StrokeWidth" , stroke_width, 0 ) |
89 | |
90 | #undef FT_STRUCTURE |
91 | #define FT_STRUCTURE FT_BBox |
92 | #undef T1CODE |
93 | #define T1CODE T1_FIELD_LOCATION_BBOX |
94 | |
95 | T1_FIELD_BBOX( "FontBBox" , xMin, 0 ) |
96 | |
97 | T1_FIELD_CALLBACK( "FontMatrix" , t42_parse_font_matrix, 0 ) |
98 | T1_FIELD_CALLBACK( "Encoding" , t42_parse_encoding, 0 ) |
99 | T1_FIELD_CALLBACK( "CharStrings" , t42_parse_charstrings, 0 ) |
100 | T1_FIELD_CALLBACK( "sfnts" , t42_parse_sfnts, 0 ) |
101 | |
102 | { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 } |
103 | }; |
104 | |
105 | |
106 | #define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l ) |
107 | #define T1_Release_Table( p ) \ |
108 | do \ |
109 | { \ |
110 | if ( (p)->funcs.release ) \ |
111 | (p)->funcs.release( p ); \ |
112 | } while ( 0 ) |
113 | |
114 | #define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root ) |
115 | #define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root ) |
116 | |
117 | #define T1_ToInt( p ) \ |
118 | (p)->root.funcs.to_int( &(p)->root ) |
119 | #define T1_ToBytes( p, b, m, n, d ) \ |
120 | (p)->root.funcs.to_bytes( &(p)->root, b, m, n, d ) |
121 | |
122 | #define T1_ToFixedArray( p, m, f, t ) \ |
123 | (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) |
124 | #define T1_ToToken( p, t ) \ |
125 | (p)->root.funcs.to_token( &(p)->root, t ) |
126 | |
127 | #define T1_Load_Field( p, f, o, m, pf ) \ |
128 | (p)->root.funcs.load_field( &(p)->root, f, o, m, pf ) |
129 | #define T1_Load_Field_Table( p, f, o, m, pf ) \ |
130 | (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf ) |
131 | |
132 | |
133 | /********************* Parsing Functions ******************/ |
134 | |
135 | FT_LOCAL_DEF( FT_Error ) |
136 | t42_parser_init( T42_Parser parser, |
137 | FT_Stream stream, |
138 | FT_Memory memory, |
139 | PSAux_Service psaux ) |
140 | { |
141 | FT_Error error = FT_Err_Ok; |
142 | FT_Long size; |
143 | |
144 | |
145 | psaux->ps_parser_funcs->init( &parser->root, NULL, NULL, memory ); |
146 | |
147 | parser->stream = stream; |
148 | parser->base_len = 0; |
149 | parser->base_dict = NULL; |
150 | parser->in_memory = 0; |
151 | |
152 | /******************************************************************** |
153 | * |
154 | * Here a short summary of what is going on: |
155 | * |
156 | * When creating a new Type 42 parser, we try to locate and load |
157 | * the base dictionary, loading the whole font into memory. |
158 | * |
159 | * When `loading' the base dictionary, we only set up pointers |
160 | * in the case of a memory-based stream. Otherwise, we allocate |
161 | * and load the base dictionary in it. |
162 | * |
163 | * parser->in_memory is set if we have a memory stream. |
164 | */ |
165 | |
166 | if ( FT_STREAM_SEEK( 0L ) || |
167 | FT_FRAME_ENTER( 17 ) ) |
168 | goto Exit; |
169 | |
170 | if ( ft_memcmp( stream->cursor, "%!PS-TrueTypeFont" , 17 ) != 0 ) |
171 | { |
172 | FT_TRACE2(( " not a Type42 font\n" )); |
173 | error = FT_THROW( Unknown_File_Format ); |
174 | } |
175 | |
176 | FT_FRAME_EXIT(); |
177 | |
178 | if ( error || FT_STREAM_SEEK( 0 ) ) |
179 | goto Exit; |
180 | |
181 | size = (FT_Long)stream->size; |
182 | |
183 | /* now, try to load `size' bytes of the `base' dictionary we */ |
184 | /* found previously */ |
185 | |
186 | /* if it is a memory-based resource, set up pointers */ |
187 | if ( !stream->read ) |
188 | { |
189 | parser->base_dict = (FT_Byte*)stream->base + stream->pos; |
190 | parser->base_len = size; |
191 | parser->in_memory = 1; |
192 | |
193 | /* check that the `size' field is valid */ |
194 | if ( FT_STREAM_SKIP( size ) ) |
195 | goto Exit; |
196 | } |
197 | else |
198 | { |
199 | /* read segment in memory */ |
200 | if ( FT_QALLOC( parser->base_dict, size ) || |
201 | FT_STREAM_READ( parser->base_dict, size ) ) |
202 | goto Exit; |
203 | |
204 | parser->base_len = size; |
205 | } |
206 | |
207 | parser->root.base = parser->base_dict; |
208 | parser->root.cursor = parser->base_dict; |
209 | parser->root.limit = parser->root.cursor + parser->base_len; |
210 | |
211 | Exit: |
212 | if ( error && !parser->in_memory ) |
213 | FT_FREE( parser->base_dict ); |
214 | |
215 | return error; |
216 | } |
217 | |
218 | |
219 | FT_LOCAL_DEF( void ) |
220 | t42_parser_done( T42_Parser parser ) |
221 | { |
222 | FT_Memory memory = parser->root.memory; |
223 | |
224 | |
225 | /* free the base dictionary only when we have a disk stream */ |
226 | if ( !parser->in_memory ) |
227 | FT_FREE( parser->base_dict ); |
228 | |
229 | if ( parser->root.funcs.done ) |
230 | parser->root.funcs.done( &parser->root ); |
231 | } |
232 | |
233 | |
234 | static int |
235 | t42_is_space( FT_Byte c ) |
236 | { |
237 | return ( c == ' ' || c == '\t' || |
238 | c == '\r' || c == '\n' || c == '\f' || |
239 | c == '\0' ); |
240 | } |
241 | |
242 | |
243 | static void |
244 | t42_parse_font_matrix( FT_Face face, /* T42_Face */ |
245 | void* loader_ ) |
246 | { |
247 | T42_Face t42face = (T42_Face)face; |
248 | T42_Loader loader = (T42_Loader)loader_; |
249 | T42_Parser parser = &loader->parser; |
250 | FT_Matrix* matrix = &t42face->type1.font_matrix; |
251 | FT_Vector* offset = &t42face->type1.font_offset; |
252 | FT_Fixed temp[6]; |
253 | FT_Fixed temp_scale; |
254 | FT_Int result; |
255 | |
256 | |
257 | result = T1_ToFixedArray( parser, 6, temp, 0 ); |
258 | |
259 | if ( result < 6 ) |
260 | { |
261 | parser->root.error = FT_THROW( Invalid_File_Format ); |
262 | return; |
263 | } |
264 | |
265 | temp_scale = FT_ABS( temp[3] ); |
266 | |
267 | if ( temp_scale == 0 ) |
268 | { |
269 | FT_ERROR(( "t42_parse_font_matrix: invalid font matrix\n" )); |
270 | parser->root.error = FT_THROW( Invalid_File_Format ); |
271 | return; |
272 | } |
273 | |
274 | /* atypical case */ |
275 | if ( temp_scale != 0x10000L ) |
276 | { |
277 | temp[0] = FT_DivFix( temp[0], temp_scale ); |
278 | temp[1] = FT_DivFix( temp[1], temp_scale ); |
279 | temp[2] = FT_DivFix( temp[2], temp_scale ); |
280 | temp[4] = FT_DivFix( temp[4], temp_scale ); |
281 | temp[5] = FT_DivFix( temp[5], temp_scale ); |
282 | temp[3] = temp[3] < 0 ? -0x10000L : 0x10000L; |
283 | } |
284 | |
285 | matrix->xx = temp[0]; |
286 | matrix->yx = temp[1]; |
287 | matrix->xy = temp[2]; |
288 | matrix->yy = temp[3]; |
289 | |
290 | if ( !FT_Matrix_Check( matrix ) ) |
291 | { |
292 | FT_ERROR(( "t42_parse_font_matrix: invalid font matrix\n" )); |
293 | parser->root.error = FT_THROW( Invalid_File_Format ); |
294 | return; |
295 | } |
296 | |
297 | /* note that the offsets must be expressed in integer font units */ |
298 | offset->x = temp[4] >> 16; |
299 | offset->y = temp[5] >> 16; |
300 | } |
301 | |
302 | |
303 | static void |
304 | t42_parse_encoding( FT_Face face, |
305 | void* loader_ ) |
306 | { |
307 | T42_Face t42face = (T42_Face)face; |
308 | T42_Loader loader = (T42_Loader)loader_; |
309 | T42_Parser parser = &loader->parser; |
310 | FT_Byte* cur; |
311 | FT_Byte* limit = parser->root.limit; |
312 | |
313 | PSAux_Service psaux = (PSAux_Service)t42face->psaux; |
314 | |
315 | |
316 | T1_Skip_Spaces( parser ); |
317 | cur = parser->root.cursor; |
318 | if ( cur >= limit ) |
319 | { |
320 | FT_ERROR(( "t42_parse_encoding: out of bounds\n" )); |
321 | parser->root.error = FT_THROW( Invalid_File_Format ); |
322 | return; |
323 | } |
324 | |
325 | /* if we have a number or `[', the encoding is an array, */ |
326 | /* and we must load it now */ |
327 | if ( ft_isdigit( *cur ) || *cur == '[' ) |
328 | { |
329 | T1_Encoding encode = &t42face->type1.encoding; |
330 | FT_Int count, n; |
331 | PS_Table char_table = &loader->encoding_table; |
332 | FT_Memory memory = parser->root.memory; |
333 | FT_Error error; |
334 | FT_Bool only_immediates = 0; |
335 | |
336 | |
337 | /* read the number of entries in the encoding; should be 256 */ |
338 | if ( *cur == '[' ) |
339 | { |
340 | count = 256; |
341 | only_immediates = 1; |
342 | parser->root.cursor++; |
343 | } |
344 | else |
345 | count = (FT_Int)T1_ToInt( parser ); |
346 | |
347 | /* only composite fonts (which we don't support) */ |
348 | /* can have larger values */ |
349 | if ( count > 256 ) |
350 | { |
351 | FT_ERROR(( "t42_parse_encoding: invalid encoding array size\n" )); |
352 | parser->root.error = FT_THROW( Invalid_File_Format ); |
353 | return; |
354 | } |
355 | |
356 | T1_Skip_Spaces( parser ); |
357 | if ( parser->root.cursor >= limit ) |
358 | return; |
359 | |
360 | /* PostScript happily allows overwriting of encoding arrays */ |
361 | if ( encode->char_index ) |
362 | { |
363 | FT_FREE( encode->char_index ); |
364 | FT_FREE( encode->char_name ); |
365 | T1_Release_Table( char_table ); |
366 | } |
367 | |
368 | /* we use a T1_Table to store our charnames */ |
369 | loader->num_chars = encode->num_chars = count; |
370 | if ( FT_QNEW_ARRAY( encode->char_index, count ) || |
371 | FT_QNEW_ARRAY( encode->char_name, count ) || |
372 | FT_SET_ERROR( psaux->ps_table_funcs->init( |
373 | char_table, count, memory ) ) ) |
374 | { |
375 | parser->root.error = error; |
376 | return; |
377 | } |
378 | |
379 | /* We need to `zero' out encoding_table.elements */ |
380 | for ( n = 0; n < count; n++ ) |
381 | (void)T1_Add_Table( char_table, n, ".notdef" , 8 ); |
382 | |
383 | /* Now we need to read records of the form */ |
384 | /* */ |
385 | /* ... charcode /charname ... */ |
386 | /* */ |
387 | /* for each entry in our table. */ |
388 | /* */ |
389 | /* We simply look for a number followed by an immediate */ |
390 | /* name. Note that this ignores correctly the sequence */ |
391 | /* that is often seen in type42 fonts: */ |
392 | /* */ |
393 | /* 0 1 255 { 1 index exch /.notdef put } for dup */ |
394 | /* */ |
395 | /* used to clean the encoding array before anything else. */ |
396 | /* */ |
397 | /* Alternatively, if the array is directly given as */ |
398 | /* */ |
399 | /* /Encoding [ ... ] */ |
400 | /* */ |
401 | /* we only read immediates. */ |
402 | |
403 | n = 0; |
404 | T1_Skip_Spaces( parser ); |
405 | |
406 | while ( parser->root.cursor < limit ) |
407 | { |
408 | cur = parser->root.cursor; |
409 | |
410 | /* we stop when we encounter `def' or `]' */ |
411 | if ( *cur == 'd' && cur + 3 < limit ) |
412 | { |
413 | if ( cur[1] == 'e' && |
414 | cur[2] == 'f' && |
415 | t42_is_space( cur[3] ) ) |
416 | { |
417 | FT_TRACE6(( "encoding end\n" )); |
418 | cur += 3; |
419 | break; |
420 | } |
421 | } |
422 | if ( *cur == ']' ) |
423 | { |
424 | FT_TRACE6(( "encoding end\n" )); |
425 | cur++; |
426 | break; |
427 | } |
428 | |
429 | /* check whether we have found an entry */ |
430 | if ( ft_isdigit( *cur ) || only_immediates ) |
431 | { |
432 | FT_Int charcode; |
433 | |
434 | |
435 | if ( only_immediates ) |
436 | charcode = n; |
437 | else |
438 | { |
439 | charcode = (FT_Int)T1_ToInt( parser ); |
440 | T1_Skip_Spaces( parser ); |
441 | |
442 | /* protect against invalid charcode */ |
443 | if ( cur == parser->root.cursor ) |
444 | { |
445 | parser->root.error = FT_THROW( Unknown_File_Format ); |
446 | return; |
447 | } |
448 | } |
449 | |
450 | cur = parser->root.cursor; |
451 | |
452 | if ( cur + 2 < limit && *cur == '/' && n < count ) |
453 | { |
454 | FT_UInt len; |
455 | |
456 | |
457 | cur++; |
458 | |
459 | parser->root.cursor = cur; |
460 | T1_Skip_PS_Token( parser ); |
461 | if ( parser->root.cursor >= limit ) |
462 | return; |
463 | if ( parser->root.error ) |
464 | return; |
465 | |
466 | len = (FT_UInt)( parser->root.cursor - cur ); |
467 | |
468 | parser->root.error = T1_Add_Table( char_table, charcode, |
469 | cur, len + 1 ); |
470 | if ( parser->root.error ) |
471 | return; |
472 | char_table->elements[charcode][len] = '\0'; |
473 | |
474 | n++; |
475 | } |
476 | else if ( only_immediates ) |
477 | { |
478 | /* Since the current position is not updated for */ |
479 | /* immediates-only mode we would get an infinite loop if */ |
480 | /* we don't do anything here. */ |
481 | /* */ |
482 | /* This encoding array is not valid according to the */ |
483 | /* type42 specification (it might be an encoding for a CID */ |
484 | /* type42 font, however), so we conclude that this font is */ |
485 | /* NOT a type42 font. */ |
486 | parser->root.error = FT_THROW( Unknown_File_Format ); |
487 | return; |
488 | } |
489 | } |
490 | else |
491 | { |
492 | T1_Skip_PS_Token( parser ); |
493 | if ( parser->root.error ) |
494 | return; |
495 | } |
496 | |
497 | T1_Skip_Spaces( parser ); |
498 | } |
499 | |
500 | t42face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY; |
501 | parser->root.cursor = cur; |
502 | } |
503 | |
504 | /* Otherwise, we should have either `StandardEncoding', */ |
505 | /* `ExpertEncoding', or `ISOLatin1Encoding' */ |
506 | else |
507 | { |
508 | if ( cur + 17 < limit && |
509 | ft_strncmp( (const char*)cur, "StandardEncoding" , 16 ) == 0 ) |
510 | t42face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD; |
511 | |
512 | else if ( cur + 15 < limit && |
513 | ft_strncmp( (const char*)cur, "ExpertEncoding" , 14 ) == 0 ) |
514 | t42face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT; |
515 | |
516 | else if ( cur + 18 < limit && |
517 | ft_strncmp( (const char*)cur, "ISOLatin1Encoding" , 17 ) == 0 ) |
518 | t42face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1; |
519 | |
520 | else |
521 | parser->root.error = FT_ERR( Ignore ); |
522 | } |
523 | } |
524 | |
525 | |
526 | typedef enum T42_Load_Status_ |
527 | { |
528 | BEFORE_START, |
529 | BEFORE_TABLE_DIR, |
530 | OTHER_TABLES |
531 | |
532 | } T42_Load_Status; |
533 | |
534 | |
535 | static void |
536 | t42_parse_sfnts( FT_Face face, |
537 | void* loader_ ) |
538 | { |
539 | T42_Face t42face = (T42_Face)face; |
540 | T42_Loader loader = (T42_Loader)loader_; |
541 | T42_Parser parser = &loader->parser; |
542 | FT_Memory memory = parser->root.memory; |
543 | FT_Byte* cur; |
544 | FT_Byte* limit = parser->root.limit; |
545 | FT_Error error; |
546 | FT_Int num_tables = 0; |
547 | FT_Long ttf_count; |
548 | FT_Long ttf_reserved; |
549 | |
550 | FT_ULong n, string_size, old_string_size, real_size; |
551 | FT_Byte* string_buf = NULL; |
552 | FT_Bool allocated = 0; |
553 | |
554 | T42_Load_Status status; |
555 | |
556 | /** There should only be one sfnts array, but free any previous. */ |
557 | FT_FREE( t42face->ttf_data ); |
558 | t42face->ttf_size = 0; |
559 | |
560 | /* The format is */ |
561 | /* */ |
562 | /* /sfnts [ <hexstring> <hexstring> ... ] def */ |
563 | /* */ |
564 | /* or */ |
565 | /* */ |
566 | /* /sfnts [ */ |
567 | /* <num_bin_bytes> RD <binary data> */ |
568 | /* <num_bin_bytes> RD <binary data> */ |
569 | /* ... */ |
570 | /* ] def */ |
571 | /* */ |
572 | /* with exactly one space after the `RD' token. */ |
573 | |
574 | T1_Skip_Spaces( parser ); |
575 | |
576 | if ( parser->root.cursor >= limit || *parser->root.cursor++ != '[' ) |
577 | { |
578 | FT_ERROR(( "t42_parse_sfnts: can't find begin of sfnts vector\n" )); |
579 | error = FT_THROW( Invalid_File_Format ); |
580 | goto Fail; |
581 | } |
582 | |
583 | T1_Skip_Spaces( parser ); |
584 | status = BEFORE_START; |
585 | string_size = 0; |
586 | old_string_size = 0; |
587 | ttf_count = 0; |
588 | ttf_reserved = 12; |
589 | if ( FT_QALLOC( t42face->ttf_data, ttf_reserved ) ) |
590 | goto Fail; |
591 | |
592 | FT_TRACE2(( "\n" )); |
593 | FT_TRACE2(( "t42_parse_sfnts:\n" )); |
594 | |
595 | while ( parser->root.cursor < limit ) |
596 | { |
597 | FT_ULong size; |
598 | |
599 | |
600 | cur = parser->root.cursor; |
601 | |
602 | if ( *cur == ']' ) |
603 | { |
604 | parser->root.cursor++; |
605 | t42face->ttf_size = ttf_count; |
606 | goto Exit; |
607 | } |
608 | |
609 | else if ( *cur == '<' ) |
610 | { |
611 | if ( string_buf && !allocated ) |
612 | { |
613 | FT_ERROR(( "t42_parse_sfnts: " |
614 | "can't handle mixed binary and hex strings\n" )); |
615 | error = FT_THROW( Invalid_File_Format ); |
616 | goto Fail; |
617 | } |
618 | |
619 | T1_Skip_PS_Token( parser ); |
620 | if ( parser->root.error ) |
621 | goto Exit; |
622 | |
623 | /* don't include delimiters */ |
624 | string_size = (FT_ULong)( ( parser->root.cursor - cur - 2 + 1 ) / 2 ); |
625 | if ( !string_size ) |
626 | { |
627 | FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); |
628 | error = FT_THROW( Invalid_File_Format ); |
629 | goto Fail; |
630 | } |
631 | if ( FT_QREALLOC( string_buf, old_string_size, string_size ) ) |
632 | goto Fail; |
633 | |
634 | allocated = 1; |
635 | |
636 | parser->root.cursor = cur; |
637 | (void)T1_ToBytes( parser, string_buf, string_size, &real_size, 1 ); |
638 | old_string_size = string_size; |
639 | string_size = real_size; |
640 | } |
641 | |
642 | else if ( ft_isdigit( *cur ) ) |
643 | { |
644 | FT_Long tmp; |
645 | |
646 | |
647 | if ( allocated ) |
648 | { |
649 | FT_ERROR(( "t42_parse_sfnts: " |
650 | "can't handle mixed binary and hex strings\n" )); |
651 | error = FT_THROW( Invalid_File_Format ); |
652 | goto Fail; |
653 | } |
654 | |
655 | tmp = T1_ToInt( parser ); |
656 | if ( tmp < 0 ) |
657 | { |
658 | FT_ERROR(( "t42_parse_sfnts: invalid string size\n" )); |
659 | error = FT_THROW( Invalid_File_Format ); |
660 | goto Fail; |
661 | } |
662 | else |
663 | string_size = (FT_ULong)tmp; |
664 | |
665 | T1_Skip_PS_Token( parser ); /* `RD' */ |
666 | if ( parser->root.error ) |
667 | return; |
668 | |
669 | string_buf = parser->root.cursor + 1; /* one space after `RD' */ |
670 | |
671 | if ( (FT_ULong)( limit - parser->root.cursor ) <= string_size ) |
672 | { |
673 | FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); |
674 | error = FT_THROW( Invalid_File_Format ); |
675 | goto Fail; |
676 | } |
677 | else |
678 | parser->root.cursor += string_size + 1; |
679 | } |
680 | |
681 | if ( !string_buf ) |
682 | { |
683 | FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); |
684 | error = FT_THROW( Invalid_File_Format ); |
685 | goto Fail; |
686 | } |
687 | |
688 | /* A string can have a trailing zero (odd) byte for padding. */ |
689 | /* Ignore it. */ |
690 | if ( ( string_size & 1 ) && string_buf[string_size - 1] == 0 ) |
691 | string_size--; |
692 | |
693 | if ( !string_size ) |
694 | { |
695 | FT_ERROR(( "t42_parse_sfnts: invalid string\n" )); |
696 | error = FT_THROW( Invalid_File_Format ); |
697 | goto Fail; |
698 | } |
699 | |
700 | FT_TRACE2(( " PS string size %5lu bytes, offset 0x%08lx (%lu)\n" , |
701 | string_size, ttf_count, ttf_count )); |
702 | |
703 | /* The whole TTF is now loaded into `string_buf'. We are */ |
704 | /* checking its contents while copying it to `ttf_data'. */ |
705 | |
706 | size = (FT_ULong)( limit - parser->root.cursor ); |
707 | |
708 | for ( n = 0; n < string_size; n++ ) |
709 | { |
710 | switch ( status ) |
711 | { |
712 | case BEFORE_START: |
713 | /* load offset table, 12 bytes */ |
714 | if ( ttf_count < 12 ) |
715 | { |
716 | t42face->ttf_data[ttf_count++] = string_buf[n]; |
717 | continue; |
718 | } |
719 | else |
720 | { |
721 | FT_Long ttf_reserved_prev = ttf_reserved; |
722 | |
723 | |
724 | num_tables = 16 * t42face->ttf_data[4] + t42face->ttf_data[5]; |
725 | status = BEFORE_TABLE_DIR; |
726 | ttf_reserved = 12 + 16 * num_tables; |
727 | |
728 | FT_TRACE2(( " SFNT directory contains %d tables\n" , |
729 | num_tables )); |
730 | |
731 | if ( (FT_Long)size < ttf_reserved ) |
732 | { |
733 | FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); |
734 | error = FT_THROW( Invalid_File_Format ); |
735 | goto Fail; |
736 | } |
737 | |
738 | if ( FT_QREALLOC( t42face->ttf_data, ttf_reserved_prev, |
739 | ttf_reserved ) ) |
740 | goto Fail; |
741 | } |
742 | FALL_THROUGH; |
743 | |
744 | case BEFORE_TABLE_DIR: |
745 | /* the offset table is read; read the table directory */ |
746 | if ( ttf_count < ttf_reserved ) |
747 | { |
748 | t42face->ttf_data[ttf_count++] = string_buf[n]; |
749 | continue; |
750 | } |
751 | else |
752 | { |
753 | int i; |
754 | FT_ULong len; |
755 | FT_Long ttf_reserved_prev = ttf_reserved; |
756 | |
757 | |
758 | FT_TRACE2(( "\n" )); |
759 | FT_TRACE2(( " table length\n" )); |
760 | FT_TRACE2(( " ------------------------------\n" )); |
761 | |
762 | for ( i = 0; i < num_tables; i++ ) |
763 | { |
764 | FT_Byte* p = t42face->ttf_data + 12 + 16 * i + 12; |
765 | |
766 | |
767 | len = FT_PEEK_ULONG( p ); |
768 | FT_TRACE2(( " %4i 0x%08lx (%lu)\n" , i, len, len )); |
769 | |
770 | if ( len > size || |
771 | ttf_reserved > (FT_Long)( size - len ) ) |
772 | { |
773 | FT_ERROR(( "t42_parse_sfnts:" |
774 | " invalid data in sfnts array\n" )); |
775 | error = FT_THROW( Invalid_File_Format ); |
776 | goto Fail; |
777 | } |
778 | |
779 | /* Pad to a 4-byte boundary length */ |
780 | ttf_reserved += (FT_Long)( ( len + 3 ) & ~3U ); |
781 | } |
782 | ttf_reserved += 1; |
783 | |
784 | status = OTHER_TABLES; |
785 | |
786 | FT_TRACE2(( "\n" )); |
787 | FT_TRACE2(( " allocating %ld bytes\n" , ttf_reserved )); |
788 | FT_TRACE2(( "\n" )); |
789 | |
790 | if ( FT_QREALLOC( t42face->ttf_data, ttf_reserved_prev, |
791 | ttf_reserved ) ) |
792 | goto Fail; |
793 | } |
794 | FALL_THROUGH; |
795 | |
796 | case OTHER_TABLES: |
797 | /* all other tables are just copied */ |
798 | if ( ttf_count >= ttf_reserved ) |
799 | { |
800 | FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); |
801 | error = FT_THROW( Invalid_File_Format ); |
802 | goto Fail; |
803 | } |
804 | t42face->ttf_data[ttf_count++] = string_buf[n]; |
805 | } |
806 | } |
807 | |
808 | T1_Skip_Spaces( parser ); |
809 | } |
810 | |
811 | /* if control reaches this point, the format was not valid */ |
812 | error = FT_THROW( Invalid_File_Format ); |
813 | |
814 | Fail: |
815 | parser->root.error = error; |
816 | |
817 | Exit: |
818 | if ( parser->root.error ) |
819 | { |
820 | FT_FREE( t42face->ttf_data ); |
821 | t42face->ttf_size = 0; |
822 | } |
823 | if ( allocated ) |
824 | FT_FREE( string_buf ); |
825 | } |
826 | |
827 | |
828 | static void |
829 | t42_parse_charstrings( FT_Face face, /* T42_Face */ |
830 | void* loader_ ) |
831 | { |
832 | T42_Face t42face = (T42_Face)face; |
833 | T42_Loader loader = (T42_Loader)loader_; |
834 | T42_Parser parser = &loader->parser; |
835 | PS_Table code_table = &loader->charstrings; |
836 | PS_Table name_table = &loader->glyph_names; |
837 | PS_Table swap_table = &loader->swap_table; |
838 | FT_Memory memory = parser->root.memory; |
839 | FT_Error error; |
840 | |
841 | PSAux_Service psaux = (PSAux_Service)t42face->psaux; |
842 | |
843 | FT_Byte* cur; |
844 | FT_Byte* limit = parser->root.limit; |
845 | FT_Int n; |
846 | FT_Int notdef_index = 0; |
847 | FT_Byte notdef_found = 0; |
848 | |
849 | |
850 | T1_Skip_Spaces( parser ); |
851 | |
852 | if ( parser->root.cursor >= limit ) |
853 | { |
854 | FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
855 | error = FT_THROW( Invalid_File_Format ); |
856 | goto Fail; |
857 | } |
858 | |
859 | if ( ft_isdigit( *parser->root.cursor ) ) |
860 | { |
861 | loader->num_glyphs = T1_ToInt( parser ); |
862 | if ( parser->root.error ) |
863 | return; |
864 | if ( loader->num_glyphs < 0 ) |
865 | { |
866 | FT_ERROR(( "t42_parse_encoding: invalid number of glyphs\n" )); |
867 | error = FT_THROW( Invalid_File_Format ); |
868 | goto Fail; |
869 | } |
870 | |
871 | /* we certainly need more than 4 bytes per glyph */ |
872 | if ( loader->num_glyphs > ( limit - parser->root.cursor ) >> 2 ) |
873 | { |
874 | FT_TRACE0(( "t42_parse_charstrings: adjusting number of glyphs" |
875 | " (from %d to %zu)\n" , |
876 | loader->num_glyphs, |
877 | ( limit - parser->root.cursor ) >> 2 )); |
878 | loader->num_glyphs = ( limit - parser->root.cursor ) >> 2; |
879 | } |
880 | |
881 | } |
882 | else if ( *parser->root.cursor == '<' ) |
883 | { |
884 | /* We have `<< ... >>'. Count the number of `/' in the dictionary */ |
885 | /* to get its size. */ |
886 | FT_Int count = 0; |
887 | |
888 | |
889 | T1_Skip_PS_Token( parser ); |
890 | if ( parser->root.error ) |
891 | return; |
892 | T1_Skip_Spaces( parser ); |
893 | cur = parser->root.cursor; |
894 | |
895 | while ( parser->root.cursor < limit ) |
896 | { |
897 | if ( *parser->root.cursor == '/' ) |
898 | count++; |
899 | else if ( *parser->root.cursor == '>' ) |
900 | { |
901 | loader->num_glyphs = count; |
902 | parser->root.cursor = cur; /* rewind */ |
903 | break; |
904 | } |
905 | T1_Skip_PS_Token( parser ); |
906 | if ( parser->root.error ) |
907 | return; |
908 | T1_Skip_Spaces( parser ); |
909 | } |
910 | } |
911 | else |
912 | { |
913 | FT_ERROR(( "t42_parse_charstrings: invalid token\n" )); |
914 | error = FT_THROW( Invalid_File_Format ); |
915 | goto Fail; |
916 | } |
917 | |
918 | if ( parser->root.cursor >= limit ) |
919 | { |
920 | FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
921 | error = FT_THROW( Invalid_File_Format ); |
922 | goto Fail; |
923 | } |
924 | |
925 | /* initialize tables */ |
926 | |
927 | /* contrary to Type1, we disallow multiple CharStrings arrays */ |
928 | if ( swap_table->init ) |
929 | { |
930 | FT_ERROR(( "t42_parse_charstrings:" |
931 | " only one CharStrings array allowed\n" )); |
932 | error = FT_THROW( Invalid_File_Format ); |
933 | goto Fail; |
934 | } |
935 | |
936 | error = psaux->ps_table_funcs->init( code_table, |
937 | loader->num_glyphs, |
938 | memory ); |
939 | if ( error ) |
940 | goto Fail; |
941 | |
942 | error = psaux->ps_table_funcs->init( name_table, |
943 | loader->num_glyphs, |
944 | memory ); |
945 | if ( error ) |
946 | goto Fail; |
947 | |
948 | /* Initialize table for swapping index notdef_index and */ |
949 | /* index 0 names and codes (if necessary). */ |
950 | |
951 | error = psaux->ps_table_funcs->init( swap_table, 4, memory ); |
952 | if ( error ) |
953 | goto Fail; |
954 | |
955 | n = 0; |
956 | |
957 | for (;;) |
958 | { |
959 | /* We support two formats. */ |
960 | /* */ |
961 | /* `/glyphname' + index [+ `def'] */ |
962 | /* `(glyphname)' [+ `cvn'] + index [+ `def'] */ |
963 | /* */ |
964 | /* The latter format gets created by the */ |
965 | /* LilyPond typesetting program. */ |
966 | |
967 | T1_Skip_Spaces( parser ); |
968 | |
969 | cur = parser->root.cursor; |
970 | if ( cur >= limit ) |
971 | break; |
972 | |
973 | /* We stop when we find an `end' keyword or '>' */ |
974 | if ( *cur == 'e' && |
975 | cur + 3 < limit && |
976 | cur[1] == 'n' && |
977 | cur[2] == 'd' && |
978 | t42_is_space( cur[3] ) ) |
979 | break; |
980 | if ( *cur == '>' ) |
981 | break; |
982 | |
983 | T1_Skip_PS_Token( parser ); |
984 | if ( parser->root.cursor >= limit ) |
985 | { |
986 | FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
987 | error = FT_THROW( Invalid_File_Format ); |
988 | goto Fail; |
989 | } |
990 | if ( parser->root.error ) |
991 | return; |
992 | |
993 | if ( *cur == '/' || *cur == '(' ) |
994 | { |
995 | FT_UInt len; |
996 | FT_Bool have_literal = FT_BOOL( *cur == '(' ); |
997 | |
998 | |
999 | if ( cur + ( have_literal ? 3 : 2 ) >= limit ) |
1000 | { |
1001 | FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
1002 | error = FT_THROW( Invalid_File_Format ); |
1003 | goto Fail; |
1004 | } |
1005 | |
1006 | cur++; /* skip `/' */ |
1007 | len = (FT_UInt)( parser->root.cursor - cur ); |
1008 | if ( have_literal ) |
1009 | len--; |
1010 | |
1011 | error = T1_Add_Table( name_table, n, cur, len + 1 ); |
1012 | if ( error ) |
1013 | goto Fail; |
1014 | |
1015 | /* add a trailing zero to the name table */ |
1016 | name_table->elements[n][len] = '\0'; |
1017 | |
1018 | /* record index of /.notdef */ |
1019 | if ( *cur == '.' && |
1020 | ft_strcmp( ".notdef" , |
1021 | (const char*)( name_table->elements[n] ) ) == 0 ) |
1022 | { |
1023 | notdef_index = n; |
1024 | notdef_found = 1; |
1025 | } |
1026 | |
1027 | T1_Skip_Spaces( parser ); |
1028 | |
1029 | if ( have_literal ) |
1030 | T1_Skip_PS_Token( parser ); |
1031 | |
1032 | cur = parser->root.cursor; |
1033 | |
1034 | (void)T1_ToInt( parser ); |
1035 | if ( parser->root.cursor >= limit ) |
1036 | { |
1037 | FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
1038 | error = FT_THROW( Invalid_File_Format ); |
1039 | goto Fail; |
1040 | } |
1041 | |
1042 | len = (FT_UInt)( parser->root.cursor - cur ); |
1043 | |
1044 | error = T1_Add_Table( code_table, n, cur, len + 1 ); |
1045 | if ( error ) |
1046 | goto Fail; |
1047 | |
1048 | code_table->elements[n][len] = '\0'; |
1049 | |
1050 | n++; |
1051 | if ( n >= loader->num_glyphs ) |
1052 | break; |
1053 | } |
1054 | } |
1055 | |
1056 | loader->num_glyphs = n; |
1057 | |
1058 | if ( !notdef_found ) |
1059 | { |
1060 | FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph\n" )); |
1061 | error = FT_THROW( Invalid_File_Format ); |
1062 | goto Fail; |
1063 | } |
1064 | |
1065 | /* if /.notdef does not occupy index 0, do our magic. */ |
1066 | if ( ft_strcmp( ".notdef" , (const char*)name_table->elements[0] ) ) |
1067 | { |
1068 | /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */ |
1069 | /* name and code entries to swap_table. Then place notdef_index */ |
1070 | /* name and code entries into swap_table. Then swap name and code */ |
1071 | /* entries at indices notdef_index and 0 using values stored in */ |
1072 | /* swap_table. */ |
1073 | |
1074 | /* Index 0 name */ |
1075 | error = T1_Add_Table( swap_table, 0, |
1076 | name_table->elements[0], |
1077 | name_table->lengths [0] ); |
1078 | if ( error ) |
1079 | goto Fail; |
1080 | |
1081 | /* Index 0 code */ |
1082 | error = T1_Add_Table( swap_table, 1, |
1083 | code_table->elements[0], |
1084 | code_table->lengths [0] ); |
1085 | if ( error ) |
1086 | goto Fail; |
1087 | |
1088 | /* Index notdef_index name */ |
1089 | error = T1_Add_Table( swap_table, 2, |
1090 | name_table->elements[notdef_index], |
1091 | name_table->lengths [notdef_index] ); |
1092 | if ( error ) |
1093 | goto Fail; |
1094 | |
1095 | /* Index notdef_index code */ |
1096 | error = T1_Add_Table( swap_table, 3, |
1097 | code_table->elements[notdef_index], |
1098 | code_table->lengths [notdef_index] ); |
1099 | if ( error ) |
1100 | goto Fail; |
1101 | |
1102 | error = T1_Add_Table( name_table, notdef_index, |
1103 | swap_table->elements[0], |
1104 | swap_table->lengths [0] ); |
1105 | if ( error ) |
1106 | goto Fail; |
1107 | |
1108 | error = T1_Add_Table( code_table, notdef_index, |
1109 | swap_table->elements[1], |
1110 | swap_table->lengths [1] ); |
1111 | if ( error ) |
1112 | goto Fail; |
1113 | |
1114 | error = T1_Add_Table( name_table, 0, |
1115 | swap_table->elements[2], |
1116 | swap_table->lengths [2] ); |
1117 | if ( error ) |
1118 | goto Fail; |
1119 | |
1120 | error = T1_Add_Table( code_table, 0, |
1121 | swap_table->elements[3], |
1122 | swap_table->lengths [3] ); |
1123 | if ( error ) |
1124 | goto Fail; |
1125 | |
1126 | } |
1127 | |
1128 | return; |
1129 | |
1130 | Fail: |
1131 | parser->root.error = error; |
1132 | } |
1133 | |
1134 | |
1135 | static FT_Error |
1136 | t42_load_keyword( T42_Face face, |
1137 | T42_Loader loader, |
1138 | T1_Field field ) |
1139 | { |
1140 | FT_Error error; |
1141 | void* dummy_object; |
1142 | void** objects; |
1143 | FT_UInt max_objects = 0; |
1144 | |
1145 | |
1146 | /* if the keyword has a dedicated callback, call it */ |
1147 | if ( field->type == T1_FIELD_TYPE_CALLBACK ) |
1148 | { |
1149 | field->reader( (FT_Face)face, loader ); |
1150 | error = loader->parser.root.error; |
1151 | goto Exit; |
1152 | } |
1153 | |
1154 | /* now the keyword is either a simple field or a table of fields; */ |
1155 | /* we are now going to take care of it */ |
1156 | |
1157 | switch ( field->location ) |
1158 | { |
1159 | case T1_FIELD_LOCATION_FONT_INFO: |
1160 | dummy_object = &face->type1.font_info; |
1161 | break; |
1162 | |
1163 | case T1_FIELD_LOCATION_FONT_EXTRA: |
1164 | dummy_object = &face->type1.font_extra; |
1165 | break; |
1166 | |
1167 | case T1_FIELD_LOCATION_BBOX: |
1168 | dummy_object = &face->type1.font_bbox; |
1169 | break; |
1170 | |
1171 | default: |
1172 | dummy_object = &face->type1; |
1173 | } |
1174 | |
1175 | objects = &dummy_object; |
1176 | |
1177 | if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY || |
1178 | field->type == T1_FIELD_TYPE_FIXED_ARRAY ) |
1179 | error = T1_Load_Field_Table( &loader->parser, field, |
1180 | objects, max_objects, 0 ); |
1181 | else |
1182 | error = T1_Load_Field( &loader->parser, field, |
1183 | objects, max_objects, 0 ); |
1184 | |
1185 | Exit: |
1186 | return error; |
1187 | } |
1188 | |
1189 | |
1190 | FT_LOCAL_DEF( FT_Error ) |
1191 | t42_parse_dict( T42_Face face, |
1192 | T42_Loader loader, |
1193 | FT_Byte* base, |
1194 | FT_Long size ) |
1195 | { |
1196 | T42_Parser parser = &loader->parser; |
1197 | FT_Byte* limit; |
1198 | FT_Int n_keywords = (FT_Int)( sizeof ( t42_keywords ) / |
1199 | sizeof ( t42_keywords[0] ) ); |
1200 | |
1201 | |
1202 | parser->root.cursor = base; |
1203 | parser->root.limit = base + size; |
1204 | parser->root.error = FT_Err_Ok; |
1205 | |
1206 | limit = parser->root.limit; |
1207 | |
1208 | T1_Skip_Spaces( parser ); |
1209 | |
1210 | while ( parser->root.cursor < limit ) |
1211 | { |
1212 | FT_Byte* cur; |
1213 | |
1214 | |
1215 | cur = parser->root.cursor; |
1216 | |
1217 | /* look for `FontDirectory' which causes problems for some fonts */ |
1218 | if ( *cur == 'F' && cur + 25 < limit && |
1219 | ft_strncmp( (char*)cur, "FontDirectory" , 13 ) == 0 ) |
1220 | { |
1221 | FT_Byte* cur2; |
1222 | |
1223 | |
1224 | /* skip the `FontDirectory' keyword */ |
1225 | T1_Skip_PS_Token( parser ); |
1226 | T1_Skip_Spaces ( parser ); |
1227 | cur = cur2 = parser->root.cursor; |
1228 | |
1229 | /* look up the `known' keyword */ |
1230 | while ( cur < limit ) |
1231 | { |
1232 | if ( *cur == 'k' && cur + 5 < limit && |
1233 | ft_strncmp( (char*)cur, "known" , 5 ) == 0 ) |
1234 | break; |
1235 | |
1236 | T1_Skip_PS_Token( parser ); |
1237 | if ( parser->root.error ) |
1238 | goto Exit; |
1239 | T1_Skip_Spaces ( parser ); |
1240 | cur = parser->root.cursor; |
1241 | } |
1242 | |
1243 | if ( cur < limit ) |
1244 | { |
1245 | T1_TokenRec token; |
1246 | |
1247 | |
1248 | /* skip the `known' keyword and the token following it */ |
1249 | T1_Skip_PS_Token( parser ); |
1250 | T1_ToToken( parser, &token ); |
1251 | |
1252 | /* if the last token was an array, skip it! */ |
1253 | if ( token.type == T1_TOKEN_TYPE_ARRAY ) |
1254 | cur2 = parser->root.cursor; |
1255 | } |
1256 | parser->root.cursor = cur2; |
1257 | } |
1258 | |
1259 | /* look for immediates */ |
1260 | else if ( *cur == '/' && cur + 2 < limit ) |
1261 | { |
1262 | FT_UInt len; |
1263 | |
1264 | |
1265 | cur++; |
1266 | |
1267 | parser->root.cursor = cur; |
1268 | T1_Skip_PS_Token( parser ); |
1269 | if ( parser->root.error ) |
1270 | goto Exit; |
1271 | |
1272 | len = (FT_UInt)( parser->root.cursor - cur ); |
1273 | |
1274 | if ( len > 0 && len < 22 && parser->root.cursor < limit ) |
1275 | { |
1276 | int i; |
1277 | |
1278 | |
1279 | /* now compare the immediate name to the keyword table */ |
1280 | |
1281 | /* loop through all known keywords */ |
1282 | for ( i = 0; i < n_keywords; i++ ) |
1283 | { |
1284 | T1_Field keyword = (T1_Field)&t42_keywords[i]; |
1285 | FT_Byte *name = (FT_Byte*)keyword->ident; |
1286 | |
1287 | |
1288 | if ( !name ) |
1289 | continue; |
1290 | |
1291 | if ( cur[0] == name[0] && |
1292 | len == ft_strlen( (const char *)name ) && |
1293 | ft_memcmp( cur, name, len ) == 0 ) |
1294 | { |
1295 | /* we found it -- run the parsing callback! */ |
1296 | parser->root.error = t42_load_keyword( face, |
1297 | loader, |
1298 | keyword ); |
1299 | if ( parser->root.error ) |
1300 | return parser->root.error; |
1301 | break; |
1302 | } |
1303 | } |
1304 | } |
1305 | } |
1306 | else |
1307 | { |
1308 | T1_Skip_PS_Token( parser ); |
1309 | if ( parser->root.error ) |
1310 | goto Exit; |
1311 | } |
1312 | |
1313 | T1_Skip_Spaces( parser ); |
1314 | } |
1315 | |
1316 | Exit: |
1317 | return parser->root.error; |
1318 | } |
1319 | |
1320 | |
1321 | FT_LOCAL_DEF( void ) |
1322 | t42_loader_init( T42_Loader loader, |
1323 | T42_Face face ) |
1324 | { |
1325 | FT_UNUSED( face ); |
1326 | |
1327 | FT_ZERO( loader ); |
1328 | loader->num_glyphs = 0; |
1329 | loader->num_chars = 0; |
1330 | |
1331 | /* initialize the tables -- simply set their `init' field to 0 */ |
1332 | loader->encoding_table.init = 0; |
1333 | loader->charstrings.init = 0; |
1334 | loader->glyph_names.init = 0; |
1335 | } |
1336 | |
1337 | |
1338 | FT_LOCAL_DEF( void ) |
1339 | t42_loader_done( T42_Loader loader ) |
1340 | { |
1341 | T42_Parser parser = &loader->parser; |
1342 | |
1343 | |
1344 | /* finalize tables */ |
1345 | T1_Release_Table( &loader->encoding_table ); |
1346 | T1_Release_Table( &loader->charstrings ); |
1347 | T1_Release_Table( &loader->glyph_names ); |
1348 | T1_Release_Table( &loader->swap_table ); |
1349 | |
1350 | /* finalize parser */ |
1351 | t42_parser_done( parser ); |
1352 | } |
1353 | |
1354 | |
1355 | /* END */ |
1356 | |