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