1 /***************************************************************************/ |
|
2 /* */ |
|
3 /* t42parse.c */ |
|
4 /* */ |
|
5 /* Type 42 font parser (body). */ |
|
6 /* */ |
|
7 /* Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 FT_INTERNAL_DEBUG_H |
|
22 #include FT_INTERNAL_STREAM_H |
|
23 #include FT_INTERNAL_POSTSCRIPT_AUX_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 trace_t42 |
|
34 |
|
35 |
|
36 static void |
|
37 t42_parse_font_matrix( T42_Face face, |
|
38 T42_Loader loader ); |
|
39 static void |
|
40 t42_parse_encoding( T42_Face face, |
|
41 T42_Loader loader ); |
|
42 |
|
43 static void |
|
44 t42_parse_charstrings( T42_Face face, |
|
45 T42_Loader loader ); |
|
46 |
|
47 static void |
|
48 t42_parse_sfnts( T42_Face face, |
|
49 T42_Loader 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_Done_Table( p ) \ |
|
108 do \ |
|
109 { \ |
|
110 if ( (p)->funcs.done ) \ |
|
111 (p)->funcs.done( p ); \ |
|
112 } while ( 0 ) |
|
113 #define T1_Release_Table( p ) \ |
|
114 do \ |
|
115 { \ |
|
116 if ( (p)->funcs.release ) \ |
|
117 (p)->funcs.release( p ); \ |
|
118 } while ( 0 ) |
|
119 |
|
120 #define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root ) |
|
121 #define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root ) |
|
122 |
|
123 #define T1_ToInt( p ) \ |
|
124 (p)->root.funcs.to_int( &(p)->root ) |
|
125 #define T1_ToBytes( p, b, m, n, d ) \ |
|
126 (p)->root.funcs.to_bytes( &(p)->root, b, m, n, d ) |
|
127 |
|
128 #define T1_ToFixedArray( p, m, f, t ) \ |
|
129 (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) |
|
130 #define T1_ToToken( p, t ) \ |
|
131 (p)->root.funcs.to_token( &(p)->root, t ) |
|
132 |
|
133 #define T1_Load_Field( p, f, o, m, pf ) \ |
|
134 (p)->root.funcs.load_field( &(p)->root, f, o, m, pf ) |
|
135 #define T1_Load_Field_Table( p, f, o, m, pf ) \ |
|
136 (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf ) |
|
137 |
|
138 |
|
139 /********************* Parsing Functions ******************/ |
|
140 |
|
141 FT_LOCAL_DEF( FT_Error ) |
|
142 t42_parser_init( T42_Parser parser, |
|
143 FT_Stream stream, |
|
144 FT_Memory memory, |
|
145 PSAux_Service psaux ) |
|
146 { |
|
147 FT_Error error = T42_Err_Ok; |
|
148 FT_Long size; |
|
149 |
|
150 |
|
151 psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory ); |
|
152 |
|
153 parser->stream = stream; |
|
154 parser->base_len = 0; |
|
155 parser->base_dict = 0; |
|
156 parser->in_memory = 0; |
|
157 |
|
158 /*******************************************************************/ |
|
159 /* */ |
|
160 /* Here a short summary of what is going on: */ |
|
161 /* */ |
|
162 /* When creating a new Type 42 parser, we try to locate and load */ |
|
163 /* the base dictionary, loading the whole font into memory. */ |
|
164 /* */ |
|
165 /* When `loading' the base dictionary, we only set up pointers */ |
|
166 /* in the case of a memory-based stream. Otherwise, we allocate */ |
|
167 /* and load the base dictionary in it. */ |
|
168 /* */ |
|
169 /* parser->in_memory is set if we have a memory stream. */ |
|
170 /* */ |
|
171 |
|
172 if ( FT_STREAM_SEEK( 0L ) || |
|
173 FT_FRAME_ENTER( 17 ) ) |
|
174 goto Exit; |
|
175 |
|
176 if ( ft_memcmp( stream->cursor, "%!PS-TrueTypeFont", 17 ) != 0 ) |
|
177 { |
|
178 FT_TRACE2(( "not a Type42 font\n" )); |
|
179 error = T42_Err_Unknown_File_Format; |
|
180 } |
|
181 |
|
182 FT_FRAME_EXIT(); |
|
183 |
|
184 if ( error || FT_STREAM_SEEK( 0 ) ) |
|
185 goto Exit; |
|
186 |
|
187 size = stream->size; |
|
188 |
|
189 /* now, try to load `size' bytes of the `base' dictionary we */ |
|
190 /* found previously */ |
|
191 |
|
192 /* if it is a memory-based resource, set up pointers */ |
|
193 if ( !stream->read ) |
|
194 { |
|
195 parser->base_dict = (FT_Byte*)stream->base + stream->pos; |
|
196 parser->base_len = size; |
|
197 parser->in_memory = 1; |
|
198 |
|
199 /* check that the `size' field is valid */ |
|
200 if ( FT_STREAM_SKIP( size ) ) |
|
201 goto Exit; |
|
202 } |
|
203 else |
|
204 { |
|
205 /* read segment in memory */ |
|
206 if ( FT_ALLOC( parser->base_dict, size ) || |
|
207 FT_STREAM_READ( parser->base_dict, size ) ) |
|
208 goto Exit; |
|
209 |
|
210 parser->base_len = size; |
|
211 } |
|
212 |
|
213 parser->root.base = parser->base_dict; |
|
214 parser->root.cursor = parser->base_dict; |
|
215 parser->root.limit = parser->root.cursor + parser->base_len; |
|
216 |
|
217 Exit: |
|
218 if ( error && !parser->in_memory ) |
|
219 FT_FREE( parser->base_dict ); |
|
220 |
|
221 return error; |
|
222 } |
|
223 |
|
224 |
|
225 FT_LOCAL_DEF( void ) |
|
226 t42_parser_done( T42_Parser parser ) |
|
227 { |
|
228 FT_Memory memory = parser->root.memory; |
|
229 |
|
230 |
|
231 /* free the base dictionary only when we have a disk stream */ |
|
232 if ( !parser->in_memory ) |
|
233 FT_FREE( parser->base_dict ); |
|
234 |
|
235 parser->root.funcs.done( &parser->root ); |
|
236 } |
|
237 |
|
238 |
|
239 static int |
|
240 t42_is_space( FT_Byte c ) |
|
241 { |
|
242 return ( c == ' ' || c == '\t' || |
|
243 c == '\r' || c == '\n' || c == '\f' || |
|
244 c == '\0' ); |
|
245 } |
|
246 |
|
247 |
|
248 static void |
|
249 t42_parse_font_matrix( T42_Face face, |
|
250 T42_Loader loader ) |
|
251 { |
|
252 T42_Parser parser = &loader->parser; |
|
253 FT_Matrix* matrix = &face->type1.font_matrix; |
|
254 FT_Vector* offset = &face->type1.font_offset; |
|
255 FT_Face root = (FT_Face)&face->root; |
|
256 FT_Fixed temp[6]; |
|
257 FT_Fixed temp_scale; |
|
258 |
|
259 |
|
260 (void)T1_ToFixedArray( parser, 6, temp, 3 ); |
|
261 |
|
262 temp_scale = FT_ABS( temp[3] ); |
|
263 |
|
264 /* Set Units per EM based on FontMatrix values. We set the value to */ |
|
265 /* 1000 / temp_scale, because temp_scale was already multiplied by */ |
|
266 /* 1000 (in t1_tofixed, from psobjs.c). */ |
|
267 |
|
268 root->units_per_EM = (FT_UShort)( FT_DivFix( 1000 * 0x10000L, |
|
269 temp_scale ) >> 16 ); |
|
270 |
|
271 /* we need to scale the values by 1.0/temp_scale */ |
|
272 if ( temp_scale != 0x10000L ) |
|
273 { |
|
274 temp[0] = FT_DivFix( temp[0], temp_scale ); |
|
275 temp[1] = FT_DivFix( temp[1], temp_scale ); |
|
276 temp[2] = FT_DivFix( temp[2], temp_scale ); |
|
277 temp[4] = FT_DivFix( temp[4], temp_scale ); |
|
278 temp[5] = FT_DivFix( temp[5], temp_scale ); |
|
279 temp[3] = 0x10000L; |
|
280 } |
|
281 |
|
282 matrix->xx = temp[0]; |
|
283 matrix->yx = temp[1]; |
|
284 matrix->xy = temp[2]; |
|
285 matrix->yy = temp[3]; |
|
286 |
|
287 /* note that the offsets must be expressed in integer font units */ |
|
288 offset->x = temp[4] >> 16; |
|
289 offset->y = temp[5] >> 16; |
|
290 } |
|
291 |
|
292 |
|
293 static void |
|
294 t42_parse_encoding( T42_Face face, |
|
295 T42_Loader loader ) |
|
296 { |
|
297 T42_Parser parser = &loader->parser; |
|
298 FT_Byte* cur; |
|
299 FT_Byte* limit = parser->root.limit; |
|
300 |
|
301 PSAux_Service psaux = (PSAux_Service)face->psaux; |
|
302 |
|
303 |
|
304 T1_Skip_Spaces( parser ); |
|
305 cur = parser->root.cursor; |
|
306 if ( cur >= limit ) |
|
307 { |
|
308 FT_ERROR(( "t42_parse_encoding: out of bounds\n" )); |
|
309 parser->root.error = T42_Err_Invalid_File_Format; |
|
310 return; |
|
311 } |
|
312 |
|
313 /* if we have a number or `[', the encoding is an array, */ |
|
314 /* and we must load it now */ |
|
315 if ( ft_isdigit( *cur ) || *cur == '[' ) |
|
316 { |
|
317 T1_Encoding encode = &face->type1.encoding; |
|
318 FT_UInt count, n; |
|
319 PS_Table char_table = &loader->encoding_table; |
|
320 FT_Memory memory = parser->root.memory; |
|
321 FT_Error error; |
|
322 FT_Bool only_immediates = 0; |
|
323 |
|
324 |
|
325 /* read the number of entries in the encoding; should be 256 */ |
|
326 if ( *cur == '[' ) |
|
327 { |
|
328 count = 256; |
|
329 only_immediates = 1; |
|
330 parser->root.cursor++; |
|
331 } |
|
332 else |
|
333 count = (FT_UInt)T1_ToInt( parser ); |
|
334 |
|
335 T1_Skip_Spaces( parser ); |
|
336 if ( parser->root.cursor >= limit ) |
|
337 return; |
|
338 |
|
339 /* we use a T1_Table to store our charnames */ |
|
340 loader->num_chars = encode->num_chars = count; |
|
341 if ( FT_NEW_ARRAY( encode->char_index, count ) || |
|
342 FT_NEW_ARRAY( encode->char_name, count ) || |
|
343 FT_SET_ERROR( psaux->ps_table_funcs->init( |
|
344 char_table, count, memory ) ) ) |
|
345 { |
|
346 parser->root.error = error; |
|
347 return; |
|
348 } |
|
349 |
|
350 /* We need to `zero' out encoding_table.elements */ |
|
351 for ( n = 0; n < count; n++ ) |
|
352 { |
|
353 char* notdef = (char *)".notdef"; |
|
354 |
|
355 |
|
356 T1_Add_Table( char_table, n, notdef, 8 ); |
|
357 } |
|
358 |
|
359 /* Now we need to read records of the form */ |
|
360 /* */ |
|
361 /* ... charcode /charname ... */ |
|
362 /* */ |
|
363 /* for each entry in our table. */ |
|
364 /* */ |
|
365 /* We simply look for a number followed by an immediate */ |
|
366 /* name. Note that this ignores correctly the sequence */ |
|
367 /* that is often seen in type42 fonts: */ |
|
368 /* */ |
|
369 /* 0 1 255 { 1 index exch /.notdef put } for dup */ |
|
370 /* */ |
|
371 /* used to clean the encoding array before anything else. */ |
|
372 /* */ |
|
373 /* Alternatively, if the array is directly given as */ |
|
374 /* */ |
|
375 /* /Encoding [ ... ] */ |
|
376 /* */ |
|
377 /* we only read immediates. */ |
|
378 |
|
379 n = 0; |
|
380 T1_Skip_Spaces( parser ); |
|
381 |
|
382 while ( parser->root.cursor < limit ) |
|
383 { |
|
384 cur = parser->root.cursor; |
|
385 |
|
386 /* we stop when we encounter `def' or `]' */ |
|
387 if ( *cur == 'd' && cur + 3 < limit ) |
|
388 { |
|
389 if ( cur[1] == 'e' && |
|
390 cur[2] == 'f' && |
|
391 t42_is_space( cur[3] ) ) |
|
392 { |
|
393 FT_TRACE6(( "encoding end\n" )); |
|
394 cur += 3; |
|
395 break; |
|
396 } |
|
397 } |
|
398 if ( *cur == ']' ) |
|
399 { |
|
400 FT_TRACE6(( "encoding end\n" )); |
|
401 cur++; |
|
402 break; |
|
403 } |
|
404 |
|
405 /* check whether we have found an entry */ |
|
406 if ( ft_isdigit( *cur ) || only_immediates ) |
|
407 { |
|
408 FT_Int charcode; |
|
409 |
|
410 |
|
411 if ( only_immediates ) |
|
412 charcode = n; |
|
413 else |
|
414 { |
|
415 charcode = (FT_Int)T1_ToInt( parser ); |
|
416 T1_Skip_Spaces( parser ); |
|
417 } |
|
418 |
|
419 cur = parser->root.cursor; |
|
420 |
|
421 if ( *cur == '/' && cur + 2 < limit && n < count ) |
|
422 { |
|
423 FT_PtrDist len; |
|
424 |
|
425 |
|
426 cur++; |
|
427 |
|
428 parser->root.cursor = cur; |
|
429 T1_Skip_PS_Token( parser ); |
|
430 if ( parser->root.error ) |
|
431 return; |
|
432 |
|
433 len = parser->root.cursor - cur; |
|
434 |
|
435 parser->root.error = T1_Add_Table( char_table, charcode, |
|
436 cur, len + 1 ); |
|
437 if ( parser->root.error ) |
|
438 return; |
|
439 char_table->elements[charcode][len] = '\0'; |
|
440 |
|
441 n++; |
|
442 } |
|
443 } |
|
444 else |
|
445 { |
|
446 T1_Skip_PS_Token( parser ); |
|
447 if ( parser->root.error ) |
|
448 return; |
|
449 } |
|
450 |
|
451 T1_Skip_Spaces( parser ); |
|
452 } |
|
453 |
|
454 face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY; |
|
455 parser->root.cursor = cur; |
|
456 } |
|
457 |
|
458 /* Otherwise, we should have either `StandardEncoding', */ |
|
459 /* `ExpertEncoding', or `ISOLatin1Encoding' */ |
|
460 else |
|
461 { |
|
462 if ( cur + 17 < limit && |
|
463 ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 ) |
|
464 face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD; |
|
465 |
|
466 else if ( cur + 15 < limit && |
|
467 ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 ) |
|
468 face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT; |
|
469 |
|
470 else if ( cur + 18 < limit && |
|
471 ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 ) |
|
472 face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1; |
|
473 |
|
474 else |
|
475 { |
|
476 FT_ERROR(( "t42_parse_encoding: invalid token\n" )); |
|
477 parser->root.error = T42_Err_Invalid_File_Format; |
|
478 } |
|
479 } |
|
480 } |
|
481 |
|
482 |
|
483 typedef enum T42_Load_Status_ |
|
484 { |
|
485 BEFORE_START, |
|
486 BEFORE_TABLE_DIR, |
|
487 OTHER_TABLES |
|
488 |
|
489 } T42_Load_Status; |
|
490 |
|
491 |
|
492 static void |
|
493 t42_parse_sfnts( T42_Face face, |
|
494 T42_Loader loader ) |
|
495 { |
|
496 T42_Parser parser = &loader->parser; |
|
497 FT_Memory memory = parser->root.memory; |
|
498 FT_Byte* cur; |
|
499 FT_Byte* limit = parser->root.limit; |
|
500 FT_Error error; |
|
501 FT_Int num_tables = 0; |
|
502 FT_ULong count, ttf_size = 0; |
|
503 |
|
504 FT_Long n, string_size, old_string_size, real_size; |
|
505 FT_Byte* string_buf = NULL; |
|
506 FT_Bool allocated = 0; |
|
507 |
|
508 T42_Load_Status status; |
|
509 |
|
510 |
|
511 /* The format is */ |
|
512 /* */ |
|
513 /* /sfnts [ <hexstring> <hexstring> ... ] def */ |
|
514 /* */ |
|
515 /* or */ |
|
516 /* */ |
|
517 /* /sfnts [ */ |
|
518 /* <num_bin_bytes> RD <binary data> */ |
|
519 /* <num_bin_bytes> RD <binary data> */ |
|
520 /* ... */ |
|
521 /* ] def */ |
|
522 /* */ |
|
523 /* with exactly one space after the `RD' token. */ |
|
524 |
|
525 T1_Skip_Spaces( parser ); |
|
526 |
|
527 if ( parser->root.cursor >= limit || *parser->root.cursor++ != '[' ) |
|
528 { |
|
529 FT_ERROR(( "t42_parse_sfnts: can't find begin of sfnts vector\n" )); |
|
530 error = T42_Err_Invalid_File_Format; |
|
531 goto Fail; |
|
532 } |
|
533 |
|
534 T1_Skip_Spaces( parser ); |
|
535 status = BEFORE_START; |
|
536 string_size = 0; |
|
537 old_string_size = 0; |
|
538 count = 0; |
|
539 |
|
540 while ( parser->root.cursor < limit ) |
|
541 { |
|
542 cur = parser->root.cursor; |
|
543 |
|
544 if ( *cur == ']' ) |
|
545 { |
|
546 parser->root.cursor++; |
|
547 goto Exit; |
|
548 } |
|
549 |
|
550 else if ( *cur == '<' ) |
|
551 { |
|
552 T1_Skip_PS_Token( parser ); |
|
553 if ( parser->root.error ) |
|
554 goto Exit; |
|
555 |
|
556 /* don't include delimiters */ |
|
557 string_size = (FT_Long)( ( parser->root.cursor - cur - 2 + 1 ) / 2 ); |
|
558 if ( FT_REALLOC( string_buf, old_string_size, string_size ) ) |
|
559 goto Fail; |
|
560 |
|
561 allocated = 1; |
|
562 |
|
563 parser->root.cursor = cur; |
|
564 (void)T1_ToBytes( parser, string_buf, string_size, &real_size, 1 ); |
|
565 old_string_size = string_size; |
|
566 string_size = real_size; |
|
567 } |
|
568 |
|
569 else if ( ft_isdigit( *cur ) ) |
|
570 { |
|
571 if ( allocated ) |
|
572 { |
|
573 FT_ERROR(( "t42_parse_sfnts: " |
|
574 "can't handle mixed binary and hex strings\n" )); |
|
575 error = T42_Err_Invalid_File_Format; |
|
576 goto Fail; |
|
577 } |
|
578 |
|
579 string_size = T1_ToInt( parser ); |
|
580 if ( string_size < 0 ) |
|
581 { |
|
582 FT_ERROR(( "t42_parse_sfnts: invalid string size\n" )); |
|
583 error = T42_Err_Invalid_File_Format; |
|
584 goto Fail; |
|
585 } |
|
586 |
|
587 T1_Skip_PS_Token( parser ); /* `RD' */ |
|
588 if ( parser->root.error ) |
|
589 return; |
|
590 |
|
591 string_buf = parser->root.cursor + 1; /* one space after `RD' */ |
|
592 |
|
593 if ( limit - parser->root.cursor < string_size ) |
|
594 { |
|
595 FT_ERROR(( "t42_parse_sfnts: too many binary data\n" )); |
|
596 error = T42_Err_Invalid_File_Format; |
|
597 goto Fail; |
|
598 } |
|
599 else |
|
600 parser->root.cursor += string_size + 1; |
|
601 } |
|
602 |
|
603 if ( !string_buf ) |
|
604 { |
|
605 FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); |
|
606 error = T42_Err_Invalid_File_Format; |
|
607 goto Fail; |
|
608 } |
|
609 |
|
610 /* A string can have a trailing zero byte for padding. Ignore it. */ |
|
611 if ( string_buf[string_size - 1] == 0 && ( string_size % 2 == 1 ) ) |
|
612 string_size--; |
|
613 |
|
614 if ( !string_size ) |
|
615 { |
|
616 FT_ERROR(( "t42_parse_sfnts: invalid string\n" )); |
|
617 error = T42_Err_Invalid_File_Format; |
|
618 goto Fail; |
|
619 } |
|
620 |
|
621 for ( n = 0; n < string_size; n++ ) |
|
622 { |
|
623 switch ( status ) |
|
624 { |
|
625 case BEFORE_START: |
|
626 /* load offset table, 12 bytes */ |
|
627 if ( count < 12 ) |
|
628 { |
|
629 face->ttf_data[count++] = string_buf[n]; |
|
630 continue; |
|
631 } |
|
632 else |
|
633 { |
|
634 num_tables = 16 * face->ttf_data[4] + face->ttf_data[5]; |
|
635 status = BEFORE_TABLE_DIR; |
|
636 ttf_size = 12 + 16 * num_tables; |
|
637 |
|
638 if ( FT_REALLOC( face->ttf_data, 12, ttf_size ) ) |
|
639 goto Fail; |
|
640 } |
|
641 /* fall through */ |
|
642 |
|
643 case BEFORE_TABLE_DIR: |
|
644 /* the offset table is read; read the table directory */ |
|
645 if ( count < ttf_size ) |
|
646 { |
|
647 face->ttf_data[count++] = string_buf[n]; |
|
648 continue; |
|
649 } |
|
650 else |
|
651 { |
|
652 int i; |
|
653 FT_ULong len; |
|
654 |
|
655 |
|
656 for ( i = 0; i < num_tables; i++ ) |
|
657 { |
|
658 FT_Byte* p = face->ttf_data + 12 + 16 * i + 12; |
|
659 |
|
660 |
|
661 len = FT_PEEK_ULONG( p ); |
|
662 |
|
663 /* Pad to a 4-byte boundary length */ |
|
664 ttf_size += ( len + 3 ) & ~3; |
|
665 } |
|
666 |
|
667 status = OTHER_TABLES; |
|
668 face->ttf_size = ttf_size; |
|
669 |
|
670 /* there are no more than 256 tables, so no size check here */ |
|
671 if ( FT_REALLOC( face->ttf_data, 12 + 16 * num_tables, |
|
672 ttf_size + 1 ) ) |
|
673 goto Fail; |
|
674 } |
|
675 /* fall through */ |
|
676 |
|
677 case OTHER_TABLES: |
|
678 /* all other tables are just copied */ |
|
679 if ( count >= ttf_size ) |
|
680 { |
|
681 FT_ERROR(( "t42_parse_sfnts: too many binary data\n" )); |
|
682 error = T42_Err_Invalid_File_Format; |
|
683 goto Fail; |
|
684 } |
|
685 face->ttf_data[count++] = string_buf[n]; |
|
686 } |
|
687 } |
|
688 |
|
689 T1_Skip_Spaces( parser ); |
|
690 } |
|
691 |
|
692 /* if control reaches this point, the format was not valid */ |
|
693 error = T42_Err_Invalid_File_Format; |
|
694 |
|
695 Fail: |
|
696 parser->root.error = error; |
|
697 |
|
698 Exit: |
|
699 if ( allocated ) |
|
700 FT_FREE( string_buf ); |
|
701 } |
|
702 |
|
703 |
|
704 static void |
|
705 t42_parse_charstrings( T42_Face face, |
|
706 T42_Loader loader ) |
|
707 { |
|
708 T42_Parser parser = &loader->parser; |
|
709 PS_Table code_table = &loader->charstrings; |
|
710 PS_Table name_table = &loader->glyph_names; |
|
711 PS_Table swap_table = &loader->swap_table; |
|
712 FT_Memory memory = parser->root.memory; |
|
713 FT_Error error; |
|
714 |
|
715 PSAux_Service psaux = (PSAux_Service)face->psaux; |
|
716 |
|
717 FT_Byte* cur; |
|
718 FT_Byte* limit = parser->root.limit; |
|
719 FT_UInt n; |
|
720 FT_UInt notdef_index = 0; |
|
721 FT_Byte notdef_found = 0; |
|
722 |
|
723 |
|
724 T1_Skip_Spaces( parser ); |
|
725 |
|
726 if ( parser->root.cursor >= limit ) |
|
727 { |
|
728 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
|
729 error = T42_Err_Invalid_File_Format; |
|
730 goto Fail; |
|
731 } |
|
732 |
|
733 if ( ft_isdigit( *parser->root.cursor ) ) |
|
734 { |
|
735 loader->num_glyphs = (FT_UInt)T1_ToInt( parser ); |
|
736 if ( parser->root.error ) |
|
737 return; |
|
738 } |
|
739 else if ( *parser->root.cursor == '<' ) |
|
740 { |
|
741 /* We have `<< ... >>'. Count the number of `/' in the dictionary */ |
|
742 /* to get its size. */ |
|
743 FT_UInt count = 0; |
|
744 |
|
745 |
|
746 T1_Skip_PS_Token( parser ); |
|
747 if ( parser->root.error ) |
|
748 return; |
|
749 T1_Skip_Spaces( parser ); |
|
750 cur = parser->root.cursor; |
|
751 |
|
752 while ( parser->root.cursor < limit ) |
|
753 { |
|
754 if ( *parser->root.cursor == '/' ) |
|
755 count++; |
|
756 else if ( *parser->root.cursor == '>' ) |
|
757 { |
|
758 loader->num_glyphs = count; |
|
759 parser->root.cursor = cur; /* rewind */ |
|
760 break; |
|
761 } |
|
762 T1_Skip_PS_Token( parser ); |
|
763 if ( parser->root.error ) |
|
764 return; |
|
765 T1_Skip_Spaces( parser ); |
|
766 } |
|
767 } |
|
768 else |
|
769 { |
|
770 FT_ERROR(( "t42_parse_charstrings: invalid token\n" )); |
|
771 error = T42_Err_Invalid_File_Format; |
|
772 goto Fail; |
|
773 } |
|
774 |
|
775 if ( parser->root.cursor >= limit ) |
|
776 { |
|
777 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
|
778 error = T42_Err_Invalid_File_Format; |
|
779 goto Fail; |
|
780 } |
|
781 |
|
782 /* initialize tables */ |
|
783 |
|
784 error = psaux->ps_table_funcs->init( code_table, |
|
785 loader->num_glyphs, |
|
786 memory ); |
|
787 if ( error ) |
|
788 goto Fail; |
|
789 |
|
790 error = psaux->ps_table_funcs->init( name_table, |
|
791 loader->num_glyphs, |
|
792 memory ); |
|
793 if ( error ) |
|
794 goto Fail; |
|
795 |
|
796 /* Initialize table for swapping index notdef_index and */ |
|
797 /* index 0 names and codes (if necessary). */ |
|
798 |
|
799 error = psaux->ps_table_funcs->init( swap_table, 4, memory ); |
|
800 if ( error ) |
|
801 goto Fail; |
|
802 |
|
803 n = 0; |
|
804 |
|
805 for (;;) |
|
806 { |
|
807 /* The format is simple: */ |
|
808 /* `/glyphname' + index [+ def] */ |
|
809 |
|
810 T1_Skip_Spaces( parser ); |
|
811 |
|
812 cur = parser->root.cursor; |
|
813 if ( cur >= limit ) |
|
814 break; |
|
815 |
|
816 /* We stop when we find an `end' keyword or '>' */ |
|
817 if ( *cur == 'e' && |
|
818 cur + 3 < limit && |
|
819 cur[1] == 'n' && |
|
820 cur[2] == 'd' && |
|
821 t42_is_space( cur[3] ) ) |
|
822 break; |
|
823 if ( *cur == '>' ) |
|
824 break; |
|
825 |
|
826 T1_Skip_PS_Token( parser ); |
|
827 if ( parser->root.error ) |
|
828 return; |
|
829 |
|
830 if ( *cur == '/' ) |
|
831 { |
|
832 FT_PtrDist len; |
|
833 |
|
834 |
|
835 if ( cur + 1 >= limit ) |
|
836 { |
|
837 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
|
838 error = T42_Err_Invalid_File_Format; |
|
839 goto Fail; |
|
840 } |
|
841 |
|
842 cur++; /* skip `/' */ |
|
843 len = parser->root.cursor - cur; |
|
844 |
|
845 error = T1_Add_Table( name_table, n, cur, len + 1 ); |
|
846 if ( error ) |
|
847 goto Fail; |
|
848 |
|
849 /* add a trailing zero to the name table */ |
|
850 name_table->elements[n][len] = '\0'; |
|
851 |
|
852 /* record index of /.notdef */ |
|
853 if ( *cur == '.' && |
|
854 ft_strcmp( ".notdef", |
|
855 (const char*)(name_table->elements[n]) ) == 0 ) |
|
856 { |
|
857 notdef_index = n; |
|
858 notdef_found = 1; |
|
859 } |
|
860 |
|
861 T1_Skip_Spaces( parser ); |
|
862 |
|
863 cur = parser->root.cursor; |
|
864 |
|
865 (void)T1_ToInt( parser ); |
|
866 if ( parser->root.cursor >= limit ) |
|
867 { |
|
868 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); |
|
869 error = T42_Err_Invalid_File_Format; |
|
870 goto Fail; |
|
871 } |
|
872 |
|
873 len = parser->root.cursor - cur; |
|
874 |
|
875 error = T1_Add_Table( code_table, n, cur, len + 1 ); |
|
876 if ( error ) |
|
877 goto Fail; |
|
878 |
|
879 code_table->elements[n][len] = '\0'; |
|
880 |
|
881 n++; |
|
882 if ( n >= loader->num_glyphs ) |
|
883 break; |
|
884 } |
|
885 } |
|
886 |
|
887 loader->num_glyphs = n; |
|
888 |
|
889 if ( !notdef_found ) |
|
890 { |
|
891 FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph\n" )); |
|
892 error = T42_Err_Invalid_File_Format; |
|
893 goto Fail; |
|
894 } |
|
895 |
|
896 /* if /.notdef does not occupy index 0, do our magic. */ |
|
897 if ( ft_strcmp( (const char*)".notdef", |
|
898 (const char*)name_table->elements[0] ) ) |
|
899 { |
|
900 /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */ |
|
901 /* name and code entries to swap_table. Then place notdef_index */ |
|
902 /* name and code entries into swap_table. Then swap name and code */ |
|
903 /* entries at indices notdef_index and 0 using values stored in */ |
|
904 /* swap_table. */ |
|
905 |
|
906 /* Index 0 name */ |
|
907 error = T1_Add_Table( swap_table, 0, |
|
908 name_table->elements[0], |
|
909 name_table->lengths [0] ); |
|
910 if ( error ) |
|
911 goto Fail; |
|
912 |
|
913 /* Index 0 code */ |
|
914 error = T1_Add_Table( swap_table, 1, |
|
915 code_table->elements[0], |
|
916 code_table->lengths [0] ); |
|
917 if ( error ) |
|
918 goto Fail; |
|
919 |
|
920 /* Index notdef_index name */ |
|
921 error = T1_Add_Table( swap_table, 2, |
|
922 name_table->elements[notdef_index], |
|
923 name_table->lengths [notdef_index] ); |
|
924 if ( error ) |
|
925 goto Fail; |
|
926 |
|
927 /* Index notdef_index code */ |
|
928 error = T1_Add_Table( swap_table, 3, |
|
929 code_table->elements[notdef_index], |
|
930 code_table->lengths [notdef_index] ); |
|
931 if ( error ) |
|
932 goto Fail; |
|
933 |
|
934 error = T1_Add_Table( name_table, notdef_index, |
|
935 swap_table->elements[0], |
|
936 swap_table->lengths [0] ); |
|
937 if ( error ) |
|
938 goto Fail; |
|
939 |
|
940 error = T1_Add_Table( code_table, notdef_index, |
|
941 swap_table->elements[1], |
|
942 swap_table->lengths [1] ); |
|
943 if ( error ) |
|
944 goto Fail; |
|
945 |
|
946 error = T1_Add_Table( name_table, 0, |
|
947 swap_table->elements[2], |
|
948 swap_table->lengths [2] ); |
|
949 if ( error ) |
|
950 goto Fail; |
|
951 |
|
952 error = T1_Add_Table( code_table, 0, |
|
953 swap_table->elements[3], |
|
954 swap_table->lengths [3] ); |
|
955 if ( error ) |
|
956 goto Fail; |
|
957 |
|
958 } |
|
959 |
|
960 return; |
|
961 |
|
962 Fail: |
|
963 parser->root.error = error; |
|
964 } |
|
965 |
|
966 |
|
967 static FT_Error |
|
968 t42_load_keyword( T42_Face face, |
|
969 T42_Loader loader, |
|
970 T1_Field field ) |
|
971 { |
|
972 FT_Error error; |
|
973 void* dummy_object; |
|
974 void** objects; |
|
975 FT_UInt max_objects = 0; |
|
976 |
|
977 |
|
978 /* if the keyword has a dedicated callback, call it */ |
|
979 if ( field->type == T1_FIELD_TYPE_CALLBACK ) |
|
980 { |
|
981 field->reader( (FT_Face)face, loader ); |
|
982 error = loader->parser.root.error; |
|
983 goto Exit; |
|
984 } |
|
985 |
|
986 /* now the keyword is either a simple field or a table of fields; */ |
|
987 /* we are now going to take care of it */ |
|
988 |
|
989 switch ( field->location ) |
|
990 { |
|
991 case T1_FIELD_LOCATION_FONT_INFO: |
|
992 dummy_object = &face->type1.font_info; |
|
993 break; |
|
994 |
|
995 case T1_FIELD_LOCATION_FONT_EXTRA: |
|
996 dummy_object = &face->type1.font_extra; |
|
997 break; |
|
998 |
|
999 case T1_FIELD_LOCATION_BBOX: |
|
1000 dummy_object = &face->type1.font_bbox; |
|
1001 break; |
|
1002 |
|
1003 default: |
|
1004 dummy_object = &face->type1; |
|
1005 } |
|
1006 |
|
1007 objects = &dummy_object; |
|
1008 |
|
1009 if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY || |
|
1010 field->type == T1_FIELD_TYPE_FIXED_ARRAY ) |
|
1011 error = T1_Load_Field_Table( &loader->parser, field, |
|
1012 objects, max_objects, 0 ); |
|
1013 else |
|
1014 error = T1_Load_Field( &loader->parser, field, |
|
1015 objects, max_objects, 0 ); |
|
1016 |
|
1017 Exit: |
|
1018 return error; |
|
1019 } |
|
1020 |
|
1021 |
|
1022 FT_LOCAL_DEF( FT_Error ) |
|
1023 t42_parse_dict( T42_Face face, |
|
1024 T42_Loader loader, |
|
1025 FT_Byte* base, |
|
1026 FT_Long size ) |
|
1027 { |
|
1028 T42_Parser parser = &loader->parser; |
|
1029 FT_Byte* limit; |
|
1030 FT_Int n_keywords = (FT_Int)( sizeof ( t42_keywords ) / |
|
1031 sizeof ( t42_keywords[0] ) ); |
|
1032 |
|
1033 |
|
1034 parser->root.cursor = base; |
|
1035 parser->root.limit = base + size; |
|
1036 parser->root.error = T42_Err_Ok; |
|
1037 |
|
1038 limit = parser->root.limit; |
|
1039 |
|
1040 T1_Skip_Spaces( parser ); |
|
1041 |
|
1042 while ( parser->root.cursor < limit ) |
|
1043 { |
|
1044 FT_Byte* cur; |
|
1045 |
|
1046 |
|
1047 cur = parser->root.cursor; |
|
1048 |
|
1049 /* look for `FontDirectory' which causes problems for some fonts */ |
|
1050 if ( *cur == 'F' && cur + 25 < limit && |
|
1051 ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 ) |
|
1052 { |
|
1053 FT_Byte* cur2; |
|
1054 |
|
1055 |
|
1056 /* skip the `FontDirectory' keyword */ |
|
1057 T1_Skip_PS_Token( parser ); |
|
1058 T1_Skip_Spaces ( parser ); |
|
1059 cur = cur2 = parser->root.cursor; |
|
1060 |
|
1061 /* look up the `known' keyword */ |
|
1062 while ( cur < limit ) |
|
1063 { |
|
1064 if ( *cur == 'k' && cur + 5 < limit && |
|
1065 ft_strncmp( (char*)cur, "known", 5 ) == 0 ) |
|
1066 break; |
|
1067 |
|
1068 T1_Skip_PS_Token( parser ); |
|
1069 if ( parser->root.error ) |
|
1070 goto Exit; |
|
1071 T1_Skip_Spaces ( parser ); |
|
1072 cur = parser->root.cursor; |
|
1073 } |
|
1074 |
|
1075 if ( cur < limit ) |
|
1076 { |
|
1077 T1_TokenRec token; |
|
1078 |
|
1079 |
|
1080 /* skip the `known' keyword and the token following it */ |
|
1081 T1_Skip_PS_Token( parser ); |
|
1082 T1_ToToken( parser, &token ); |
|
1083 |
|
1084 /* if the last token was an array, skip it! */ |
|
1085 if ( token.type == T1_TOKEN_TYPE_ARRAY ) |
|
1086 cur2 = parser->root.cursor; |
|
1087 } |
|
1088 parser->root.cursor = cur2; |
|
1089 } |
|
1090 |
|
1091 /* look for immediates */ |
|
1092 else if ( *cur == '/' && cur + 2 < limit ) |
|
1093 { |
|
1094 FT_PtrDist len; |
|
1095 |
|
1096 |
|
1097 cur++; |
|
1098 |
|
1099 parser->root.cursor = cur; |
|
1100 T1_Skip_PS_Token( parser ); |
|
1101 if ( parser->root.error ) |
|
1102 goto Exit; |
|
1103 |
|
1104 len = parser->root.cursor - cur; |
|
1105 |
|
1106 if ( len > 0 && len < 22 && parser->root.cursor < limit ) |
|
1107 { |
|
1108 int i; |
|
1109 |
|
1110 |
|
1111 /* now compare the immediate name to the keyword table */ |
|
1112 |
|
1113 /* loop through all known keywords */ |
|
1114 for ( i = 0; i < n_keywords; i++ ) |
|
1115 { |
|
1116 T1_Field keyword = (T1_Field)&t42_keywords[i]; |
|
1117 FT_Byte *name = (FT_Byte*)keyword->ident; |
|
1118 |
|
1119 |
|
1120 if ( !name ) |
|
1121 continue; |
|
1122 |
|
1123 if ( cur[0] == name[0] && |
|
1124 len == (FT_PtrDist)ft_strlen( (const char *)name ) && |
|
1125 ft_memcmp( cur, name, len ) == 0 ) |
|
1126 { |
|
1127 /* we found it -- run the parsing callback! */ |
|
1128 parser->root.error = t42_load_keyword( face, |
|
1129 loader, |
|
1130 keyword ); |
|
1131 if ( parser->root.error ) |
|
1132 return parser->root.error; |
|
1133 break; |
|
1134 } |
|
1135 } |
|
1136 } |
|
1137 } |
|
1138 else |
|
1139 { |
|
1140 T1_Skip_PS_Token( parser ); |
|
1141 if ( parser->root.error ) |
|
1142 goto Exit; |
|
1143 } |
|
1144 |
|
1145 T1_Skip_Spaces( parser ); |
|
1146 } |
|
1147 |
|
1148 Exit: |
|
1149 return parser->root.error; |
|
1150 } |
|
1151 |
|
1152 |
|
1153 FT_LOCAL_DEF( void ) |
|
1154 t42_loader_init( T42_Loader loader, |
|
1155 T42_Face face ) |
|
1156 { |
|
1157 FT_UNUSED( face ); |
|
1158 |
|
1159 FT_MEM_ZERO( loader, sizeof ( *loader ) ); |
|
1160 loader->num_glyphs = 0; |
|
1161 loader->num_chars = 0; |
|
1162 |
|
1163 /* initialize the tables -- simply set their `init' field to 0 */ |
|
1164 loader->encoding_table.init = 0; |
|
1165 loader->charstrings.init = 0; |
|
1166 loader->glyph_names.init = 0; |
|
1167 } |
|
1168 |
|
1169 |
|
1170 FT_LOCAL_DEF( void ) |
|
1171 t42_loader_done( T42_Loader loader ) |
|
1172 { |
|
1173 T42_Parser parser = &loader->parser; |
|
1174 |
|
1175 |
|
1176 /* finalize tables */ |
|
1177 T1_Release_Table( &loader->encoding_table ); |
|
1178 T1_Release_Table( &loader->charstrings ); |
|
1179 T1_Release_Table( &loader->glyph_names ); |
|
1180 T1_Release_Table( &loader->swap_table ); |
|
1181 |
|
1182 /* finalize parser */ |
|
1183 t42_parser_done( parser ); |
|
1184 } |
|
1185 |
|
1186 |
|
1187 /* END */ |
|