update for HEAD-2003021201
[reactos.git] / subsys / win32k / freetype / src / type1 / t1tokens.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  t1parse.c                                                              */
4 /*                                                                         */
5 /*    Type 1 parser (body).                                                */
6 /*                                                                         */
7 /*  Copyright 1996-2000 by                                                 */
8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
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   /*************************************************************************/
20   /*                                                                       */
21   /* The tokenizer is in charge of loading and reading a Type1 font file   */
22   /* (either in PFB or PFA format), and extracting successive tokens and   */
23   /* keywords from its two streams (i.e. the font program, and the private */
24   /* dictionary).                                                          */
25   /*                                                                       */
26   /* Eexec decryption is performed automatically when entering the private */
27   /* dictionary, or when retrieving char strings.                          */
28   /*                                                                       */
29   /*************************************************************************/
30
31
32 #include <freetype/internal/ftstream.h>
33 #include <freetype/internal/ftdebug.h>
34
35
36 #ifdef FT_FLAT_COMPILE
37
38 #include "t1tokens.h"
39 #include "t1load.h"
40
41 #else
42
43 #include <freetype/src/type1/t1tokens.h>
44 #include <freetype/src/type1/t1load.h>
45
46 #endif
47
48
49 #include <string.h>     /* for strncmp() */
50
51
52 #undef  READ_BUFFER_INCREMENT
53 #define READ_BUFFER_INCREMENT  0x400
54
55
56   /*************************************************************************/
57   /*                                                                       */
58   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
59   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
60   /* messages during execution.                                            */
61   /*                                                                       */
62 #undef  FT_COMPONENT
63 #define FT_COMPONENT  trace_t1load
64
65
66   /* An array of Type1 keywords supported by this engine.  This table */
67   /* places the keyword in lexicographical order.  It should always   */
68   /* correspond  to the enums `key_xxx'!                              */
69   /*                                                                  */
70   const char*  t1_keywords[key_max - key_first_] =
71   {
72     "-|", "ExpertEncoding", "ND", "NP", "RD", "StandardEncoding", "array",
73     "begin", "closefile", "currentdict", "currentfile", "def", "dict", "dup",
74     "eexec", "end", "executeonly", "false", "for", "index", "noaccess",
75     "put", "readonly", "true", "userdict", "|", "|-"
76   };
77
78
79   const char*  t1_immediates[imm_max - imm_first_] =
80   {
81     "-|", ".notdef", "BlendAxisTypes", "BlueFuzz", "BlueScale", "BlueShift",
82     "BlueValues", "CharStrings", "Encoding", "FamilyBlues", "FamilyName",
83     "FamilyOtherBlues", "FID", "FontBBox", "FontID", "FontInfo", "FontMatrix",
84     "FontName", "FontType", "ForceBold", "FullName", "ItalicAngle",
85     "LanguageGroup", "Metrics", "MinFeature", "ND", "NP", "Notice",
86     "OtherBlues", "OtherSubrs", "PaintType", "Private", "RD", "RndStemUp",
87     "StdHW", "StdVW", "StemSnapH", "StemSnapV", "StrokeWidth", "Subrs",
88     "UnderlinePosition", "UnderlineThickness", "UniqueID", "Weight",
89     "isFixedPitch", "lenIV", "password", "version", "|", "|-"
90   };
91
92
93   /* lexicographic comparison of two strings */
94   static
95   int  lexico_strcmp( const char*  str1,
96                       int          str1_len,
97                       const char*  str2 )
98   {
99     int  c2 = 0;
100
101
102     for ( ; str1_len > 0; str1_len-- )
103     {
104       int  c1, diff;
105
106
107       c1 = *str1++;
108       c2 = *str2++;
109
110       diff = c1 - c2;
111       if ( diff )
112         return diff;
113     };
114
115     return -*str2;
116   }
117
118
119   /* find a given token/name, performing binary search */
120   static
121   int  Find_Name( char*         base,
122                   int           length,
123                   const char**  table,
124                   int           table_len )
125   {
126     int  left, right;
127
128
129     left  = 0;
130     right = table_len - 1;
131
132     while ( right - left > 1 )
133     {
134       int  middle = left + ( ( right - left ) >> 1 );
135       int  cmp;
136
137
138       cmp = lexico_strcmp( base, length, table[middle] );
139       if ( !cmp )
140         return middle;
141
142       if ( cmp < 0 )
143         right = middle;
144       else
145         left  = middle;
146     }
147
148     if ( !lexico_strcmp( base, length, table[left ] ) )
149       return left;
150     if ( !lexico_strcmp( base, length, table[right] ) )
151       return right;
152
153     return -1;
154   }
155
156
157   /* read the small PFB section header */
158   static
159   FT_Error  Read_PFB_Tag( FT_Stream   stream,
160                           FT_UShort*  atag,
161                           FT_ULong*   asize )
162   {
163     FT_UShort  tag;
164     FT_ULong   size;
165     FT_Error   error;
166
167
168     FT_TRACE2(( "Read_PFB_Tag: reading\n" ));
169
170     if ( ACCESS_Frame( 6L ) )
171       return error;
172
173     tag  = GET_UShort();
174     size = GET_ULong();
175
176     FORGET_Frame();
177
178     *atag  = tag;
179     *asize = (   ( size         & 0xFF ) << 24 ) |
180              ( ( ( size >> 8  ) & 0xFF ) << 16 ) |
181              ( ( ( size >> 16 ) & 0xFF ) << 8 )  |
182              ( ( ( size >> 24 ) & 0xFF ) );
183
184     FT_TRACE2(( "  tag  = %04x\n", tag    ));
185     FT_TRACE4(( "  asze = %08x\n", size   ));
186     FT_TRACE2(( "  size = %08x\n", *asize ));
187
188     return T1_Err_Ok;
189   }
190
191
192   static
193   FT_Error  grow( T1_Tokenizer  tokzer )
194   {
195     FT_Error   error;
196     FT_Long    left_bytes;
197     FT_Memory  memory = tokzer->memory;
198
199
200     left_bytes = tokzer->max - tokzer->limit;
201
202     if ( left_bytes > 0 )
203     {
204       FT_Stream stream = tokzer->stream;
205
206
207       if ( left_bytes > READ_BUFFER_INCREMENT )
208         left_bytes = READ_BUFFER_INCREMENT;
209
210       FT_TRACE2(( "Growing tokenizer buffer by %d bytes\n", left_bytes ));
211
212       if ( !REALLOC( tokzer->base, tokzer->limit,
213                      tokzer->limit + left_bytes )                 &&
214            !FILE_Read( tokzer->base + tokzer->limit, left_bytes ) )
215         tokzer->limit += left_bytes;
216     }
217     else
218     {
219       FT_ERROR(( "Unexpected end of Type1 fragment!\n" ));
220       error = T1_Err_Invalid_File_Format;
221     }
222
223     tokzer->error = error;
224     return error;
225   }
226
227
228   /*************************************************************************/
229   /*                                                                       */
230   /* <Function>                                                            */
231   /*    t1_decrypt                                                         */
232   /*                                                                       */
233   /* <Description>                                                         */
234   /*    Performs the Type 1 charstring decryption process.                 */
235   /*                                                                       */
236   /* <Input>                                                               */
237   /*    buffer  :: The base address of the data to decrypt.                */
238   /*    length  :: The number of bytes to decrypt (beginning from the base */
239   /*               address.                                                */
240   /*    seed    :: The encryption seed (4330 for charstrings).             */
241   /*                                                                       */
242   LOCAL_FUNC
243   void  t1_decrypt( FT_Byte*   buffer,
244                     FT_Int     length,
245                     FT_UShort  seed )
246   {
247     while ( length > 0 )
248     {
249       FT_Byte  plain;
250
251
252       plain     = ( *buffer ^ ( seed >> 8 ) );
253       seed      = ( *buffer + seed ) * 52845 + 22719;
254       *buffer++ = plain;
255       length--;
256     }
257   }
258
259
260   /*************************************************************************/
261   /*                                                                       */
262   /* <Function>                                                            */
263   /*    New_Tokenizer                                                      */
264   /*                                                                       */
265   /* <Description>                                                         */
266   /*    Creates a new tokenizer from a given input stream.  This function  */
267   /*    automatically recognizes `pfa' or `pfb' files.  The function       */
268   /*    Read_Token() can then be used to extract successive tokens from    */
269   /*    the stream.                                                        */
270   /*                                                                       */
271   /* <Input>                                                               */
272   /*    stream    :: The input stream.                                     */
273   /*                                                                       */
274   /* <Output>                                                              */
275   /*    tokenizer :: A handle to a new tokenizer object.                   */
276   /*                                                                       */
277   /* <Return>                                                              */
278   /*    FreeType error code.  0 means success.                             */
279   /*                                                                       */
280   /* <Note>                                                                */
281   /*    This function copies the stream handle within the object.  Callers */
282   /*    should not discard `stream'.  This is done by the Done_Tokenizer() */
283   /*    function.                                                          */
284   /*                                                                       */
285   LOCAL_FUNC
286   FT_Error  New_Tokenizer( FT_Stream      stream,
287                            T1_Tokenizer*  tokenizer )
288   {
289     FT_Memory     memory = stream->memory;
290     T1_Tokenizer  tokzer;
291     FT_Error      error;
292     FT_UShort     tag;
293     FT_ULong      size;
294
295     FT_Byte*      tok_base;
296     FT_ULong      tok_limit;
297     FT_ULong      tok_max;
298
299
300     *tokenizer = 0;
301
302     /* allocate object */
303     if ( FILE_Seek( 0L )                     ||
304          ALLOC( tokzer, sizeof ( *tokzer ) ) )
305       return error;
306
307     tokzer->stream = stream;
308     tokzer->memory = stream->memory;
309
310     tokzer->in_pfb     = 0;
311     tokzer->in_private = 0;
312
313     tok_base  = 0;
314     tok_limit = 0;
315     tok_max   = stream->size;
316
317     error = Read_PFB_Tag( stream, &tag, &size );
318     if ( error )
319       goto Fail;
320
321     if ( tag != 0x8001 )
322     {
323       /* assume that it is a PFA file -- an error will be produced later */
324       /* if a character with value > 127 is encountered                  */
325
326       /* rewind to start of file */
327       if ( FILE_Seek( 0L ) )
328         goto Fail;
329
330       size = stream->size;
331     }
332     else
333       tokzer->in_pfb = 1;
334
335     /* if it is a memory-based resource, set up pointer */
336     if ( !stream->read )
337     {
338       tok_base  = (FT_Byte*)stream->base + stream->pos;
339       tok_limit = size;
340       tok_max   = size;
341
342       /* check that the `size' field is valid */
343       if ( FILE_Skip( size ) )
344         goto Fail;
345     }
346     else if ( tag == 0x8001 )
347     {
348       /* read segment in memory */
349       if ( ALLOC( tok_base, size ) )
350         goto Fail;
351
352       if ( FILE_Read( tok_base, size ) )
353       {
354         FREE( tok_base );
355         goto Fail;
356       }
357
358       tok_limit = size;
359       tok_max   = size;
360     }
361
362     tokzer->base   = tok_base;
363     tokzer->limit  = tok_limit;
364     tokzer->max    = tok_max;
365     tokzer->cursor = 0;
366
367     *tokenizer = tokzer;
368
369     /* now check font format; we must see `%!PS-AdobeFont-1' */
370     /* or `%!FontType'                                       */
371     {
372       if ( 16 > tokzer->limit )
373         grow( tokzer );
374
375       if ( tokzer->limit <= 16 ||
376            ( strncmp( (const char*)tokzer->base, "%!PS-AdobeFont-1", 16 )   &&
377              strncmp( (const char*)tokzer->base, "%!FontType", 10 )       ) )
378       {
379         FT_TRACE2(( "[not a Type1 font]\n" ));
380         error = FT_Err_Unknown_File_Format;
381         goto Fail;
382       }
383     }
384     return T1_Err_Ok;
385
386   Fail:
387     FREE( tokzer->base );
388     FREE( tokzer );
389     return error;
390   }
391
392
393   /* return the value of an hexadecimal digit */
394   static
395   int  hexa_value( char  c )
396   {
397    unsigned int  d;
398
399
400     d = (unsigned int)( c - '0' );
401     if ( d <= 9 )
402       return (int)d;
403
404     d = (unsigned int)( c - 'a' );
405     if ( d <= 5 )
406       return (int)( d + 10 );
407
408     d = (unsigned int)( c - 'A' );
409     if ( d <= 5 )
410       return (int)( d + 10 );
411
412     return -1;
413   }
414
415
416   /*************************************************************************/
417   /*                                                                       */
418   /* <Function>                                                            */
419   /*    Done_Tokenizer                                                     */
420   /*                                                                       */
421   /* <Description>                                                         */
422   /*    Closes a given tokenizer.  This function will also close the       */
423   /*    stream embedded in the object.                                     */
424   /*                                                                       */
425   /* <Input>                                                               */
426   /*    tokenizer :: The target tokenizer object.                          */
427   /*                                                                       */
428   /* <Return>                                                              */
429   /*    FreeType error code.  0 means success.                             */
430   /*                                                                       */
431   LOCAL_FUNC
432   FT_Error  Done_Tokenizer( T1_Tokenizer  tokenizer )
433   {
434     FT_Memory  memory = tokenizer->memory;
435
436
437     /* clear read buffer if needed (disk-based resources) */
438     if ( tokenizer->in_private || !tokenizer->stream->base )
439       FREE( tokenizer->base );
440
441     FREE( tokenizer );
442     return T1_Err_Ok;
443   }
444
445
446   /*************************************************************************/
447   /*                                                                       */
448   /* <Function>                                                            */
449   /*    Open_PrivateDict                                                   */
450   /*                                                                       */
451   /* <Description>                                                         */
452   /*    This function must be called to set the tokenizer to the private   */
453   /*    section of the Type1 file.  It recognizes automatically the        */
454   /*    the kind of eexec encryption used (ascii or binary).               */
455   /*                                                                       */
456   /* <Input>                                                               */
457   /*    tokenizer :: The target tokenizer object.                          */
458   /*    lenIV     :: The value of the `lenIV' variable.                    */
459   /*                                                                       */
460   /* <Return>                                                              */
461   /*    FreeType error code.  0 means success.                             */
462   /*                                                                       */
463   LOCAL_FUNC
464   FT_Error  Open_PrivateDict( T1_Tokenizer  tokenizer )
465   {
466     T1_Tokenizer  tokzer = tokenizer;
467     FT_Stream     stream = tokzer->stream;
468     FT_Memory     memory = tokzer->memory;
469     FT_Error      error = 0;
470
471     FT_UShort     tag;
472     FT_ULong      size;
473
474     FT_Byte*      private_dict;
475
476     /* are we already in the private dictionary ? */
477     if ( tokzer->in_private )
478       return 0;
479
480     if ( tokzer->in_pfb )
481     {
482       /* in the case of the PFB format, the private dictionary can be */
483       /* made of several segments.  We thus first read the number of  */
484       /* segments to compute the total size of the private dictionary */
485       /* then re-read them into memory.                               */
486       FT_Long   start_pos         = FILE_Pos();
487       FT_ULong  private_dict_size = 0;
488
489
490       for (;;)
491       {
492         error = Read_PFB_Tag( stream, &tag, &size );
493         if ( error || tag != 0x8002 )
494           break;
495
496         private_dict_size += size;
497
498         if ( FILE_Skip( size ) )
499           goto Fail;
500       }
501
502       /* check that we have a private dictionary there */
503       /* and allocate private dictionary buffer        */
504       if ( private_dict_size == 0 )
505       {
506         FT_ERROR(( "Open_PrivateDict:" ));
507         FT_ERROR(( " invalid private dictionary section\n" ));
508         error = T1_Err_Invalid_File_Format;
509         goto Fail;
510       }
511
512       if ( ALLOC( private_dict, private_dict_size ) )
513         goto Fail;
514
515       /* read all sections into buffer */
516       if ( FILE_Seek( start_pos ) )
517         goto Fail_Private;
518
519       private_dict_size = 0;
520       for (;;)
521       {
522         error = Read_PFB_Tag( stream, &tag, &size );
523         if ( error || tag != 0x8002 )
524         {
525           error = 0;
526           break;
527         }
528
529         if ( FILE_Read( private_dict + private_dict_size, size ) )
530           goto Fail_Private;
531
532         private_dict_size += size;
533       }
534
535       /* we must free the field `tokzer.base' if we are in a disk-based */
536       /* PFB file.                                                      */
537       if ( stream->read )
538         FREE( tokzer->base );
539
540       tokzer->base   = private_dict;
541       tokzer->cursor = 0;
542       tokzer->limit  = private_dict_size;
543       tokzer->max    = private_dict_size;
544     }
545     else
546     {
547       char*  base;
548
549
550       /* we are in a PFA file; read each token until we find `eexec' */
551       while ( tokzer->token.kind2 != key_eexec )
552       {
553         error = Read_Token( tokzer );
554         if ( error )
555           goto Fail;
556       }
557
558       /* now determine whether the private dictionary is encoded in binary */
559       /* or hexadecimal ASCII format.                                      */
560
561       /* we need to access the next 4 bytes (after the final \r following  */
562       /* the `eexec' keyword); if they all are hexadecimal digits, then    */
563       /* we have a case of ASCII storage.                                  */
564       while ( tokzer->cursor + 5 > tokzer->limit )
565       {
566         error = grow( tokzer );
567         if ( error )
568           goto Fail;
569       }
570
571       /* skip whitespace/line feed after `eexec' */
572       base = (char*)tokzer->base + tokzer->cursor + 1;
573       if ( ( hexa_value( base[0] ) | hexa_value( base[1] ) |
574              hexa_value( base[2] ) | hexa_value( base[3] ) ) < 0 )
575       {
576         /* binary encoding -- `simply' read the stream */
577
578         /* if it is a memory-based resource, we need to allocate a new */
579         /* storage buffer for the private dictionary, as it must be    */
580         /* decrypted later                                             */
581         if ( stream->base )
582         {
583           size = stream->size - tokzer->cursor - 1; /* remaining bytes */
584
585           if ( ALLOC( private_dict, size ) )  /* alloc private dict buffer */
586             goto Fail;
587
588           /* copy eexec-encrypted bytes */
589           MEM_Copy( private_dict, tokzer->base + tokzer->cursor + 1, size );
590
591           /* reset pointers - forget about file mapping */
592           tokzer->base   = private_dict;
593           tokzer->limit  = size;
594           tokzer->max    = size;
595           tokzer->cursor = 0;
596         }
597         /* On the opposite, for disk based resources, we simply grow  */
598         /* the current buffer until its completion, and decrypt the   */
599         /* bytes within it.  In all cases, the `base' buffer will be  */
600         /* discarded on DoneTokenizer if we are in the private dict.  */
601         else
602         {
603           /* grow the read buffer to the full file */
604           while ( tokzer->limit < tokzer->max )
605           {
606             error = grow( tokenizer );
607             if ( error )
608               goto Fail;
609           }
610
611           /* set up cursor to first encrypted byte */
612           tokzer->cursor++;
613         }
614       }
615       else
616       {
617         /* ASCII hexadecimal encoding.  This sucks... */
618         FT_Byte*  write;
619         FT_Byte*  cur;
620         FT_Byte*  limit;
621         FT_Int    count;
622
623
624         /* allocate a buffer, read each one byte at a time */
625         count = stream->size - tokzer->cursor;
626         size  = count / 2;
627
628         if ( ALLOC( private_dict, size ) )   /* alloc private dict buffer */
629           goto Fail;
630
631         write = private_dict;
632         cur   = tokzer->base + tokzer->cursor;
633         limit = tokzer->base + tokzer->limit;
634
635         /* read each bytes */
636         while ( count > 0 )
637         {
638           /* ensure that we can read the next 2 bytes! */
639           while ( cur + 2 > limit )
640           {
641             int  cursor = cur - tokzer->base;
642
643
644             error = grow( tokzer );
645             if ( error )
646               goto Fail_Private;
647             cur   = tokzer->base + cursor;
648             limit = tokzer->base + tokzer->limit;
649           }
650
651           /* check for new line */
652           if ( cur[0] == '\r' || cur[0] == '\n' )
653           {
654             cur++;
655             count--;
656           }
657           else
658           {
659             int  hex1 = hexa_value(cur[0]);
660
661
662             /* exit if we have a non-hexadecimal digit which isn't */
663             /* a new-line character                                */
664             if ( hex1 < 0 )
665               break;
666
667             /* otherwise, store byte */
668             *write++ = ( hex1 << 4 ) | hexa_value( cur[1] );
669             cur   += 2;
670             count -= 2;
671           }
672         }
673
674         /* get rid of old buffer in the case of disk-based resources */
675         if ( !stream->base )
676           FREE( tokzer->base );
677
678         /* set up pointers */
679         tokzer->base   = private_dict;
680         tokzer->limit  = size;
681         tokzer->max    = size;
682         tokzer->cursor = 0;
683       }
684     }
685
686     /* finally, decrypt the private dictionary - and skip the lenIV bytes */
687     t1_decrypt( tokzer->base, tokzer->limit, 55665 );
688     tokzer->cursor += 4;
689
690   Fail:
691     return error;
692
693   Fail_Private:
694     FREE( private_dict );
695     goto Fail;
696   }
697
698
699   /*************************************************************************/
700   /*                                                                       */
701   /* <Function>                                                            */
702   /*    Read_Token                                                         */
703   /*                                                                       */
704   /* <Description>                                                         */
705   /*    Reads a new token from the current input stream.  This function    */
706   /*    extracts a token from the font program until Open_PrivateDict()    */
707   /*    has been called.  After this, it returns tokens from the           */
708   /*    (eexec-encrypted) private dictionary.                              */
709   /*                                                                       */
710   /* <Input>                                                               */
711   /*    tokenizer :: The target tokenizer object.                          */
712   /*                                                                       */
713   /* <Return>                                                              */
714   /*    FreeType error code.  0 means success.                             */
715   /*                                                                       */
716   /* <Note>                                                                */
717   /*    Use the function Read_CharStrings() to read the binary charstrings */
718   /*    from the private dict.                                             */
719   /*                                                                       */
720   LOCAL_FUNC
721   FT_Error  Read_Token( T1_Tokenizer  tokenizer )
722   {
723     T1_Tokenizer  tok = tokenizer;
724     FT_Long       cur, limit;
725     FT_Byte*      base;
726     char          c, starter, ender;
727     FT_Bool       token_started;
728
729     T1_TokenType  kind;
730
731
732     tok->error      = T1_Err_Ok;
733     tok->token.kind = tok_any;
734
735     base  = tok->base;
736     limit = tok->limit;
737     cur   = tok->cursor;
738
739     token_started = 0;
740
741     for (;;)
742     {
743       if ( cur >= limit )
744       {
745         if ( grow( tok ) )
746           goto Exit;
747         base  = tok->base;
748         limit = tok->limit;
749       }
750
751       c = (char)base[cur++];
752
753       /* check that we have an ASCII character */
754       if ( (FT_Byte)c > 127 )
755       {
756         FT_ERROR(( "Read_Token:" ));
757         FT_ERROR(( " unexpected binary data in Type1 fragment!\n" ));
758         tok->error = T1_Err_Invalid_File_Format;
759         goto Exit;
760       }
761
762       switch ( c )
763       {
764       case '\r':
765       case '\n':
766       case ' ' :
767       case '\t':    /* skip initial whitespace => skip to next */
768         if ( token_started )
769         {
770           /* possibly a name, keyword, wathever */
771           tok->token.kind = tok_any;
772           tok->token.len  = cur-tok->token.start - 1;
773           goto Exit;
774         }
775         /* otherwise, skip everything */
776         break;
777
778       case '%':     /* this is a comment -- skip everything */
779         for (;;)
780         {
781           FT_Int  left = limit - cur;
782
783
784           while ( left > 0 )
785           {
786             c = (char)base[cur++];
787             if ( c == '\r' || c == '\n' )
788               goto Next;
789             left--;
790           }
791
792           if ( grow( tokenizer ) )
793             goto Exit;
794           base  = tok->base;
795           limit = tok->limit;
796         }
797
798       case '(':     /* a Postscript string */
799         kind  = tok_string;
800         ender = ')';
801
802       L1:
803         if ( !token_started )
804         {
805           token_started    = 1;
806           tok->token.start = cur - 1;
807         }
808
809         {
810           FT_Int  nest_level = 1;
811
812
813           starter = c;
814           for (;;)
815           {
816             FT_Int  left = limit - cur;
817
818
819             while ( left > 0 )
820             {
821               c = (char)base[cur++];
822
823               if ( c == starter )
824                 nest_level++;
825
826               else if ( c == ender )
827               {
828                 nest_level--;
829                 if ( nest_level <= 0 )
830                 {
831                   tok->token.kind = kind;
832                   tok->token.len  = cur - tok->token.start;
833                   goto Exit;
834                 }
835               }
836               left--;
837             }
838
839             if ( grow( tok ) )
840               goto Exit;
841             base  = tok->base;
842             limit = tok->limit;
843           }
844         }
845
846       case '[':   /* a Postscript array */
847         if ( token_started )
848           goto Any_Token;
849
850         kind  = tok_array;
851         ender = ']';
852         goto L1;
853         break;
854
855       case '{':   /* a Postscript program */
856         if ( token_started )
857           goto Any_Token;
858
859         kind  = tok_program;
860         ender = '}';
861         goto L1;
862         break;
863
864       case '<':   /* a Postscript hex byte array? */
865         if ( token_started )
866           goto Any_Token;
867
868         kind  = tok_hexarray;
869         ender = '>';
870         goto L1;
871         break;
872
873       case '0':  /* any number */
874       case '1':
875       case '2':
876       case '3':
877       case '4':
878       case '5':
879       case '6':
880       case '7':
881       case '8':
882       case '9':
883         if ( token_started )
884           goto Next;
885
886         tok->token.kind  = tok_number;
887         token_started    = 1;
888         tok->token.start = cur - 1;
889
890       L2:
891         for (;;)
892         {
893           FT_Int  left = limit-cur;
894
895
896           while ( left > 0 )
897           {
898             c = (char)base[cur++];
899
900             switch ( c )
901             {
902             case '[':                     /* ] */
903             case '{':                     /* } */
904             case '(':                     /* ) */
905             case '<':
906             case '/':
907               goto Any_Token;
908
909             case ' ':
910             case '\r':
911             case '\t':
912             case '\n':
913               tok->token.len = cur - tok->token.start - 1;
914               goto Exit;
915
916             default:
917               ;
918             }
919             left--;
920           }
921
922           if ( grow( tok ) )
923             goto Exit;
924           base  = tok->base;
925           limit = tok->limit;
926         }
927
928       case '.':   /* maybe a number */
929       case '-':
930       case '+':
931         if ( token_started )
932            goto Next;
933
934         token_started    = 1;
935         tok->token.start = cur - 1;
936
937         for (;;)
938         {
939           FT_Int  left = limit - cur;
940
941
942           if ( left > 0 )
943           {
944             /* test for any following digit, interpreted as number */
945             c = (char)base[cur];
946             tok->token.kind = ( c >= '0' && c <= '9' ? tok_number : tok_any );
947             goto L2;
948           }
949
950           if ( grow( tok ) )
951             goto Exit;
952           base  = tok->base;
953           limit = tok->limit;
954         }
955
956       case '/':  /* maybe an immediate name */
957         if ( !token_started )
958         {
959           token_started    = 1;
960           tok->token.start = cur - 1;
961
962           for (;;)
963           {
964             FT_Int  left = limit - cur;
965
966
967             if ( left > 0 )
968             {
969               /* test for single '/', interpreted as garbage */
970               c = (char)base[cur];
971               tok->token.kind = ( c == ' '  || c == '\t' ||
972                                   c == '\r' || c == '\n' ) ? tok_any
973                                                            : tok_immediate;
974               goto L2;
975             }
976
977             if ( grow( tok ) )
978               goto Exit;
979             base  = tok->base;
980             limit = tok->limit;
981           }
982         }
983         else
984         {
985       Any_Token:        /* possibly a name or wathever */
986           cur--;
987           tok->token.len = cur - tok->token.start;
988           goto Exit;
989         }
990
991       default:
992         if ( !token_started )
993         {
994           token_started    = 1;
995           tok->token.start = cur - 1;
996         }
997       }
998
999     Next:
1000       ;
1001     }
1002
1003   Exit:
1004     tok->cursor = cur;
1005
1006     if ( !tok->error )
1007     {
1008       /* now, tries to match keywords and immediate names */
1009       FT_Int  index;
1010
1011
1012       switch ( tok->token.kind )
1013       {
1014       case tok_immediate:   /* immediate name */
1015         index = Find_Name( (char*)( tok->base + tok->token.start + 1 ),
1016                            tok->token.len - 1,
1017                            t1_immediates,
1018                            imm_max - imm_first_ );
1019         tok->token.kind2 = ( index >= 0 )
1020                               ? (T1_TokenType)( imm_first_ + index )
1021                               : tok_error;
1022         break;
1023
1024       case tok_any:         /* test for keyword */
1025         index = Find_Name( (char*)( tok->base + tok->token.start ),
1026                            tok->token.len,
1027                            t1_keywords,
1028                            key_max - key_first_ );
1029         if ( index >= 0 )
1030         {
1031           tok->token.kind  = tok_keyword;
1032           tok->token.kind2 = (T1_TokenType)( key_first_ + index );
1033         }
1034         else
1035           tok->token.kind2 = tok_error;
1036         break;
1037
1038       default:
1039          tok->token.kind2 = tok_error;
1040       }
1041     }
1042     return tokenizer->error;
1043   }
1044
1045
1046 #if 0
1047
1048   /*************************************************************************/
1049   /*                                                                       */
1050   /* <Function>                                                            */
1051   /*    Read_CharStrings                                                   */
1052   /*                                                                       */
1053   /* <Description>                                                         */
1054   /*    Reads a charstrings element from the current input stream.  These  */
1055   /*    are binary bytes that encode each individual glyph outline.        */
1056   /*                                                                       */
1057   /*    The caller is responsible for skipping the `lenIV' bytes at the    */
1058   /*    start of the record.                                               */
1059   /*                                                                       */
1060   /* <Input>                                                               */
1061   /*    tokenizer :: The target tokenizer object.                          */
1062   /*    num_chars :: The number of binary bytes to read.                   */
1063   /*                                                                       */
1064   /* <Output>                                                              */
1065   /*    buffer    :: The target array of bytes.  These are                 */
1066   /*    eexec-decrypted.                                                   */
1067   /*                                                                       */
1068   /* <Return>                                                              */
1069   /*    FreeType error code.  0 means success.                             */
1070   /*                                                                       */
1071   /* <Note>                                                                */
1072   /*    Use the function Read_CharStrings() to read binary charstrings     */
1073   /*    from the private dict.                                             */
1074   /*                                                                       */
1075   LOCAL_FUNC
1076   FT_Error  Read_CharStrings( T1_Tokenizer  tokenizer,
1077                               FT_Int        num_chars,
1078                               FT_Byte*      buffer )
1079   {
1080     for (;;)
1081     {
1082       FT_Int  left = tokenizer->limit - tokenizer->cursor;
1083
1084
1085       if ( left >= num_chars )
1086       {
1087         MEM_Copy( buffer, tokenizer->base + tokenizer->cursor, num_chars );
1088         t1_decrypt( buffer, num_chars, 4330 );
1089         tokenizer->cursor += num_chars;
1090         return T1_Err_Ok;
1091       }
1092
1093       if ( grow( tokenizer ) )
1094         return tokenizer->error;
1095     }
1096   }
1097
1098 #endif /* 0 */
1099
1100
1101 /* END */