source: trunk/poppler/freetype2/src/psaux/psobjs.c @ 262

Last change on this file since 262 was 262, checked in by Eugene Romanenko, 12 years ago

PDF plugin: freetype library updated to version 2.3.8

File size: 48.3 KB
Line 
1/***************************************************************************/
2/*                                                                         */
3/*  psobjs.c                                                               */
4/*                                                                         */
5/*    Auxiliary functions for PostScript fonts (body).                     */
6/*                                                                         */
7/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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#include <ft2build.h>
20#include FT_INTERNAL_POSTSCRIPT_AUX_H
21#include FT_INTERNAL_DEBUG_H
22
23#include "psobjs.h"
24#include "psconv.h"
25
26#include "psauxerr.h"
27
28
29  /*************************************************************************/
30  /*                                                                       */
31  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
32  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
33  /* messages during execution.                                            */
34  /*                                                                       */
35#undef  FT_COMPONENT
36#define FT_COMPONENT  trace_psobjs
37
38
39  /*************************************************************************/
40  /*************************************************************************/
41  /*****                                                               *****/
42  /*****                             PS_TABLE                          *****/
43  /*****                                                               *****/
44  /*************************************************************************/
45  /*************************************************************************/
46
47  /*************************************************************************/
48  /*                                                                       */
49  /* <Function>                                                            */
50  /*    ps_table_new                                                       */
51  /*                                                                       */
52  /* <Description>                                                         */
53  /*    Initializes a PS_Table.                                            */
54  /*                                                                       */
55  /* <InOut>                                                               */
56  /*    table  :: The address of the target table.                         */
57  /*                                                                       */
58  /* <Input>                                                               */
59  /*    count  :: The table size = the maximum number of elements.         */
60  /*                                                                       */
61  /*    memory :: The memory object to use for all subsequent              */
62  /*              reallocations.                                           */
63  /*                                                                       */
64  /* <Return>                                                              */
65  /*    FreeType error code.  0 means success.                             */
66  /*                                                                       */
67  FT_LOCAL_DEF( FT_Error )
68  ps_table_new( PS_Table   table,
69                FT_Int     count,
70                FT_Memory  memory )
71  {
72    FT_Error  error;
73
74
75    table->memory = memory;
76    if ( FT_NEW_ARRAY( table->elements, count ) ||
77         FT_NEW_ARRAY( table->lengths,  count ) )
78      goto Exit;
79
80    table->max_elems = count;
81    table->init      = 0xDEADBEEFUL;
82    table->num_elems = 0;
83    table->block     = 0;
84    table->capacity  = 0;
85    table->cursor    = 0;
86
87    *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs;
88
89  Exit:
90    if ( error )
91      FT_FREE( table->elements );
92
93    return error;
94  }
95
96
97  static void
98  shift_elements( PS_Table  table,
99                  FT_Byte*  old_base )
100  {
101    FT_PtrDist  delta  = table->block - old_base;
102    FT_Byte**   offset = table->elements;
103    FT_Byte**   limit  = offset + table->max_elems;
104
105
106    for ( ; offset < limit; offset++ )
107    {
108      if ( offset[0] )
109        offset[0] += delta;
110    }
111  }
112
113
114  static FT_Error
115  reallocate_t1_table( PS_Table  table,
116                       FT_Long   new_size )
117  {
118    FT_Memory  memory   = table->memory;
119    FT_Byte*   old_base = table->block;
120    FT_Error   error;
121
122
123    /* allocate new base block */
124    if ( FT_ALLOC( table->block, new_size ) )
125    {
126      table->block = old_base;
127      return error;
128    }
129
130    /* copy elements and shift offsets */
131    if ( old_base )
132    {
133      FT_MEM_COPY( table->block, old_base, table->capacity );
134      shift_elements( table, old_base );
135      FT_FREE( old_base );
136    }
137
138    table->capacity = new_size;
139
140    return PSaux_Err_Ok;
141  }
142
143
144  /*************************************************************************/
145  /*                                                                       */
146  /* <Function>                                                            */
147  /*    ps_table_add                                                       */
148  /*                                                                       */
149  /* <Description>                                                         */
150  /*    Adds an object to a PS_Table, possibly growing its memory block.   */
151  /*                                                                       */
152  /* <InOut>                                                               */
153  /*    table  :: The target table.                                        */
154  /*                                                                       */
155  /* <Input>                                                               */
156  /*    idx    :: The index of the object in the table.                    */
157  /*                                                                       */
158  /*    object :: The address of the object to copy in memory.             */
159  /*                                                                       */
160  /*    length :: The length in bytes of the source object.                */
161  /*                                                                       */
162  /* <Return>                                                              */
163  /*    FreeType error code.  0 means success.  An error is returned if a  */
164  /*    reallocation fails.                                                */
165  /*                                                                       */
166  FT_LOCAL_DEF( FT_Error )
167  ps_table_add( PS_Table    table,
168                FT_Int      idx,
169                void*       object,
170                FT_PtrDist  length )
171  {
172    if ( idx < 0 || idx >= table->max_elems )
173    {
174      FT_ERROR(( "ps_table_add: invalid index\n" ));
175      return PSaux_Err_Invalid_Argument;
176    }
177
178    if ( length < 0 )
179    {
180      FT_ERROR(( "ps_table_add: invalid length\n" ));
181      return PSaux_Err_Invalid_Argument;
182    }
183
184    /* grow the base block if needed */
185    if ( table->cursor + length > table->capacity )
186    {
187      FT_Error   error;
188      FT_Offset  new_size = table->capacity;
189      FT_Long    in_offset;
190
191
192      in_offset = (FT_Long)((FT_Byte*)object - table->block);
193      if ( (FT_ULong)in_offset >= table->capacity )
194        in_offset = -1;
195
196      while ( new_size < table->cursor + length )
197      {
198        /* increase size by 25% and round up to the nearest multiple
199           of 1024 */
200        new_size += ( new_size >> 2 ) + 1;
201        new_size  = FT_PAD_CEIL( new_size, 1024 );
202      }
203
204      error = reallocate_t1_table( table, new_size );
205      if ( error )
206        return error;
207
208      if ( in_offset >= 0 )
209        object = table->block + in_offset;
210    }
211
212    /* add the object to the base block and adjust offset */
213    table->elements[idx] = table->block + table->cursor;
214    table->lengths [idx] = length;
215    FT_MEM_COPY( table->block + table->cursor, object, length );
216
217    table->cursor += length;
218    return PSaux_Err_Ok;
219  }
220
221
222  /*************************************************************************/
223  /*                                                                       */
224  /* <Function>                                                            */
225  /*    ps_table_done                                                      */
226  /*                                                                       */
227  /* <Description>                                                         */
228  /*    Finalizes a PS_TableRec (i.e., reallocate it to its current        */
229  /*    cursor).                                                           */
230  /*                                                                       */
231  /* <InOut>                                                               */
232  /*    table :: The target table.                                         */
233  /*                                                                       */
234  /* <Note>                                                                */
235  /*    This function does NOT release the heap's memory block.  It is up  */
236  /*    to the caller to clean it, or reference it in its own structures.  */
237  /*                                                                       */
238  FT_LOCAL_DEF( void )
239  ps_table_done( PS_Table  table )
240  {
241    FT_Memory  memory = table->memory;
242    FT_Error   error;
243    FT_Byte*   old_base = table->block;
244
245
246    /* should never fail, because rec.cursor <= rec.size */
247    if ( !old_base )
248      return;
249
250    if ( FT_ALLOC( table->block, table->cursor ) )
251      return;
252    FT_MEM_COPY( table->block, old_base, table->cursor );
253    shift_elements( table, old_base );
254
255    table->capacity = table->cursor;
256    FT_FREE( old_base );
257
258    FT_UNUSED( error );
259  }
260
261
262  FT_LOCAL_DEF( void )
263  ps_table_release( PS_Table  table )
264  {
265    FT_Memory  memory = table->memory;
266
267
268    if ( (FT_ULong)table->init == 0xDEADBEEFUL )
269    {
270      FT_FREE( table->block );
271      FT_FREE( table->elements );
272      FT_FREE( table->lengths );
273      table->init = 0;
274    }
275  }
276
277
278  /*************************************************************************/
279  /*************************************************************************/
280  /*****                                                               *****/
281  /*****                            T1 PARSER                          *****/
282  /*****                                                               *****/
283  /*************************************************************************/
284  /*************************************************************************/
285
286
287  /* first character must be already part of the comment */
288
289  static void
290  skip_comment( FT_Byte*  *acur,
291                FT_Byte*   limit )
292  {
293    FT_Byte*  cur = *acur;
294
295
296    while ( cur < limit )
297    {
298      if ( IS_PS_NEWLINE( *cur ) )
299        break;
300      cur++;
301    }
302
303    *acur = cur;
304  }
305
306
307  static void
308  skip_spaces( FT_Byte*  *acur,
309               FT_Byte*   limit )
310  {
311    FT_Byte*  cur = *acur;
312
313
314    while ( cur < limit )
315    {
316      if ( !IS_PS_SPACE( *cur ) )
317      {
318        if ( *cur == '%' )
319          /* According to the PLRM, a comment is equal to a space. */
320          skip_comment( &cur, limit );
321        else
322          break;
323      }
324      cur++;
325    }
326
327    *acur = cur;
328  }
329
330
331#define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' )
332
333
334  /* first character must be `(';                               */
335  /* *acur is positioned at the character after the closing `)' */
336
337  static FT_Error
338  skip_literal_string( FT_Byte*  *acur,
339                       FT_Byte*   limit )
340  {
341    FT_Byte*      cur   = *acur;
342    FT_Int        embed = 0;
343    FT_Error      error = PSaux_Err_Invalid_File_Format;
344    unsigned int  i;
345
346
347    while ( cur < limit )
348    {
349      FT_Byte  c = *cur;
350
351
352      ++cur;
353
354      if ( c == '\\' )
355      {
356        /* Red Book 3rd ed., section `Literal Text Strings', p. 29:     */
357        /* A backslash can introduce three different types              */
358        /* of escape sequences:                                         */
359        /*   - a special escaped char like \r, \n, etc.                 */
360        /*   - a one-, two-, or three-digit octal number                */
361        /*   - none of the above in which case the backslash is ignored */
362
363        if ( cur == limit )
364          /* error (or to be ignored?) */
365          break;
366
367        switch ( *cur )
368        {
369          /* skip `special' escape */
370        case 'n':
371        case 'r':
372        case 't':
373        case 'b':
374        case 'f':
375        case '\\':
376        case '(':
377        case ')':
378          ++cur;
379          break;
380
381        default:
382          /* skip octal escape or ignore backslash */
383          for ( i = 0; i < 3 && cur < limit; ++i )
384          {
385            if ( !IS_OCTAL_DIGIT( *cur ) )
386              break;
387
388            ++cur;
389          }
390        }
391      }
392      else if ( c == '(' )
393        embed++;
394      else if ( c == ')' )
395      {
396        embed--;
397        if ( embed == 0 )
398        {
399          error = PSaux_Err_Ok;
400          break;
401        }
402      }
403    }
404
405    *acur = cur;
406
407    return error;
408  }
409
410
411  /* first character must be `<' */
412
413  static FT_Error
414  skip_string( FT_Byte*  *acur,
415               FT_Byte*   limit )
416  {
417    FT_Byte*  cur = *acur;
418    FT_Error  err =  PSaux_Err_Ok;
419
420
421    while ( ++cur < limit )
422    {
423      /* All whitespace characters are ignored. */
424      skip_spaces( &cur, limit );
425      if ( cur >= limit )
426        break;
427
428      if ( !IS_PS_XDIGIT( *cur ) )
429        break;
430    }
431
432    if ( cur < limit && *cur != '>' )
433    {
434      FT_ERROR(( "skip_string: missing closing delimiter `>'\n" ));
435      err = PSaux_Err_Invalid_File_Format;
436    }
437    else
438      cur++;
439
440    *acur = cur;
441    return err;
442  }
443
444
445  /* first character must be the opening brace that */
446  /* starts the procedure                           */
447
448  /* NB: [ and ] need not match:                    */
449  /* `/foo {[} def' is a valid PostScript fragment, */
450  /* even within a Type1 font                       */
451
452  static FT_Error
453  skip_procedure( FT_Byte*  *acur,
454                  FT_Byte*   limit )
455  {
456    FT_Byte*  cur;
457    FT_Int    embed = 0;
458    FT_Error  error = PSaux_Err_Ok;
459
460
461    FT_ASSERT( **acur == '{' );
462
463    for ( cur = *acur; cur < limit && error == PSaux_Err_Ok; ++cur )
464    {
465      switch ( *cur )
466      {
467      case '{':
468        ++embed;
469        break;
470
471      case '}':
472        --embed;
473        if ( embed == 0 )
474        {
475          ++cur;
476          goto end;
477        }
478        break;
479
480      case '(':
481        error = skip_literal_string( &cur, limit );
482        break;
483
484      case '<':
485        error = skip_string( &cur, limit );
486        break;
487
488      case '%':
489        skip_comment( &cur, limit );
490        break;
491      }
492    }
493
494  end:
495    if ( embed != 0 )
496      error = PSaux_Err_Invalid_File_Format;
497
498    *acur = cur;
499
500    return error;
501  }
502
503
504  /***********************************************************************/
505  /*                                                                     */
506  /* All exported parsing routines handle leading whitespace and stop at */
507  /* the first character which isn't part of the just handled token.     */
508  /*                                                                     */
509  /***********************************************************************/
510
511
512  FT_LOCAL_DEF( void )
513  ps_parser_skip_PS_token( PS_Parser  parser )
514  {
515    /* Note: PostScript allows any non-delimiting, non-whitespace        */
516    /*       character in a name (PS Ref Manual, 3rd ed, p31).           */
517    /*       PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */
518
519    FT_Byte*  cur   = parser->cursor;
520    FT_Byte*  limit = parser->limit;
521    FT_Error  error = PSaux_Err_Ok;
522
523
524    skip_spaces( &cur, limit );             /* this also skips comments */
525    if ( cur >= limit )
526      goto Exit;
527
528    /* self-delimiting, single-character tokens */
529    if ( *cur == '[' || *cur == ']' )
530    {
531      cur++;
532      goto Exit;
533    }
534
535    /* skip balanced expressions (procedures and strings) */
536
537    if ( *cur == '{' )                              /* {...} */
538    {
539      error = skip_procedure( &cur, limit );
540      goto Exit;
541    }
542
543    if ( *cur == '(' )                              /* (...) */
544    {
545      error = skip_literal_string( &cur, limit );
546      goto Exit;
547    }
548
549    if ( *cur == '<' )                              /* <...> */
550    {
551      if ( cur + 1 < limit && *(cur + 1) == '<' )   /* << */
552      {
553        cur++;
554        cur++;
555      }
556      else
557        error = skip_string( &cur, limit );
558
559      goto Exit;
560    }
561
562    if ( *cur == '>' )
563    {
564      cur++;
565      if ( cur >= limit || *cur != '>' )             /* >> */
566      {
567        FT_ERROR(( "ps_parser_skip_PS_token: "
568                   "unexpected closing delimiter `>'\n" ));
569        error = PSaux_Err_Invalid_File_Format;
570        goto Exit;
571      }
572      cur++;
573      goto Exit;
574    }
575
576    if ( *cur == '/' )
577      cur++;
578
579    /* anything else */
580    while ( cur < limit )
581    {
582      /* *cur might be invalid (e.g., ')' or '}'), but this   */
583      /* is handled by the test `cur == parser->cursor' below */
584      if ( IS_PS_DELIM( *cur ) )
585        break;
586
587      cur++;
588    }
589
590  Exit:
591    if ( cur == parser->cursor )
592    {
593      FT_ERROR(( "ps_parser_skip_PS_token: "
594                 "current token is `%c', which is self-delimiting "
595                 "but invalid at this point\n",
596                 *cur ));
597
598      error = PSaux_Err_Invalid_File_Format;
599    }
600
601    parser->error  = error;
602    parser->cursor = cur;
603  }
604
605
606  FT_LOCAL_DEF( void )
607  ps_parser_skip_spaces( PS_Parser  parser )
608  {
609    skip_spaces( &parser->cursor, parser->limit );
610  }
611
612
613  /* `token' here means either something between balanced delimiters */
614  /* or the next token; the delimiters are not removed.              */
615
616  FT_LOCAL_DEF( void )
617  ps_parser_to_token( PS_Parser  parser,
618                      T1_Token   token )
619  {
620    FT_Byte*  cur;
621    FT_Byte*  limit;
622    FT_Int    embed;
623
624
625    token->type  = T1_TOKEN_TYPE_NONE;
626    token->start = 0;
627    token->limit = 0;
628
629    /* first of all, skip leading whitespace */
630    ps_parser_skip_spaces( parser );
631
632    cur   = parser->cursor;
633    limit = parser->limit;
634
635    if ( cur >= limit )
636      return;
637
638    switch ( *cur )
639    {
640      /************* check for literal string *****************/
641    case '(':
642      token->type  = T1_TOKEN_TYPE_STRING;
643      token->start = cur;
644
645      if ( skip_literal_string( &cur, limit ) == PSaux_Err_Ok )
646        token->limit = cur;
647      break;
648
649      /************* check for programs/array *****************/
650    case '{':
651      token->type  = T1_TOKEN_TYPE_ARRAY;
652      token->start = cur;
653
654      if ( skip_procedure( &cur, limit ) == PSaux_Err_Ok )
655        token->limit = cur;
656      break;
657
658      /************* check for table/array ********************/
659      /* XXX: in theory we should also look for "<<"          */
660      /*      since this is semantically equivalent to "[";   */
661      /*      in practice it doesn't matter (?)               */
662    case '[':
663      token->type  = T1_TOKEN_TYPE_ARRAY;
664      embed        = 1;
665      token->start = cur++;
666
667      /* we need this to catch `[ ]' */
668      parser->cursor = cur;
669      ps_parser_skip_spaces( parser );
670      cur = parser->cursor;
671
672      while ( cur < limit && !parser->error )
673      {
674        /* XXX: this is wrong because it does not      */
675        /*      skip comments, procedures, and strings */
676        if ( *cur == '[' )
677          embed++;
678        else if ( *cur == ']' )
679        {
680          embed--;
681          if ( embed <= 0 )
682          {
683            token->limit = ++cur;
684            break;
685          }
686        }
687
688        parser->cursor = cur;
689        ps_parser_skip_PS_token( parser );
690        /* we need this to catch `[XXX ]' */
691        ps_parser_skip_spaces  ( parser );
692        cur = parser->cursor;
693      }
694      break;
695
696      /* ************ otherwise, it is any token **************/
697    default:
698      token->start = cur;
699      token->type  = ( *cur == '/' ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY );
700      ps_parser_skip_PS_token( parser );
701      cur = parser->cursor;
702      if ( !parser->error )
703        token->limit = cur;
704    }
705
706    if ( !token->limit )
707    {
708      token->start = 0;
709      token->type  = T1_TOKEN_TYPE_NONE;
710    }
711
712    parser->cursor = cur;
713  }
714
715
716  /* NB: `tokens' can be NULL if we only want to count */
717  /* the number of array elements                      */
718
719  FT_LOCAL_DEF( void )
720  ps_parser_to_token_array( PS_Parser  parser,
721                            T1_Token   tokens,
722                            FT_UInt    max_tokens,
723                            FT_Int*    pnum_tokens )
724  {
725    T1_TokenRec  master;
726
727
728    *pnum_tokens = -1;
729
730    /* this also handles leading whitespace */
731    ps_parser_to_token( parser, &master );
732
733    if ( master.type == T1_TOKEN_TYPE_ARRAY )
734    {
735      FT_Byte*  old_cursor = parser->cursor;
736      FT_Byte*  old_limit  = parser->limit;
737      T1_Token  cur        = tokens;
738      T1_Token  limit      = cur + max_tokens;
739
740
741      /* don't include outermost delimiters */
742      parser->cursor = master.start + 1;
743      parser->limit  = master.limit - 1;
744
745      while ( parser->cursor < parser->limit )
746      {
747        T1_TokenRec  token;
748
749
750        ps_parser_to_token( parser, &token );
751        if ( !token.type )
752          break;
753
754        if ( tokens != NULL && cur < limit )
755          *cur = token;
756
757        cur++;
758      }
759
760      *pnum_tokens = (FT_Int)( cur - tokens );
761
762      parser->cursor = old_cursor;
763      parser->limit  = old_limit;
764    }
765  }
766
767
768  /* first character must be a delimiter or a part of a number */
769  /* NB: `coords' can be NULL if we just want to skip the      */
770  /*     array; in this case we ignore `max_coords'            */
771
772  static FT_Int
773  ps_tocoordarray( FT_Byte*  *acur,
774                   FT_Byte*   limit,
775                   FT_Int     max_coords,
776                   FT_Short*  coords )
777  {
778    FT_Byte*  cur   = *acur;
779    FT_Int    count = 0;
780    FT_Byte   c, ender;
781
782
783    if ( cur >= limit )
784      goto Exit;
785
786    /* check for the beginning of an array; otherwise, only one number */
787    /* will be read                                                    */
788    c     = *cur;
789    ender = 0;
790
791    if ( c == '[' )
792      ender = ']';
793    else if ( c == '{' )
794      ender = '}';
795
796    if ( ender )
797      cur++;
798
799    /* now, read the coordinates */
800    while ( cur < limit )
801    {
802      FT_Short  dummy;
803      FT_Byte*  old_cur;
804
805
806      /* skip whitespace in front of data */
807      skip_spaces( &cur, limit );
808      if ( cur >= limit )
809        goto Exit;
810
811      if ( *cur == ender )
812      {
813        cur++;
814        break;
815      }
816
817      old_cur = cur;
818
819      if ( coords != NULL && count >= max_coords )
820        break;
821
822      /* call PS_Conv_ToFixed() even if coords == NULL */
823      /* to properly parse number at `cur'             */
824      *( coords != NULL ? &coords[count] : &dummy ) =
825        (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 );
826
827      if ( old_cur == cur )
828      {
829        count = -1;
830        goto Exit;
831      }
832      else
833        count++;
834
835      if ( !ender )
836        break;
837    }
838
839  Exit:
840    *acur = cur;
841    return count;
842  }
843
844
845  /* first character must be a delimiter or a part of a number */
846  /* NB: `values' can be NULL if we just want to skip the      */
847  /*     array; in this case we ignore `max_values'            */
848
849  static FT_Int
850  ps_tofixedarray( FT_Byte*  *acur,
851                   FT_Byte*   limit,
852                   FT_Int     max_values,
853                   FT_Fixed*  values,
854                   FT_Int     power_ten )
855  {
856    FT_Byte*  cur   = *acur;
857    FT_Int    count = 0;
858    FT_Byte   c, ender;
859
860
861    if ( cur >= limit )
862      goto Exit;
863
864    /* Check for the beginning of an array.  Otherwise, only one number */
865    /* will be read.                                                    */
866    c     = *cur;
867    ender = 0;
868
869    if ( c == '[' )
870      ender = ']';
871    else if ( c == '{' )
872      ender = '}';
873
874    if ( ender )
875      cur++;
876
877    /* now, read the values */
878    while ( cur < limit )
879    {
880      FT_Fixed  dummy;
881      FT_Byte*  old_cur;
882
883
884      /* skip whitespace in front of data */
885      skip_spaces( &cur, limit );
886      if ( cur >= limit )
887        goto Exit;
888
889      if ( *cur == ender )
890      {
891        cur++;
892        break;
893      }
894
895      old_cur = cur;
896
897      if ( values != NULL && count >= max_values )
898        break;
899
900      /* call PS_Conv_ToFixed() even if coords == NULL */
901      /* to properly parse number at `cur'             */
902      *( values != NULL ? &values[count] : &dummy ) =
903        PS_Conv_ToFixed( &cur, limit, power_ten );
904
905      if ( old_cur == cur )
906      {
907        count = -1;
908        goto Exit;
909      }
910      else
911        count++;
912
913      if ( !ender )
914        break;
915    }
916
917  Exit:
918    *acur = cur;
919    return count;
920  }
921
922
923#if 0
924
925  static FT_String*
926  ps_tostring( FT_Byte**  cursor,
927               FT_Byte*   limit,
928               FT_Memory  memory )
929  {
930    FT_Byte*    cur = *cursor;
931    FT_PtrDist  len = 0;
932    FT_Int      count;
933    FT_String*  result;
934    FT_Error    error;
935
936
937    /* XXX: some stupid fonts have a `Notice' or `Copyright' string     */
938    /*      that simply doesn't begin with an opening parenthesis, even */
939    /*      though they have a closing one!  E.g. "amuncial.pfb"        */
940    /*                                                                  */
941    /*      We must deal with these ill-fated cases there.  Note that   */
942    /*      these fonts didn't work with the old Type 1 driver as the   */
943    /*      notice/copyright was not recognized as a valid string token */
944    /*      and made the old token parser commit errors.                */
945
946    while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) )
947      cur++;
948    if ( cur + 1 >= limit )
949      return 0;
950
951    if ( *cur == '(' )
952      cur++;  /* skip the opening parenthesis, if there is one */
953
954    *cursor = cur;
955    count   = 0;
956
957    /* then, count its length */
958    for ( ; cur < limit; cur++ )
959    {
960      if ( *cur == '(' )
961        count++;
962
963      else if ( *cur == ')' )
964      {
965        count--;
966        if ( count < 0 )
967          break;
968      }
969    }
970
971    len = cur - *cursor;
972    if ( cur >= limit || FT_ALLOC( result, len + 1 ) )
973      return 0;
974
975    /* now copy the string */
976    FT_MEM_COPY( result, *cursor, len );
977    result[len] = '\0';
978    *cursor = cur;
979    return result;
980  }
981
982#endif /* 0 */
983
984
985  static int
986  ps_tobool( FT_Byte*  *acur,
987             FT_Byte*   limit )
988  {
989    FT_Byte*  cur    = *acur;
990    FT_Bool   result = 0;
991
992
993    /* return 1 if we find `true', 0 otherwise */
994    if ( cur + 3 < limit &&
995         cur[0] == 't'   &&
996         cur[1] == 'r'   &&
997         cur[2] == 'u'   &&
998         cur[3] == 'e'   )
999    {
1000      result = 1;
1001      cur   += 5;
1002    }
1003    else if ( cur + 4 < limit &&
1004              cur[0] == 'f'   &&
1005              cur[1] == 'a'   &&
1006              cur[2] == 'l'   &&
1007              cur[3] == 's'   &&
1008              cur[4] == 'e'   )
1009    {
1010      result = 0;
1011      cur   += 6;
1012    }
1013
1014    *acur = cur;
1015    return result;
1016  }
1017
1018
1019  /* load a simple field (i.e. non-table) into the current list of objects */
1020
1021  FT_LOCAL_DEF( FT_Error )
1022  ps_parser_load_field( PS_Parser       parser,
1023                        const T1_Field  field,
1024                        void**          objects,
1025                        FT_UInt         max_objects,
1026                        FT_ULong*       pflags )
1027  {
1028    T1_TokenRec  token;
1029    FT_Byte*     cur;
1030    FT_Byte*     limit;
1031    FT_UInt      count;
1032    FT_UInt      idx;
1033    FT_Error     error;
1034
1035
1036    /* this also skips leading whitespace */
1037    ps_parser_to_token( parser, &token );
1038    if ( !token.type )
1039      goto Fail;
1040
1041    count = 1;
1042    idx   = 0;
1043    cur   = token.start;
1044    limit = token.limit;
1045
1046    /* we must detect arrays in /FontBBox */
1047    if ( field->type == T1_FIELD_TYPE_BBOX )
1048    {
1049      T1_TokenRec  token2;
1050      FT_Byte*     old_cur   = parser->cursor;
1051      FT_Byte*     old_limit = parser->limit;
1052
1053
1054      /* don't include delimiters */
1055      parser->cursor = token.start + 1;
1056      parser->limit  = token.limit - 1;
1057
1058      ps_parser_to_token( parser, &token2 );
1059      parser->cursor = old_cur;
1060      parser->limit  = old_limit;
1061
1062      if ( token2.type == T1_TOKEN_TYPE_ARRAY )
1063        goto FieldArray;
1064    }
1065    else if ( token.type == T1_TOKEN_TYPE_ARRAY )
1066    {
1067    FieldArray:
1068      /* if this is an array and we have no blend, an error occurs */
1069      if ( max_objects == 0 )
1070        goto Fail;
1071
1072      count = max_objects;
1073      idx   = 1;
1074
1075      /* don't include delimiters */
1076      cur++;
1077      limit--;
1078    }
1079
1080    for ( ; count > 0; count--, idx++ )
1081    {
1082      FT_Byte*    q = (FT_Byte*)objects[idx] + field->offset;
1083      FT_Long     val;
1084      FT_String*  string;
1085
1086
1087      skip_spaces( &cur, limit );
1088
1089      switch ( field->type )
1090      {
1091      case T1_FIELD_TYPE_BOOL:
1092        val = ps_tobool( &cur, limit );
1093        goto Store_Integer;
1094
1095      case T1_FIELD_TYPE_FIXED:
1096        val = PS_Conv_ToFixed( &cur, limit, 0 );
1097        goto Store_Integer;
1098
1099      case T1_FIELD_TYPE_FIXED_1000:
1100        val = PS_Conv_ToFixed( &cur, limit, 3 );
1101        goto Store_Integer;
1102
1103      case T1_FIELD_TYPE_INTEGER:
1104        val = PS_Conv_ToInt( &cur, limit );
1105        /* fall through */
1106
1107      Store_Integer:
1108        switch ( field->size )
1109        {
1110        case (8 / FT_CHAR_BIT):
1111          *(FT_Byte*)q = (FT_Byte)val;
1112          break;
1113
1114        case (16 / FT_CHAR_BIT):
1115          *(FT_UShort*)q = (FT_UShort)val;
1116          break;
1117
1118        case (32 / FT_CHAR_BIT):
1119          *(FT_UInt32*)q = (FT_UInt32)val;
1120          break;
1121
1122        default:                /* for 64-bit systems */
1123          *(FT_Long*)q = val;
1124        }
1125        break;
1126
1127      case T1_FIELD_TYPE_STRING:
1128      case T1_FIELD_TYPE_KEY:
1129        {
1130          FT_Memory  memory = parser->memory;
1131          FT_UInt    len    = (FT_UInt)( limit - cur );
1132
1133
1134          if ( cur >= limit )
1135            break;
1136
1137          /* we allow both a string or a name   */
1138          /* for cases like /FontName (foo) def */
1139          if ( token.type == T1_TOKEN_TYPE_KEY )
1140          {
1141            /* don't include leading `/' */
1142            len--;
1143            cur++;
1144          }
1145          else if ( token.type == T1_TOKEN_TYPE_STRING )
1146          {
1147            /* don't include delimiting parentheses    */
1148            /* XXX we don't handle <<...>> here        */
1149            /* XXX should we convert octal escapes?    */
1150            /*     if so, what encoding should we use? */
1151            cur++;
1152            len -= 2;
1153          }
1154          else
1155          {
1156            FT_ERROR(( "ps_parser_load_field: expected a name or string "
1157                       "but found token of type %d instead\n",
1158                       token.type ));
1159            error = PSaux_Err_Invalid_File_Format;
1160            goto Exit;
1161          }
1162
1163          /* for this to work (FT_String**)q must have been */
1164          /* initialized to NULL                            */
1165          if ( *(FT_String**)q != NULL )
1166          {
1167            FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n",
1168                        field->ident ));
1169            FT_FREE( *(FT_String**)q );
1170            *(FT_String**)q = NULL;
1171          }
1172
1173          if ( FT_ALLOC( string, len + 1 ) )
1174            goto Exit;
1175
1176          FT_MEM_COPY( string, cur, len );
1177          string[len] = 0;
1178
1179          *(FT_String**)q = string;
1180        }
1181        break;
1182
1183      case T1_FIELD_TYPE_BBOX:
1184        {
1185          FT_Fixed  temp[4];
1186          FT_BBox*  bbox = (FT_BBox*)q;
1187          FT_Int    result;
1188
1189
1190          result = ps_tofixedarray( &cur, limit, 4, temp, 0 );
1191
1192          if ( result < 0 )
1193          {
1194            FT_ERROR(( "ps_parser_load_field: "
1195                       "expected four integers in bounding box\n" ));
1196            error = PSaux_Err_Invalid_File_Format;
1197            goto Exit;
1198          }
1199
1200          bbox->xMin = FT_RoundFix( temp[0] );
1201          bbox->yMin = FT_RoundFix( temp[1] );
1202          bbox->xMax = FT_RoundFix( temp[2] );
1203          bbox->yMax = FT_RoundFix( temp[3] );
1204        }
1205        break;
1206
1207      default:
1208        /* an error occurred */
1209        goto Fail;
1210      }
1211    }
1212
1213#if 0  /* obsolete -- keep for reference */
1214    if ( pflags )
1215      *pflags |= 1L << field->flag_bit;
1216#else
1217    FT_UNUSED( pflags );
1218#endif
1219
1220    error = PSaux_Err_Ok;
1221
1222  Exit:
1223    return error;
1224
1225  Fail:
1226    error = PSaux_Err_Invalid_File_Format;
1227    goto Exit;
1228  }
1229
1230
1231#define T1_MAX_TABLE_ELEMENTS  32
1232
1233
1234  FT_LOCAL_DEF( FT_Error )
1235  ps_parser_load_field_table( PS_Parser       parser,
1236                              const T1_Field  field,
1237                              void**          objects,
1238                              FT_UInt         max_objects,
1239                              FT_ULong*       pflags )
1240  {
1241    T1_TokenRec  elements[T1_MAX_TABLE_ELEMENTS];
1242    T1_Token     token;
1243    FT_Int       num_elements;
1244    FT_Error     error = PSaux_Err_Ok;
1245    FT_Byte*     old_cursor;
1246    FT_Byte*     old_limit;
1247    T1_FieldRec  fieldrec = *(T1_Field)field;
1248
1249
1250    fieldrec.type = T1_FIELD_TYPE_INTEGER;
1251    if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY ||
1252         field->type == T1_FIELD_TYPE_BBOX        )
1253      fieldrec.type = T1_FIELD_TYPE_FIXED;
1254
1255    ps_parser_to_token_array( parser, elements,
1256                              T1_MAX_TABLE_ELEMENTS, &num_elements );
1257    if ( num_elements < 0 )
1258    {
1259      error = PSaux_Err_Ignore;
1260      goto Exit;
1261    }
1262    if ( (FT_UInt)num_elements > field->array_max )
1263      num_elements = field->array_max;
1264
1265    old_cursor = parser->cursor;
1266    old_limit  = parser->limit;
1267
1268    /* we store the elements count if necessary;           */
1269    /* we further assume that `count_offset' can't be zero */
1270    if ( field->type != T1_FIELD_TYPE_BBOX && field->count_offset != 0 )
1271      *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) =
1272        (FT_Byte)num_elements;
1273
1274    /* we now load each element, adjusting the field.offset on each one */
1275    token = elements;
1276    for ( ; num_elements > 0; num_elements--, token++ )
1277    {
1278      parser->cursor = token->start;
1279      parser->limit  = token->limit;
1280      ps_parser_load_field( parser, &fieldrec, objects, max_objects, 0 );
1281      fieldrec.offset += fieldrec.size;
1282    }
1283
1284#if 0  /* obsolete -- keep for reference */
1285    if ( pflags )
1286      *pflags |= 1L << field->flag_bit;
1287#else
1288    FT_UNUSED( pflags );
1289#endif
1290
1291    parser->cursor = old_cursor;
1292    parser->limit  = old_limit;
1293
1294  Exit:
1295    return error;
1296  }
1297
1298
1299  FT_LOCAL_DEF( FT_Long )
1300  ps_parser_to_int( PS_Parser  parser )
1301  {
1302    ps_parser_skip_spaces( parser );
1303    return PS_Conv_ToInt( &parser->cursor, parser->limit );
1304  }
1305
1306
1307  /* first character must be `<' if `delimiters' is non-zero */
1308
1309  FT_LOCAL_DEF( FT_Error )
1310  ps_parser_to_bytes( PS_Parser  parser,
1311                      FT_Byte*   bytes,
1312                      FT_Long    max_bytes,
1313                      FT_Long*   pnum_bytes,
1314                      FT_Bool    delimiters )
1315  {
1316    FT_Error  error = PSaux_Err_Ok;
1317    FT_Byte*  cur;
1318
1319
1320    ps_parser_skip_spaces( parser );
1321    cur = parser->cursor;
1322
1323    if ( cur >= parser->limit )
1324      goto Exit;
1325
1326    if ( delimiters )
1327    {
1328      if ( *cur != '<' )
1329      {
1330        FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" ));
1331        error = PSaux_Err_Invalid_File_Format;
1332        goto Exit;
1333      }
1334
1335      cur++;
1336    }
1337
1338    *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur,
1339                                          parser->limit,
1340                                          bytes,
1341                                          max_bytes );
1342
1343    if ( delimiters )
1344    {
1345      if ( cur < parser->limit && *cur != '>' )
1346      {
1347        FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" ));
1348        error = PSaux_Err_Invalid_File_Format;
1349        goto Exit;
1350      }
1351
1352      cur++;
1353    }
1354
1355    parser->cursor = cur;
1356
1357  Exit:
1358    return error;
1359  }
1360
1361
1362  FT_LOCAL_DEF( FT_Fixed )
1363  ps_parser_to_fixed( PS_Parser  parser,
1364                      FT_Int     power_ten )
1365  {
1366    ps_parser_skip_spaces( parser );
1367    return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten );
1368  }
1369
1370
1371  FT_LOCAL_DEF( FT_Int )
1372  ps_parser_to_coord_array( PS_Parser  parser,
1373                            FT_Int     max_coords,
1374                            FT_Short*  coords )
1375  {
1376    ps_parser_skip_spaces( parser );
1377    return ps_tocoordarray( &parser->cursor, parser->limit,
1378                            max_coords, coords );
1379  }
1380
1381
1382  FT_LOCAL_DEF( FT_Int )
1383  ps_parser_to_fixed_array( PS_Parser  parser,
1384                            FT_Int     max_values,
1385                            FT_Fixed*  values,
1386                            FT_Int     power_ten )
1387  {
1388    ps_parser_skip_spaces( parser );
1389    return ps_tofixedarray( &parser->cursor, parser->limit,
1390                            max_values, values, power_ten );
1391  }
1392
1393
1394#if 0
1395
1396  FT_LOCAL_DEF( FT_String* )
1397  T1_ToString( PS_Parser  parser )
1398  {
1399    return ps_tostring( &parser->cursor, parser->limit, parser->memory );
1400  }
1401
1402
1403  FT_LOCAL_DEF( FT_Bool )
1404  T1_ToBool( PS_Parser  parser )
1405  {
1406    return ps_tobool( &parser->cursor, parser->limit );
1407  }
1408
1409#endif /* 0 */
1410
1411
1412  FT_LOCAL_DEF( void )
1413  ps_parser_init( PS_Parser  parser,
1414                  FT_Byte*   base,
1415                  FT_Byte*   limit,
1416                  FT_Memory  memory )
1417  {
1418    parser->error  = PSaux_Err_Ok;
1419    parser->base   = base;
1420    parser->limit  = limit;
1421    parser->cursor = base;
1422    parser->memory = memory;
1423    parser->funcs  = ps_parser_funcs;
1424  }
1425
1426
1427  FT_LOCAL_DEF( void )
1428  ps_parser_done( PS_Parser  parser )
1429  {
1430    FT_UNUSED( parser );
1431  }
1432
1433
1434  /*************************************************************************/
1435  /*************************************************************************/
1436  /*****                                                               *****/
1437  /*****                            T1 BUILDER                         *****/
1438  /*****                                                               *****/
1439  /*************************************************************************/
1440  /*************************************************************************/
1441
1442  /*************************************************************************/
1443  /*                                                                       */
1444  /* <Function>                                                            */
1445  /*    t1_builder_init                                                    */
1446  /*                                                                       */
1447  /* <Description>                                                         */
1448  /*    Initializes a given glyph builder.                                 */
1449  /*                                                                       */
1450  /* <InOut>                                                               */
1451  /*    builder :: A pointer to the glyph builder to initialize.           */
1452  /*                                                                       */
1453  /* <Input>                                                               */
1454  /*    face    :: The current face object.                                */
1455  /*                                                                       */
1456  /*    size    :: The current size object.                                */
1457  /*                                                                       */
1458  /*    glyph   :: The current glyph object.                               */
1459  /*                                                                       */
1460  /*    hinting :: Whether hinting should be applied.                      */
1461  /*                                                                       */
1462  FT_LOCAL_DEF( void )
1463  t1_builder_init( T1_Builder    builder,
1464                   FT_Face       face,
1465                   FT_Size       size,
1466                   FT_GlyphSlot  glyph,
1467                   FT_Bool       hinting )
1468  {
1469    builder->parse_state = T1_Parse_Start;
1470    builder->load_points = 1;
1471
1472    builder->face   = face;
1473    builder->glyph  = glyph;
1474    builder->memory = face->memory;
1475
1476    if ( glyph )
1477    {
1478      FT_GlyphLoader  loader = glyph->internal->loader;
1479
1480
1481      builder->loader  = loader;
1482      builder->base    = &loader->base.outline;
1483      builder->current = &loader->current.outline;
1484      FT_GlyphLoader_Rewind( loader );
1485
1486      builder->hints_globals = size->internal;
1487      builder->hints_funcs   = 0;
1488
1489      if ( hinting )
1490        builder->hints_funcs = glyph->internal->glyph_hints;
1491    }
1492
1493    builder->pos_x = 0;
1494    builder->pos_y = 0;
1495
1496    builder->left_bearing.x = 0;
1497    builder->left_bearing.y = 0;
1498    builder->advance.x      = 0;
1499    builder->advance.y      = 0;
1500
1501    builder->funcs = t1_builder_funcs;
1502  }
1503
1504
1505  /*************************************************************************/
1506  /*                                                                       */
1507  /* <Function>                                                            */
1508  /*    t1_builder_done                                                    */
1509  /*                                                                       */
1510  /* <Description>                                                         */
1511  /*    Finalizes a given glyph builder.  Its contents can still be used   */
1512  /*    after the call, but the function saves important information       */
1513  /*    within the corresponding glyph slot.                               */
1514  /*                                                                       */
1515  /* <Input>                                                               */
1516  /*    builder :: A pointer to the glyph builder to finalize.             */
1517  /*                                                                       */
1518  FT_LOCAL_DEF( void )
1519  t1_builder_done( T1_Builder  builder )
1520  {
1521    FT_GlyphSlot  glyph = builder->glyph;
1522
1523
1524    if ( glyph )
1525      glyph->outline = *builder->base;
1526  }
1527
1528
1529  /* check that there is enough space for `count' more points */
1530  FT_LOCAL_DEF( FT_Error )
1531  t1_builder_check_points( T1_Builder  builder,
1532                           FT_Int      count )
1533  {
1534    return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
1535  }
1536
1537
1538  /* add a new point, do not check space */
1539  FT_LOCAL_DEF( void )
1540  t1_builder_add_point( T1_Builder  builder,
1541                        FT_Pos      x,
1542                        FT_Pos      y,
1543                        FT_Byte     flag )
1544  {
1545    FT_Outline*  outline = builder->current;
1546
1547
1548    if ( builder->load_points )
1549    {
1550      FT_Vector*  point   = outline->points + outline->n_points;
1551      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points;
1552
1553
1554      if ( builder->shift )
1555      {
1556        x >>= 16;
1557        y >>= 16;
1558      }
1559      point->x = x;
1560      point->y = y;
1561      *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
1562
1563      builder->last = *point;
1564    }
1565    outline->n_points++;
1566  }
1567
1568
1569  /* check space for a new on-curve point, then add it */
1570  FT_LOCAL_DEF( FT_Error )
1571  t1_builder_add_point1( T1_Builder  builder,
1572                         FT_Pos      x,
1573                         FT_Pos      y )
1574  {
1575    FT_Error  error;
1576
1577
1578    error = t1_builder_check_points( builder, 1 );
1579    if ( !error )
1580      t1_builder_add_point( builder, x, y, 1 );
1581
1582    return error;
1583  }
1584
1585
1586  /* check space for a new contour, then add it */
1587  FT_LOCAL_DEF( FT_Error )
1588  t1_builder_add_contour( T1_Builder  builder )
1589  {
1590    FT_Outline*  outline = builder->current;
1591    FT_Error     error;
1592
1593
1594    if ( !builder->load_points )
1595    {
1596      outline->n_contours++;
1597      return PSaux_Err_Ok;
1598    }
1599
1600    error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
1601    if ( !error )
1602    {
1603      if ( outline->n_contours > 0 )
1604        outline->contours[outline->n_contours - 1] =
1605          (short)( outline->n_points - 1 );
1606
1607      outline->n_contours++;
1608    }
1609
1610    return error;
1611  }
1612
1613
1614  /* if a path was begun, add its first on-curve point */
1615  FT_LOCAL_DEF( FT_Error )
1616  t1_builder_start_point( T1_Builder  builder,
1617                          FT_Pos      x,
1618                          FT_Pos      y )
1619  {
1620    FT_Error  error = PSaux_Err_Invalid_File_Format;
1621
1622
1623    /* test whether we are building a new contour */
1624
1625    if ( builder->parse_state == T1_Parse_Have_Path )
1626      error = PSaux_Err_Ok;
1627    else if ( builder->parse_state == T1_Parse_Have_Moveto )
1628    {
1629      builder->parse_state = T1_Parse_Have_Path;
1630      error = t1_builder_add_contour( builder );
1631      if ( !error )
1632        error = t1_builder_add_point1( builder, x, y );
1633    }
1634
1635    return error;
1636  }
1637
1638
1639  /* close the current contour */
1640  FT_LOCAL_DEF( void )
1641  t1_builder_close_contour( T1_Builder  builder )
1642  {
1643    FT_Outline*  outline = builder->current;
1644    FT_Int       first;
1645
1646
1647    if ( !outline )
1648      return;
1649
1650    first = outline->n_contours <= 1
1651            ? 0 : outline->contours[outline->n_contours - 2] + 1;
1652
1653    /* We must not include the last point in the path if it */
1654    /* is located on the first point.                       */
1655    if ( outline->n_points > 1 )
1656    {
1657      FT_Vector*  p1      = outline->points + first;
1658      FT_Vector*  p2      = outline->points + outline->n_points - 1;
1659      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points - 1;
1660
1661
1662      /* `delete' last point only if it coincides with the first */
1663      /* point and it is not a control point (which can happen). */
1664      if ( p1->x == p2->x && p1->y == p2->y )
1665        if ( *control == FT_CURVE_TAG_ON )
1666          outline->n_points--;
1667    }
1668
1669    if ( outline->n_contours > 0 )
1670    {
1671      /* Don't add contours only consisting of one point, i.e., */
1672      /* check whether begin point and last point are the same. */
1673      if ( first == outline->n_points - 1 )
1674      {
1675        outline->n_contours--;
1676        outline->n_points--;
1677      }
1678      else
1679        outline->contours[outline->n_contours - 1] =
1680          (short)( outline->n_points - 1 );
1681    }
1682  }
1683
1684
1685  /*************************************************************************/
1686  /*************************************************************************/
1687  /*****                                                               *****/
1688  /*****                            OTHER                              *****/
1689  /*****                                                               *****/
1690  /*************************************************************************/
1691  /*************************************************************************/
1692
1693  FT_LOCAL_DEF( void )
1694  t1_decrypt( FT_Byte*   buffer,
1695              FT_Offset  length,
1696              FT_UShort  seed )
1697  {
1698    PS_Conv_EexecDecode( &buffer,
1699                         buffer + length,
1700                         buffer,
1701                         length,
1702                         &seed );
1703  }
1704
1705
1706/* END */
Note: See TracBrowser for help on using the repository browser.