Upload tizen 2.0 beta source
[framework/graphics/freetype.git] / src / type42 / t42parse.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  t42parse.c                                                             */
4 /*                                                                         */
5 /*    Type 42 font parser (body).                                          */
6 /*                                                                         */
7 /*  Copyright 2002-2012 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 (odd) byte for padding. */
611       /* Ignore it.                                                */
612       if ( ( string_size & 1 ) && string_buf[string_size - 1] == 0 )
613         string_size--;
614
615       if ( !string_size )
616       {
617         FT_ERROR(( "t42_parse_sfnts: invalid string\n" ));
618         error = T42_Err_Invalid_File_Format;
619         goto Fail;
620       }
621
622       for ( n = 0; n < string_size; n++ )
623       {
624         switch ( status )
625         {
626         case BEFORE_START:
627           /* load offset table, 12 bytes */
628           if ( count < 12 )
629           {
630             face->ttf_data[count++] = string_buf[n];
631             continue;
632           }
633           else
634           {
635             num_tables = 16 * face->ttf_data[4] + face->ttf_data[5];
636             status     = BEFORE_TABLE_DIR;
637             ttf_size   = 12 + 16 * num_tables;
638
639             if ( FT_REALLOC( face->ttf_data, 12, ttf_size ) )
640               goto Fail;
641           }
642           /* fall through */
643
644         case BEFORE_TABLE_DIR:
645           /* the offset table is read; read the table directory */
646           if ( count < ttf_size )
647           {
648             face->ttf_data[count++] = string_buf[n];
649             continue;
650           }
651           else
652           {
653             int       i;
654             FT_ULong  len;
655
656
657             for ( i = 0; i < num_tables; i++ )
658             {
659               FT_Byte*  p = face->ttf_data + 12 + 16 * i + 12;
660
661
662               len = FT_PEEK_ULONG( p );
663
664               /* Pad to a 4-byte boundary length */
665               ttf_size += ( len + 3 ) & ~3;
666             }
667
668             status         = OTHER_TABLES;
669             face->ttf_size = ttf_size;
670
671             /* there are no more than 256 tables, so no size check here */
672             if ( FT_REALLOC( face->ttf_data, 12 + 16 * num_tables,
673                              ttf_size + 1 ) )
674               goto Fail;
675           }
676           /* fall through */
677
678         case OTHER_TABLES:
679           /* all other tables are just copied */
680           if ( count >= ttf_size )
681           {
682             FT_ERROR(( "t42_parse_sfnts: too many binary data\n" ));
683             error = T42_Err_Invalid_File_Format;
684             goto Fail;
685           }
686           face->ttf_data[count++] = string_buf[n];
687         }
688       }
689
690       T1_Skip_Spaces( parser );
691     }
692
693     /* if control reaches this point, the format was not valid */
694     error = T42_Err_Invalid_File_Format;
695
696   Fail:
697     parser->root.error = error;
698
699   Exit:
700     if ( allocated )
701       FT_FREE( string_buf );
702   }
703
704
705   static void
706   t42_parse_charstrings( T42_Face    face,
707                          T42_Loader  loader )
708   {
709     T42_Parser     parser       = &loader->parser;
710     PS_Table       code_table   = &loader->charstrings;
711     PS_Table       name_table   = &loader->glyph_names;
712     PS_Table       swap_table   = &loader->swap_table;
713     FT_Memory      memory       = parser->root.memory;
714     FT_Error       error;
715
716     PSAux_Service  psaux        = (PSAux_Service)face->psaux;
717
718     FT_Byte*       cur;
719     FT_Byte*       limit        = parser->root.limit;
720     FT_UInt        n;
721     FT_UInt        notdef_index = 0;
722     FT_Byte        notdef_found = 0;
723
724
725     T1_Skip_Spaces( parser );
726
727     if ( parser->root.cursor >= limit )
728     {
729       FT_ERROR(( "t42_parse_charstrings: out of bounds\n" ));
730       error = T42_Err_Invalid_File_Format;
731       goto Fail;
732     }
733
734     if ( ft_isdigit( *parser->root.cursor ) )
735     {
736       loader->num_glyphs = (FT_UInt)T1_ToInt( parser );
737       if ( parser->root.error )
738         return;
739     }
740     else if ( *parser->root.cursor == '<' )
741     {
742       /* We have `<< ... >>'.  Count the number of `/' in the dictionary */
743       /* to get its size.                                                */
744       FT_UInt  count = 0;
745
746
747       T1_Skip_PS_Token( parser );
748       if ( parser->root.error )
749         return;
750       T1_Skip_Spaces( parser );
751       cur = parser->root.cursor;
752
753       while ( parser->root.cursor < limit )
754       {
755         if ( *parser->root.cursor == '/' )
756           count++;
757         else if ( *parser->root.cursor == '>' )
758         {
759           loader->num_glyphs  = count;
760           parser->root.cursor = cur;        /* rewind */
761           break;
762         }
763         T1_Skip_PS_Token( parser );
764         if ( parser->root.error )
765           return;
766         T1_Skip_Spaces( parser );
767       }
768     }
769     else
770     {
771       FT_ERROR(( "t42_parse_charstrings: invalid token\n" ));
772       error = T42_Err_Invalid_File_Format;
773       goto Fail;
774     }
775
776     if ( parser->root.cursor >= limit )
777     {
778       FT_ERROR(( "t42_parse_charstrings: out of bounds\n" ));
779       error = T42_Err_Invalid_File_Format;
780       goto Fail;
781     }
782
783     /* initialize tables */
784
785     error = psaux->ps_table_funcs->init( code_table,
786                                          loader->num_glyphs,
787                                          memory );
788     if ( error )
789       goto Fail;
790
791     error = psaux->ps_table_funcs->init( name_table,
792                                          loader->num_glyphs,
793                                          memory );
794     if ( error )
795       goto Fail;
796
797     /* Initialize table for swapping index notdef_index and */
798     /* index 0 names and codes (if necessary).              */
799
800     error = psaux->ps_table_funcs->init( swap_table, 4, memory );
801     if ( error )
802       goto Fail;
803
804     n = 0;
805
806     for (;;)
807     {
808       /* The format is simple:                   */
809       /*   `/glyphname' + index [+ def]          */
810
811       T1_Skip_Spaces( parser );
812
813       cur = parser->root.cursor;
814       if ( cur >= limit )
815         break;
816
817       /* We stop when we find an `end' keyword or '>' */
818       if ( *cur   == 'e'          &&
819            cur + 3 < limit        &&
820            cur[1] == 'n'          &&
821            cur[2] == 'd'          &&
822            t42_is_space( cur[3] ) )
823         break;
824       if ( *cur == '>' )
825         break;
826
827       T1_Skip_PS_Token( parser );
828       if ( parser->root.error )
829         return;
830
831       if ( *cur == '/' )
832       {
833         FT_PtrDist  len;
834
835
836         if ( cur + 1 >= limit )
837         {
838           FT_ERROR(( "t42_parse_charstrings: out of bounds\n" ));
839           error = T42_Err_Invalid_File_Format;
840           goto Fail;
841         }
842
843         cur++;                              /* skip `/' */
844         len = parser->root.cursor - cur;
845
846         error = T1_Add_Table( name_table, n, cur, len + 1 );
847         if ( error )
848           goto Fail;
849
850         /* add a trailing zero to the name table */
851         name_table->elements[n][len] = '\0';
852
853         /* record index of /.notdef */
854         if ( *cur == '.'                                              &&
855              ft_strcmp( ".notdef",
856                         (const char*)(name_table->elements[n]) ) == 0 )
857         {
858           notdef_index = n;
859           notdef_found = 1;
860         }
861
862         T1_Skip_Spaces( parser );
863
864         cur = parser->root.cursor;
865
866         (void)T1_ToInt( parser );
867         if ( parser->root.cursor >= limit )
868         {
869           FT_ERROR(( "t42_parse_charstrings: out of bounds\n" ));
870           error = T42_Err_Invalid_File_Format;
871           goto Fail;
872         }
873
874         len = parser->root.cursor - cur;
875
876         error = T1_Add_Table( code_table, n, cur, len + 1 );
877         if ( error )
878           goto Fail;
879
880         code_table->elements[n][len] = '\0';
881
882         n++;
883         if ( n >= loader->num_glyphs )
884           break;
885       }
886     }
887
888     loader->num_glyphs = n;
889
890     if ( !notdef_found )
891     {
892       FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph\n" ));
893       error = T42_Err_Invalid_File_Format;
894       goto Fail;
895     }
896
897     /* if /.notdef does not occupy index 0, do our magic. */
898     if ( ft_strcmp( (const char*)".notdef",
899                     (const char*)name_table->elements[0] ) )
900     {
901       /* Swap glyph in index 0 with /.notdef glyph.  First, add index 0  */
902       /* name and code entries to swap_table.  Then place notdef_index   */
903       /* name and code entries into swap_table.  Then swap name and code */
904       /* entries at indices notdef_index and 0 using values stored in    */
905       /* swap_table.                                                     */
906
907       /* Index 0 name */
908       error = T1_Add_Table( swap_table, 0,
909                             name_table->elements[0],
910                             name_table->lengths [0] );
911       if ( error )
912         goto Fail;
913
914       /* Index 0 code */
915       error = T1_Add_Table( swap_table, 1,
916                             code_table->elements[0],
917                             code_table->lengths [0] );
918       if ( error )
919         goto Fail;
920
921       /* Index notdef_index name */
922       error = T1_Add_Table( swap_table, 2,
923                             name_table->elements[notdef_index],
924                             name_table->lengths [notdef_index] );
925       if ( error )
926         goto Fail;
927
928       /* Index notdef_index code */
929       error = T1_Add_Table( swap_table, 3,
930                             code_table->elements[notdef_index],
931                             code_table->lengths [notdef_index] );
932       if ( error )
933         goto Fail;
934
935       error = T1_Add_Table( name_table, notdef_index,
936                             swap_table->elements[0],
937                             swap_table->lengths [0] );
938       if ( error )
939         goto Fail;
940
941       error = T1_Add_Table( code_table, notdef_index,
942                             swap_table->elements[1],
943                             swap_table->lengths [1] );
944       if ( error )
945         goto Fail;
946
947       error = T1_Add_Table( name_table, 0,
948                             swap_table->elements[2],
949                             swap_table->lengths [2] );
950       if ( error )
951         goto Fail;
952
953       error = T1_Add_Table( code_table, 0,
954                             swap_table->elements[3],
955                             swap_table->lengths [3] );
956       if ( error )
957         goto Fail;
958
959     }
960
961     return;
962
963   Fail:
964     parser->root.error = error;
965   }
966
967
968   static FT_Error
969   t42_load_keyword( T42_Face    face,
970                     T42_Loader  loader,
971                     T1_Field    field )
972   {
973     FT_Error  error;
974     void*     dummy_object;
975     void**    objects;
976     FT_UInt   max_objects = 0;
977
978
979     /* if the keyword has a dedicated callback, call it */
980     if ( field->type == T1_FIELD_TYPE_CALLBACK )
981     {
982       field->reader( (FT_Face)face, loader );
983       error = loader->parser.root.error;
984       goto Exit;
985     }
986
987     /* now the keyword is either a simple field or a table of fields; */
988     /* we are now going to take care of it                            */
989
990     switch ( field->location )
991     {
992     case T1_FIELD_LOCATION_FONT_INFO:
993       dummy_object = &face->type1.font_info;
994       break;
995
996     case T1_FIELD_LOCATION_FONT_EXTRA:
997       dummy_object = &face->type1.font_extra;
998       break;
999
1000     case T1_FIELD_LOCATION_BBOX:
1001       dummy_object = &face->type1.font_bbox;
1002       break;
1003
1004     default:
1005       dummy_object = &face->type1;
1006     }
1007
1008     objects = &dummy_object;
1009
1010     if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY ||
1011          field->type == T1_FIELD_TYPE_FIXED_ARRAY   )
1012       error = T1_Load_Field_Table( &loader->parser, field,
1013                                    objects, max_objects, 0 );
1014     else
1015       error = T1_Load_Field( &loader->parser, field,
1016                              objects, max_objects, 0 );
1017
1018    Exit:
1019     return error;
1020   }
1021
1022
1023   FT_LOCAL_DEF( FT_Error )
1024   t42_parse_dict( T42_Face    face,
1025                   T42_Loader  loader,
1026                   FT_Byte*    base,
1027                   FT_Long     size )
1028   {
1029     T42_Parser  parser     = &loader->parser;
1030     FT_Byte*    limit;
1031     FT_Int      n_keywords = (FT_Int)( sizeof ( t42_keywords ) /
1032                                          sizeof ( t42_keywords[0] ) );
1033
1034
1035     parser->root.cursor = base;
1036     parser->root.limit  = base + size;
1037     parser->root.error  = T42_Err_Ok;
1038
1039     limit = parser->root.limit;
1040
1041     T1_Skip_Spaces( parser );
1042
1043     while ( parser->root.cursor < limit )
1044     {
1045       FT_Byte*  cur;
1046
1047
1048       cur = parser->root.cursor;
1049
1050       /* look for `FontDirectory' which causes problems for some fonts */
1051       if ( *cur == 'F' && cur + 25 < limit                    &&
1052            ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 )
1053       {
1054         FT_Byte*  cur2;
1055
1056
1057         /* skip the `FontDirectory' keyword */
1058         T1_Skip_PS_Token( parser );
1059         T1_Skip_Spaces  ( parser );
1060         cur = cur2 = parser->root.cursor;
1061
1062         /* look up the `known' keyword */
1063         while ( cur < limit )
1064         {
1065           if ( *cur == 'k' && cur + 5 < limit             &&
1066                 ft_strncmp( (char*)cur, "known", 5 ) == 0 )
1067             break;
1068
1069           T1_Skip_PS_Token( parser );
1070           if ( parser->root.error )
1071             goto Exit;
1072           T1_Skip_Spaces  ( parser );
1073           cur = parser->root.cursor;
1074         }
1075
1076         if ( cur < limit )
1077         {
1078           T1_TokenRec  token;
1079
1080
1081           /* skip the `known' keyword and the token following it */
1082           T1_Skip_PS_Token( parser );
1083           T1_ToToken( parser, &token );
1084
1085           /* if the last token was an array, skip it! */
1086           if ( token.type == T1_TOKEN_TYPE_ARRAY )
1087             cur2 = parser->root.cursor;
1088         }
1089         parser->root.cursor = cur2;
1090       }
1091
1092       /* look for immediates */
1093       else if ( *cur == '/' && cur + 2 < limit )
1094       {
1095         FT_PtrDist  len;
1096
1097
1098         cur++;
1099
1100         parser->root.cursor = cur;
1101         T1_Skip_PS_Token( parser );
1102         if ( parser->root.error )
1103           goto Exit;
1104
1105         len = parser->root.cursor - cur;
1106
1107         if ( len > 0 && len < 22 && parser->root.cursor < limit )
1108         {
1109           int  i;
1110
1111
1112           /* now compare the immediate name to the keyword table */
1113
1114           /* loop through all known keywords */
1115           for ( i = 0; i < n_keywords; i++ )
1116           {
1117             T1_Field  keyword = (T1_Field)&t42_keywords[i];
1118             FT_Byte   *name   = (FT_Byte*)keyword->ident;
1119
1120
1121             if ( !name )
1122               continue;
1123
1124             if ( cur[0] == name[0]                                  &&
1125                  len == (FT_PtrDist)ft_strlen( (const char *)name ) &&
1126                  ft_memcmp( cur, name, len ) == 0                   )
1127             {
1128               /* we found it -- run the parsing callback! */
1129               parser->root.error = t42_load_keyword( face,
1130                                                      loader,
1131                                                      keyword );
1132               if ( parser->root.error )
1133                 return parser->root.error;
1134               break;
1135             }
1136           }
1137         }
1138       }
1139       else
1140       {
1141         T1_Skip_PS_Token( parser );
1142         if ( parser->root.error )
1143           goto Exit;
1144       }
1145
1146       T1_Skip_Spaces( parser );
1147     }
1148
1149   Exit:
1150     return parser->root.error;
1151   }
1152
1153
1154   FT_LOCAL_DEF( void )
1155   t42_loader_init( T42_Loader  loader,
1156                    T42_Face    face )
1157   {
1158     FT_UNUSED( face );
1159
1160     FT_MEM_ZERO( loader, sizeof ( *loader ) );
1161     loader->num_glyphs = 0;
1162     loader->num_chars  = 0;
1163
1164     /* initialize the tables -- simply set their `init' field to 0 */
1165     loader->encoding_table.init = 0;
1166     loader->charstrings.init    = 0;
1167     loader->glyph_names.init    = 0;
1168   }
1169
1170
1171   FT_LOCAL_DEF( void )
1172   t42_loader_done( T42_Loader  loader )
1173   {
1174     T42_Parser  parser = &loader->parser;
1175
1176
1177     /* finalize tables */
1178     T1_Release_Table( &loader->encoding_table );
1179     T1_Release_Table( &loader->charstrings );
1180     T1_Release_Table( &loader->glyph_names );
1181     T1_Release_Table( &loader->swap_table );
1182
1183     /* finalize parser */
1184     t42_parser_done( parser );
1185   }
1186
1187
1188 /* END */