Edit

kc3-lang/freetype/src/psaux/psobjs.c

Branch :

  • Show log

    Commit

  • Author : suzuki toshiya
    Date : 2009-08-01 00:30:17
    Hash : d200982d
    Message : psaux: Use size_t variable to pass the buffer size.

  • src/psaux/psobjs.c
  • /***************************************************************************/
    /*                                                                         */
    /*  psobjs.c                                                               */
    /*                                                                         */
    /*    Auxiliary functions for PostScript fonts (body).                     */
    /*                                                                         */
    /*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by */
    /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
    /*                                                                         */
    /*  This file is part of the FreeType project, and may only be used,       */
    /*  modified, and distributed under the terms of the FreeType project      */
    /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
    /*  this file you indicate that you have read the license and              */
    /*  understand and accept it fully.                                        */
    /*                                                                         */
    /***************************************************************************/
    
    
    #include <ft2build.h>
    #include FT_INTERNAL_POSTSCRIPT_AUX_H
    #include FT_INTERNAL_DEBUG_H
    #include FT_INTERNAL_CALC_H
    
    #include "psobjs.h"
    #include "psconv.h"
    
    #include "psauxerr.h"
    
    
      /*************************************************************************/
      /*                                                                       */
      /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
      /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
      /* messages during execution.                                            */
      /*                                                                       */
    #undef  FT_COMPONENT
    #define FT_COMPONENT  trace_psobjs
    
    
      /*************************************************************************/
      /*************************************************************************/
      /*****                                                               *****/
      /*****                             PS_TABLE                          *****/
      /*****                                                               *****/
      /*************************************************************************/
      /*************************************************************************/
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    ps_table_new                                                       */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Initializes a PS_Table.                                            */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    table  :: The address of the target table.                         */
      /*                                                                       */
      /* <Input>                                                               */
      /*    count  :: The table size = the maximum number of elements.         */
      /*                                                                       */
      /*    memory :: The memory object to use for all subsequent              */
      /*              reallocations.                                           */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.                             */
      /*                                                                       */
      FT_LOCAL_DEF( FT_Error )
      ps_table_new( PS_Table   table,
                    FT_Int     count,
                    FT_Memory  memory )
      {
        FT_Error  error;
    
    
        table->memory = memory;
        if ( FT_NEW_ARRAY( table->elements, count ) ||
             FT_NEW_ARRAY( table->lengths,  count ) )
          goto Exit;
    
        table->max_elems = count;
        table->init      = 0xDEADBEEFUL;
        table->num_elems = 0;
        table->block     = 0;
        table->capacity  = 0;
        table->cursor    = 0;
    
        *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs;
    
      Exit:
        if ( error )
          FT_FREE( table->elements );
    
        return error;
      }
    
    
      static void
      shift_elements( PS_Table  table,
                      FT_Byte*  old_base )
      {
        FT_PtrDist  delta  = table->block - old_base;
        FT_Byte**   offset = table->elements;
        FT_Byte**   limit  = offset + table->max_elems;
    
    
        for ( ; offset < limit; offset++ )
        {
          if ( offset[0] )
            offset[0] += delta;
        }
      }
    
    
      static FT_Error
      reallocate_t1_table( PS_Table  table,
                           FT_Long   new_size )
      {
        FT_Memory  memory   = table->memory;
        FT_Byte*   old_base = table->block;
        FT_Error   error;
    
    
        /* allocate new base block */
        if ( FT_ALLOC( table->block, new_size ) )
        {
          table->block = old_base;
          return error;
        }
    
        /* copy elements and shift offsets */
        if ( old_base )
        {
          FT_MEM_COPY( table->block, old_base, table->capacity );
          shift_elements( table, old_base );
          FT_FREE( old_base );
        }
    
        table->capacity = new_size;
    
        return PSaux_Err_Ok;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    ps_table_add                                                       */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Adds an object to a PS_Table, possibly growing its memory block.   */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    table  :: The target table.                                        */
      /*                                                                       */
      /* <Input>                                                               */
      /*    idx    :: The index of the object in the table.                    */
      /*                                                                       */
      /*    object :: The address of the object to copy in memory.             */
      /*                                                                       */
      /*    length :: The length in bytes of the source object.                */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.  An error is returned if a  */
      /*    reallocation fails.                                                */
      /*                                                                       */
      FT_LOCAL_DEF( FT_Error )
      ps_table_add( PS_Table    table,
                    FT_Int      idx,
                    void*       object,
                    FT_PtrDist  length )
      {
        if ( idx < 0 || idx >= table->max_elems )
        {
          FT_ERROR(( "ps_table_add: invalid index\n" ));
          return PSaux_Err_Invalid_Argument;
        }
    
        if ( length < 0 )
        {
          FT_ERROR(( "ps_table_add: invalid length\n" ));
          return PSaux_Err_Invalid_Argument;
        }
    
        /* grow the base block if needed */
        if ( table->cursor + length > table->capacity )
        {
          FT_Error   error;
          FT_Offset  new_size = table->capacity;
          FT_Long    in_offset;
    
    
          in_offset = (FT_Long)((FT_Byte*)object - table->block);
          if ( (FT_ULong)in_offset >= table->capacity )
            in_offset = -1;
    
          while ( new_size < table->cursor + length )
          {
            /* increase size by 25% and round up to the nearest multiple
               of 1024 */
            new_size += ( new_size >> 2 ) + 1;
            new_size  = FT_PAD_CEIL( new_size, 1024 );
          }
    
          error = reallocate_t1_table( table, new_size );
          if ( error )
            return error;
    
          if ( in_offset >= 0 )
            object = table->block + in_offset;
        }
    
        /* add the object to the base block and adjust offset */
        table->elements[idx] = table->block + table->cursor;
        table->lengths [idx] = length;
        FT_MEM_COPY( table->block + table->cursor, object, length );
    
        table->cursor += length;
        return PSaux_Err_Ok;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    ps_table_done                                                      */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Finalizes a PS_TableRec (i.e., reallocate it to its current        */
      /*    cursor).                                                           */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    table :: The target table.                                         */
      /*                                                                       */
      /* <Note>                                                                */
      /*    This function does NOT release the heap's memory block.  It is up  */
      /*    to the caller to clean it, or reference it in its own structures.  */
      /*                                                                       */
      FT_LOCAL_DEF( void )
      ps_table_done( PS_Table  table )
      {
        FT_Memory  memory = table->memory;
        FT_Error   error;
        FT_Byte*   old_base = table->block;
    
    
        /* should never fail, because rec.cursor <= rec.size */
        if ( !old_base )
          return;
    
        if ( FT_ALLOC( table->block, table->cursor ) )
          return;
        FT_MEM_COPY( table->block, old_base, table->cursor );
        shift_elements( table, old_base );
    
        table->capacity = table->cursor;
        FT_FREE( old_base );
    
        FT_UNUSED( error );
      }
    
    
      FT_LOCAL_DEF( void )
      ps_table_release( PS_Table  table )
      {
        FT_Memory  memory = table->memory;
    
    
        if ( (FT_ULong)table->init == 0xDEADBEEFUL )
        {
          FT_FREE( table->block );
          FT_FREE( table->elements );
          FT_FREE( table->lengths );
          table->init = 0;
        }
      }
    
    
      /*************************************************************************/
      /*************************************************************************/
      /*****                                                               *****/
      /*****                            T1 PARSER                          *****/
      /*****                                                               *****/
      /*************************************************************************/
      /*************************************************************************/
    
    
      /* first character must be already part of the comment */
    
      static void
      skip_comment( FT_Byte*  *acur,
                    FT_Byte*   limit )
      {
        FT_Byte*  cur = *acur;
    
    
        while ( cur < limit )
        {
          if ( IS_PS_NEWLINE( *cur ) )
            break;
          cur++;
        }
    
        *acur = cur;
      }
    
    
      static void
      skip_spaces( FT_Byte*  *acur,
                   FT_Byte*   limit )
      {
        FT_Byte*  cur = *acur;
    
    
        while ( cur < limit )
        {
          if ( !IS_PS_SPACE( *cur ) )
          {
            if ( *cur == '%' )
              /* According to the PLRM, a comment is equal to a space. */
              skip_comment( &cur, limit );
            else
              break;
          }
          cur++;
        }
    
        *acur = cur;
      }
    
    
    #define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' )
    
    
      /* first character must be `(';                               */
      /* *acur is positioned at the character after the closing `)' */
    
      static FT_Error
      skip_literal_string( FT_Byte*  *acur,
                           FT_Byte*   limit )
      {
        FT_Byte*      cur   = *acur;
        FT_Int        embed = 0;
        FT_Error      error = PSaux_Err_Invalid_File_Format;
        unsigned int  i;
    
    
        while ( cur < limit )
        {
          FT_Byte  c = *cur;
    
    
          ++cur;
    
          if ( c == '\\' )
          {
            /* Red Book 3rd ed., section `Literal Text Strings', p. 29:     */
            /* A backslash can introduce three different types              */
            /* of escape sequences:                                         */
            /*   - a special escaped char like \r, \n, etc.                 */
            /*   - a one-, two-, or three-digit octal number                */
            /*   - none of the above in which case the backslash is ignored */
    
            if ( cur == limit )
              /* error (or to be ignored?) */
              break;
    
            switch ( *cur )
            {
              /* skip `special' escape */
            case 'n':
            case 'r':
            case 't':
            case 'b':
            case 'f':
            case '\\':
            case '(':
            case ')':
              ++cur;
              break;
    
            default:
              /* skip octal escape or ignore backslash */
              for ( i = 0; i < 3 && cur < limit; ++i )
              {
                if ( !IS_OCTAL_DIGIT( *cur ) )
                  break;
    
                ++cur;
              }
            }
          }
          else if ( c == '(' )
            embed++;
          else if ( c == ')' )
          {
            embed--;
            if ( embed == 0 )
            {
              error = PSaux_Err_Ok;
              break;
            }
          }
        }
    
        *acur = cur;
    
        return error;
      }
    
    
      /* first character must be `<' */
    
      static FT_Error
      skip_string( FT_Byte*  *acur,
                   FT_Byte*   limit )
      {
        FT_Byte*  cur = *acur;
        FT_Error  err =  PSaux_Err_Ok;
    
    
        while ( ++cur < limit )
        {
          /* All whitespace characters are ignored. */
          skip_spaces( &cur, limit );
          if ( cur >= limit )
            break;
    
          if ( !IS_PS_XDIGIT( *cur ) )
            break;
        }
    
        if ( cur < limit && *cur != '>' )
        {
          FT_ERROR(( "skip_string: missing closing delimiter `>'\n" ));
          err = PSaux_Err_Invalid_File_Format;
        }
        else
          cur++;
    
        *acur = cur;
        return err;
      }
    
    
      /* first character must be the opening brace that */
      /* starts the procedure                           */
    
      /* NB: [ and ] need not match:                    */
      /* `/foo {[} def' is a valid PostScript fragment, */
      /* even within a Type1 font                       */
    
      static FT_Error
      skip_procedure( FT_Byte*  *acur,
                      FT_Byte*   limit )
      {
        FT_Byte*  cur;
        FT_Int    embed = 0;
        FT_Error  error = PSaux_Err_Ok;
    
    
        FT_ASSERT( **acur == '{' );
    
        for ( cur = *acur; cur < limit && error == PSaux_Err_Ok; ++cur )
        {
          switch ( *cur )
          {
          case '{':
            ++embed;
            break;
    
          case '}':
            --embed;
            if ( embed == 0 )
            {
              ++cur;
              goto end;
            }
            break;
    
          case '(':
            error = skip_literal_string( &cur, limit );
            break;
    
          case '<':
            error = skip_string( &cur, limit );
            break;
    
          case '%':
            skip_comment( &cur, limit );
            break;
          }
        }
    
      end:
        if ( embed != 0 )
          error = PSaux_Err_Invalid_File_Format;
    
        *acur = cur;
    
        return error;
      }
    
    
      /***********************************************************************/
      /*                                                                     */
      /* All exported parsing routines handle leading whitespace and stop at */
      /* the first character which isn't part of the just handled token.     */
      /*                                                                     */
      /***********************************************************************/
    
    
      FT_LOCAL_DEF( void )
      ps_parser_skip_PS_token( PS_Parser  parser )
      {
        /* Note: PostScript allows any non-delimiting, non-whitespace        */
        /*       character in a name (PS Ref Manual, 3rd ed, p31).           */
        /*       PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */
    
        FT_Byte*  cur   = parser->cursor;
        FT_Byte*  limit = parser->limit;
        FT_Error  error = PSaux_Err_Ok;
    
    
        skip_spaces( &cur, limit );             /* this also skips comments */
        if ( cur >= limit )
          goto Exit;
    
        /* self-delimiting, single-character tokens */
        if ( *cur == '[' || *cur == ']' )
        {
          cur++;
          goto Exit;
        }
    
        /* skip balanced expressions (procedures and strings) */
    
        if ( *cur == '{' )                              /* {...} */
        {
          error = skip_procedure( &cur, limit );
          goto Exit;
        }
    
        if ( *cur == '(' )                              /* (...) */
        {
          error = skip_literal_string( &cur, limit );
          goto Exit;
        }
    
        if ( *cur == '<' )                              /* <...> */
        {
          if ( cur + 1 < limit && *(cur + 1) == '<' )   /* << */
          {
            cur++;
            cur++;
          }
          else
            error = skip_string( &cur, limit );
    
          goto Exit;
        }
    
        if ( *cur == '>' )
        {
          cur++;
          if ( cur >= limit || *cur != '>' )             /* >> */
          {
            FT_ERROR(( "ps_parser_skip_PS_token:"
                       " unexpected closing delimiter `>'\n" ));
            error = PSaux_Err_Invalid_File_Format;
            goto Exit;
          }
          cur++;
          goto Exit;
        }
    
        if ( *cur == '/' )
          cur++;
    
        /* anything else */
        while ( cur < limit )
        {
          /* *cur might be invalid (e.g., ')' or '}'), but this   */
          /* is handled by the test `cur == parser->cursor' below */
          if ( IS_PS_DELIM( *cur ) )
            break;
    
          cur++;
        }
    
      Exit:
        if ( cur == parser->cursor )
        {
          FT_ERROR(( "ps_parser_skip_PS_token:"
                     " current token is `%c' which is self-delimiting\n"
                     "                        "
                     " but invalid at this point\n",
                     *cur ));
    
          error = PSaux_Err_Invalid_File_Format;
        }
    
        parser->error  = error;
        parser->cursor = cur;
      }
    
    
      FT_LOCAL_DEF( void )
      ps_parser_skip_spaces( PS_Parser  parser )
      {
        skip_spaces( &parser->cursor, parser->limit );
      }
    
    
      /* `token' here means either something between balanced delimiters */
      /* or the next token; the delimiters are not removed.              */
    
      FT_LOCAL_DEF( void )
      ps_parser_to_token( PS_Parser  parser,
                          T1_Token   token )
      {
        FT_Byte*  cur;
        FT_Byte*  limit;
        FT_Int    embed;
    
    
        token->type  = T1_TOKEN_TYPE_NONE;
        token->start = 0;
        token->limit = 0;
    
        /* first of all, skip leading whitespace */
        ps_parser_skip_spaces( parser );
    
        cur   = parser->cursor;
        limit = parser->limit;
    
        if ( cur >= limit )
          return;
    
        switch ( *cur )
        {
          /************* check for literal string *****************/
        case '(':
          token->type  = T1_TOKEN_TYPE_STRING;
          token->start = cur;
    
          if ( skip_literal_string( &cur, limit ) == PSaux_Err_Ok )
            token->limit = cur;
          break;
    
          /************* check for programs/array *****************/
        case '{':
          token->type  = T1_TOKEN_TYPE_ARRAY;
          token->start = cur;
    
          if ( skip_procedure( &cur, limit ) == PSaux_Err_Ok )
            token->limit = cur;
          break;
    
          /************* check for table/array ********************/
          /* XXX: in theory we should also look for "<<"          */
          /*      since this is semantically equivalent to "[";   */
          /*      in practice it doesn't matter (?)               */
        case '[':
          token->type  = T1_TOKEN_TYPE_ARRAY;
          embed        = 1;
          token->start = cur++;
    
          /* we need this to catch `[ ]' */
          parser->cursor = cur;
          ps_parser_skip_spaces( parser );
          cur = parser->cursor;
    
          while ( cur < limit && !parser->error )
          {
            /* XXX: this is wrong because it does not      */
            /*      skip comments, procedures, and strings */
            if ( *cur == '[' )
              embed++;
            else if ( *cur == ']' )
            {
              embed--;
              if ( embed <= 0 )
              {
                token->limit = ++cur;
                break;
              }
            }
    
            parser->cursor = cur;
            ps_parser_skip_PS_token( parser );
            /* we need this to catch `[XXX ]' */
            ps_parser_skip_spaces  ( parser );
            cur = parser->cursor;
          }
          break;
    
          /* ************ otherwise, it is any token **************/
        default:
          token->start = cur;
          token->type  = ( *cur == '/' ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY );
          ps_parser_skip_PS_token( parser );
          cur = parser->cursor;
          if ( !parser->error )
            token->limit = cur;
        }
    
        if ( !token->limit )
        {
          token->start = 0;
          token->type  = T1_TOKEN_TYPE_NONE;
        }
    
        parser->cursor = cur;
      }
    
    
      /* NB: `tokens' can be NULL if we only want to count */
      /* the number of array elements                      */
    
      FT_LOCAL_DEF( void )
      ps_parser_to_token_array( PS_Parser  parser,
                                T1_Token   tokens,
                                FT_UInt    max_tokens,
                                FT_Int*    pnum_tokens )
      {
        T1_TokenRec  master;
    
    
        *pnum_tokens = -1;
    
        /* this also handles leading whitespace */
        ps_parser_to_token( parser, &master );
    
        if ( master.type == T1_TOKEN_TYPE_ARRAY )
        {
          FT_Byte*  old_cursor = parser->cursor;
          FT_Byte*  old_limit  = parser->limit;
          T1_Token  cur        = tokens;
          T1_Token  limit      = cur + max_tokens;
    
    
          /* don't include outermost delimiters */
          parser->cursor = master.start + 1;
          parser->limit  = master.limit - 1;
    
          while ( parser->cursor < parser->limit )
          {
            T1_TokenRec  token;
    
    
            ps_parser_to_token( parser, &token );
            if ( !token.type )
              break;
    
            if ( tokens != NULL && cur < limit )
              *cur = token;
    
            cur++;
          }
    
          *pnum_tokens = (FT_Int)( cur - tokens );
    
          parser->cursor = old_cursor;
          parser->limit  = old_limit;
        }
      }
    
    
      /* first character must be a delimiter or a part of a number */
      /* NB: `coords' can be NULL if we just want to skip the      */
      /*     array; in this case we ignore `max_coords'            */
    
      static FT_Int
      ps_tocoordarray( FT_Byte*  *acur,
                       FT_Byte*   limit,
                       FT_Int     max_coords,
                       FT_Short*  coords )
      {
        FT_Byte*  cur   = *acur;
        FT_Int    count = 0;
        FT_Byte   c, ender;
    
    
        if ( cur >= limit )
          goto Exit;
    
        /* check for the beginning of an array; otherwise, only one number */
        /* will be read                                                    */
        c     = *cur;
        ender = 0;
    
        if ( c == '[' )
          ender = ']';
        else if ( c == '{' )
          ender = '}';
    
        if ( ender )
          cur++;
    
        /* now, read the coordinates */
        while ( cur < limit )
        {
          FT_Short  dummy;
          FT_Byte*  old_cur;
    
    
          /* skip whitespace in front of data */
          skip_spaces( &cur, limit );
          if ( cur >= limit )
            goto Exit;
    
          if ( *cur == ender )
          {
            cur++;
            break;
          }
    
          old_cur = cur;
    
          if ( coords != NULL && count >= max_coords )
            break;
    
          /* call PS_Conv_ToFixed() even if coords == NULL */
          /* to properly parse number at `cur'             */
          *( coords != NULL ? &coords[count] : &dummy ) =
            (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 );
    
          if ( old_cur == cur )
          {
            count = -1;
            goto Exit;
          }
          else
            count++;
    
          if ( !ender )
            break;
        }
    
      Exit:
        *acur = cur;
        return count;
      }
    
    
      /* first character must be a delimiter or a part of a number */
      /* NB: `values' can be NULL if we just want to skip the      */
      /*     array; in this case we ignore `max_values'            */
    
      static FT_Int
      ps_tofixedarray( FT_Byte*  *acur,
                       FT_Byte*   limit,
                       FT_Int     max_values,
                       FT_Fixed*  values,
                       FT_Int     power_ten )
      {
        FT_Byte*  cur   = *acur;
        FT_Int    count = 0;
        FT_Byte   c, ender;
    
    
        if ( cur >= limit )
          goto Exit;
    
        /* Check for the beginning of an array.  Otherwise, only one number */
        /* will be read.                                                    */
        c     = *cur;
        ender = 0;
    
        if ( c == '[' )
          ender = ']';
        else if ( c == '{' )
          ender = '}';
    
        if ( ender )
          cur++;
    
        /* now, read the values */
        while ( cur < limit )
        {
          FT_Fixed  dummy;
          FT_Byte*  old_cur;
    
    
          /* skip whitespace in front of data */
          skip_spaces( &cur, limit );
          if ( cur >= limit )
            goto Exit;
    
          if ( *cur == ender )
          {
            cur++;
            break;
          }
    
          old_cur = cur;
    
          if ( values != NULL && count >= max_values )
            break;
    
          /* call PS_Conv_ToFixed() even if coords == NULL */
          /* to properly parse number at `cur'             */
          *( values != NULL ? &values[count] : &dummy ) =
            PS_Conv_ToFixed( &cur, limit, power_ten );
    
          if ( old_cur == cur )
          {
            count = -1;
            goto Exit;
          }
          else
            count++;
    
          if ( !ender )
            break;
        }
    
      Exit:
        *acur = cur;
        return count;
      }
    
    
    #if 0
    
      static FT_String*
      ps_tostring( FT_Byte**  cursor,
                   FT_Byte*   limit,
                   FT_Memory  memory )
      {
        FT_Byte*    cur = *cursor;
        FT_PtrDist  len = 0;
        FT_Int      count;
        FT_String*  result;
        FT_Error    error;
    
    
        /* XXX: some stupid fonts have a `Notice' or `Copyright' string     */
        /*      that simply doesn't begin with an opening parenthesis, even */
        /*      though they have a closing one!  E.g. "amuncial.pfb"        */
        /*                                                                  */
        /*      We must deal with these ill-fated cases there.  Note that   */
        /*      these fonts didn't work with the old Type 1 driver as the   */
        /*      notice/copyright was not recognized as a valid string token */
        /*      and made the old token parser commit errors.                */
    
        while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) )
          cur++;
        if ( cur + 1 >= limit )
          return 0;
    
        if ( *cur == '(' )
          cur++;  /* skip the opening parenthesis, if there is one */
    
        *cursor = cur;
        count   = 0;
    
        /* then, count its length */
        for ( ; cur < limit; cur++ )
        {
          if ( *cur == '(' )
            count++;
    
          else if ( *cur == ')' )
          {
            count--;
            if ( count < 0 )
              break;
          }
        }
    
        len = cur - *cursor;
        if ( cur >= limit || FT_ALLOC( result, len + 1 ) )
          return 0;
    
        /* now copy the string */
        FT_MEM_COPY( result, *cursor, len );
        result[len] = '\0';
        *cursor = cur;
        return result;
      }
    
    #endif /* 0 */
    
    
      static int
      ps_tobool( FT_Byte*  *acur,
                 FT_Byte*   limit )
      {
        FT_Byte*  cur    = *acur;
        FT_Bool   result = 0;
    
    
        /* return 1 if we find `true', 0 otherwise */
        if ( cur + 3 < limit &&
             cur[0] == 't'   &&
             cur[1] == 'r'   &&
             cur[2] == 'u'   &&
             cur[3] == 'e'   )
        {
          result = 1;
          cur   += 5;
        }
        else if ( cur + 4 < limit &&
                  cur[0] == 'f'   &&
                  cur[1] == 'a'   &&
                  cur[2] == 'l'   &&
                  cur[3] == 's'   &&
                  cur[4] == 'e'   )
        {
          result = 0;
          cur   += 6;
        }
    
        *acur = cur;
        return result;
      }
    
    
      /* load a simple field (i.e. non-table) into the current list of objects */
    
      FT_LOCAL_DEF( FT_Error )
      ps_parser_load_field( PS_Parser       parser,
                            const T1_Field  field,
                            void**          objects,
                            FT_UInt         max_objects,
                            FT_ULong*       pflags )
      {
        T1_TokenRec  token;
        FT_Byte*     cur;
        FT_Byte*     limit;
        FT_UInt      count;
        FT_UInt      idx;
        FT_Error     error;
    
    
        /* this also skips leading whitespace */
        ps_parser_to_token( parser, &token );
        if ( !token.type )
          goto Fail;
    
        count = 1;
        idx   = 0;
        cur   = token.start;
        limit = token.limit;
    
        /* we must detect arrays in /FontBBox */
        if ( field->type == T1_FIELD_TYPE_BBOX )
        {
          T1_TokenRec  token2;
          FT_Byte*     old_cur   = parser->cursor;
          FT_Byte*     old_limit = parser->limit;
    
    
          /* don't include delimiters */
          parser->cursor = token.start + 1;
          parser->limit  = token.limit - 1;
    
          ps_parser_to_token( parser, &token2 );
          parser->cursor = old_cur;
          parser->limit  = old_limit;
    
          if ( token2.type == T1_TOKEN_TYPE_ARRAY )
            goto FieldArray;
        }
        else if ( token.type == T1_TOKEN_TYPE_ARRAY )
        {
        FieldArray:
          /* if this is an array and we have no blend, an error occurs */
          if ( max_objects == 0 )
            goto Fail;
    
          count = max_objects;
          idx   = 1;
    
          /* don't include delimiters */
          cur++;
          limit--;
        }
    
        for ( ; count > 0; count--, idx++ )
        {
          FT_Byte*    q = (FT_Byte*)objects[idx] + field->offset;
          FT_Long     val;
          FT_String*  string;
    
    
          skip_spaces( &cur, limit );
    
          switch ( field->type )
          {
          case T1_FIELD_TYPE_BOOL:
            val = ps_tobool( &cur, limit );
            goto Store_Integer;
    
          case T1_FIELD_TYPE_FIXED:
            val = PS_Conv_ToFixed( &cur, limit, 0 );
            goto Store_Integer;
    
          case T1_FIELD_TYPE_FIXED_1000:
            val = PS_Conv_ToFixed( &cur, limit, 3 );
            goto Store_Integer;
    
          case T1_FIELD_TYPE_INTEGER:
            val = PS_Conv_ToInt( &cur, limit );
            /* fall through */
    
          Store_Integer:
            switch ( field->size )
            {
            case (8 / FT_CHAR_BIT):
              *(FT_Byte*)q = (FT_Byte)val;
              break;
    
            case (16 / FT_CHAR_BIT):
              *(FT_UShort*)q = (FT_UShort)val;
              break;
    
            case (32 / FT_CHAR_BIT):
              *(FT_UInt32*)q = (FT_UInt32)val;
              break;
    
            default:                /* for 64-bit systems */
              *(FT_Long*)q = val;
            }
            break;
    
          case T1_FIELD_TYPE_STRING:
          case T1_FIELD_TYPE_KEY:
            {
              FT_Memory  memory = parser->memory;
              FT_UInt    len    = (FT_UInt)( limit - cur );
    
    
              if ( cur >= limit )
                break;
    
              /* we allow both a string or a name   */
              /* for cases like /FontName (foo) def */
              if ( token.type == T1_TOKEN_TYPE_KEY )
              {
                /* don't include leading `/' */
                len--;
                cur++;
              }
              else if ( token.type == T1_TOKEN_TYPE_STRING )
              {
                /* don't include delimiting parentheses    */
                /* XXX we don't handle <<...>> here        */
                /* XXX should we convert octal escapes?    */
                /*     if so, what encoding should we use? */
                cur++;
                len -= 2;
              }
              else
              {
                FT_ERROR(( "ps_parser_load_field:"
                           " expected a name or string\n"
                           "                     "
                           " but found token of type %d instead\n",
                           token.type ));
                error = PSaux_Err_Invalid_File_Format;
                goto Exit;
              }
    
              /* for this to work (FT_String**)q must have been */
              /* initialized to NULL                            */
              if ( *(FT_String**)q != NULL )
              {
                FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n",
                            field->ident ));
                FT_FREE( *(FT_String**)q );
                *(FT_String**)q = NULL;
              }
    
              if ( FT_ALLOC( string, len + 1 ) )
                goto Exit;
    
              FT_MEM_COPY( string, cur, len );
              string[len] = 0;
    
              *(FT_String**)q = string;
            }
            break;
    
          case T1_FIELD_TYPE_BBOX:
            {
              FT_Fixed  temp[4];
              FT_BBox*  bbox = (FT_BBox*)q;
              FT_Int    result;
    
    
              result = ps_tofixedarray( &cur, limit, 4, temp, 0 );
    
              if ( result < 0 )
              {
                FT_ERROR(( "ps_parser_load_field:"
                           " expected four integers in bounding box\n" ));
                error = PSaux_Err_Invalid_File_Format;
                goto Exit;
              }
    
              bbox->xMin = FT_RoundFix( temp[0] );
              bbox->yMin = FT_RoundFix( temp[1] );
              bbox->xMax = FT_RoundFix( temp[2] );
              bbox->yMax = FT_RoundFix( temp[3] );
            }
            break;
    
          default:
            /* an error occurred */
            goto Fail;
          }
        }
    
    #if 0  /* obsolete -- keep for reference */
        if ( pflags )
          *pflags |= 1L << field->flag_bit;
    #else
        FT_UNUSED( pflags );
    #endif
    
        error = PSaux_Err_Ok;
    
      Exit:
        return error;
    
      Fail:
        error = PSaux_Err_Invalid_File_Format;
        goto Exit;
      }
    
    
    #define T1_MAX_TABLE_ELEMENTS  32
    
    
      FT_LOCAL_DEF( FT_Error )
      ps_parser_load_field_table( PS_Parser       parser,
                                  const T1_Field  field,
                                  void**          objects,
                                  FT_UInt         max_objects,
                                  FT_ULong*       pflags )
      {
        T1_TokenRec  elements[T1_MAX_TABLE_ELEMENTS];
        T1_Token     token;
        FT_Int       num_elements;
        FT_Error     error = PSaux_Err_Ok;
        FT_Byte*     old_cursor;
        FT_Byte*     old_limit;
        T1_FieldRec  fieldrec = *(T1_Field)field;
    
    
        fieldrec.type = T1_FIELD_TYPE_INTEGER;
        if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY ||
             field->type == T1_FIELD_TYPE_BBOX        )
          fieldrec.type = T1_FIELD_TYPE_FIXED;
    
        ps_parser_to_token_array( parser, elements,
                                  T1_MAX_TABLE_ELEMENTS, &num_elements );
        if ( num_elements < 0 )
        {
          error = PSaux_Err_Ignore;
          goto Exit;
        }
        if ( (FT_UInt)num_elements > field->array_max )
          num_elements = field->array_max;
    
        old_cursor = parser->cursor;
        old_limit  = parser->limit;
    
        /* we store the elements count if necessary;           */
        /* we further assume that `count_offset' can't be zero */
        if ( field->type != T1_FIELD_TYPE_BBOX && field->count_offset != 0 )
          *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) =
            (FT_Byte)num_elements;
    
        /* we now load each element, adjusting the field.offset on each one */
        token = elements;
        for ( ; num_elements > 0; num_elements--, token++ )
        {
          parser->cursor = token->start;
          parser->limit  = token->limit;
          ps_parser_load_field( parser, &fieldrec, objects, max_objects, 0 );
          fieldrec.offset += fieldrec.size;
        }
    
    #if 0  /* obsolete -- keep for reference */
        if ( pflags )
          *pflags |= 1L << field->flag_bit;
    #else
        FT_UNUSED( pflags );
    #endif
    
        parser->cursor = old_cursor;
        parser->limit  = old_limit;
    
      Exit:
        return error;
      }
    
    
      FT_LOCAL_DEF( FT_Long )
      ps_parser_to_int( PS_Parser  parser )
      {
        ps_parser_skip_spaces( parser );
        return PS_Conv_ToInt( &parser->cursor, parser->limit );
      }
    
    
      /* first character must be `<' if `delimiters' is non-zero */
    
      FT_LOCAL_DEF( FT_Error )
      ps_parser_to_bytes( PS_Parser  parser,
                          FT_Byte*   bytes,
                          FT_Offset  max_bytes,
                          FT_Long*   pnum_bytes,
                          FT_Bool    delimiters )
      {
        FT_Error  error = PSaux_Err_Ok;
        FT_Byte*  cur;
    
    
        ps_parser_skip_spaces( parser );
        cur = parser->cursor;
    
        if ( cur >= parser->limit )
          goto Exit;
    
        if ( delimiters )
        {
          if ( *cur != '<' )
          {
            FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" ));
            error = PSaux_Err_Invalid_File_Format;
            goto Exit;
          }
    
          cur++;
        }
    
        *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur,
                                              parser->limit,
                                              bytes,
                                              max_bytes );
    
        if ( delimiters )
        {
          if ( cur < parser->limit && *cur != '>' )
          {
            FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" ));
            error = PSaux_Err_Invalid_File_Format;
            goto Exit;
          }
    
          cur++;
        }
    
        parser->cursor = cur;
    
      Exit:
        return error;
      }
    
    
      FT_LOCAL_DEF( FT_Fixed )
      ps_parser_to_fixed( PS_Parser  parser,
                          FT_Int     power_ten )
      {
        ps_parser_skip_spaces( parser );
        return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten );
      }
    
    
      FT_LOCAL_DEF( FT_Int )
      ps_parser_to_coord_array( PS_Parser  parser,
                                FT_Int     max_coords,
                                FT_Short*  coords )
      {
        ps_parser_skip_spaces( parser );
        return ps_tocoordarray( &parser->cursor, parser->limit,
                                max_coords, coords );
      }
    
    
      FT_LOCAL_DEF( FT_Int )
      ps_parser_to_fixed_array( PS_Parser  parser,
                                FT_Int     max_values,
                                FT_Fixed*  values,
                                FT_Int     power_ten )
      {
        ps_parser_skip_spaces( parser );
        return ps_tofixedarray( &parser->cursor, parser->limit,
                                max_values, values, power_ten );
      }
    
    
    #if 0
    
      FT_LOCAL_DEF( FT_String* )
      T1_ToString( PS_Parser  parser )
      {
        return ps_tostring( &parser->cursor, parser->limit, parser->memory );
      }
    
    
      FT_LOCAL_DEF( FT_Bool )
      T1_ToBool( PS_Parser  parser )
      {
        return ps_tobool( &parser->cursor, parser->limit );
      }
    
    #endif /* 0 */
    
    
      FT_LOCAL_DEF( void )
      ps_parser_init( PS_Parser  parser,
                      FT_Byte*   base,
                      FT_Byte*   limit,
                      FT_Memory  memory )
      {
        parser->error  = PSaux_Err_Ok;
        parser->base   = base;
        parser->limit  = limit;
        parser->cursor = base;
        parser->memory = memory;
        parser->funcs  = ps_parser_funcs;
      }
    
    
      FT_LOCAL_DEF( void )
      ps_parser_done( PS_Parser  parser )
      {
        FT_UNUSED( parser );
      }
    
    
      /*************************************************************************/
      /*************************************************************************/
      /*****                                                               *****/
      /*****                            T1 BUILDER                         *****/
      /*****                                                               *****/
      /*************************************************************************/
      /*************************************************************************/
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    t1_builder_init                                                    */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Initializes a given glyph builder.                                 */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    builder :: A pointer to the glyph builder to initialize.           */
      /*                                                                       */
      /* <Input>                                                               */
      /*    face    :: The current face object.                                */
      /*                                                                       */
      /*    size    :: The current size object.                                */
      /*                                                                       */
      /*    glyph   :: The current glyph object.                               */
      /*                                                                       */
      /*    hinting :: Whether hinting should be applied.                      */
      /*                                                                       */
      FT_LOCAL_DEF( void )
      t1_builder_init( T1_Builder    builder,
                       FT_Face       face,
                       FT_Size       size,
                       FT_GlyphSlot  glyph,
                       FT_Bool       hinting )
      {
        builder->parse_state = T1_Parse_Start;
        builder->load_points = 1;
    
        builder->face   = face;
        builder->glyph  = glyph;
        builder->memory = face->memory;
    
        if ( glyph )
        {
          FT_GlyphLoader  loader = glyph->internal->loader;
    
    
          builder->loader  = loader;
          builder->base    = &loader->base.outline;
          builder->current = &loader->current.outline;
          FT_GlyphLoader_Rewind( loader );
    
          builder->hints_globals = size->internal;
          builder->hints_funcs   = 0;
    
          if ( hinting )
            builder->hints_funcs = glyph->internal->glyph_hints;
        }
    
        builder->pos_x = 0;
        builder->pos_y = 0;
    
        builder->left_bearing.x = 0;
        builder->left_bearing.y = 0;
        builder->advance.x      = 0;
        builder->advance.y      = 0;
    
        builder->funcs = t1_builder_funcs;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    t1_builder_done                                                    */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Finalizes a given glyph builder.  Its contents can still be used   */
      /*    after the call, but the function saves important information       */
      /*    within the corresponding glyph slot.                               */
      /*                                                                       */
      /* <Input>                                                               */
      /*    builder :: A pointer to the glyph builder to finalize.             */
      /*                                                                       */
      FT_LOCAL_DEF( void )
      t1_builder_done( T1_Builder  builder )
      {
        FT_GlyphSlot  glyph = builder->glyph;
    
    
        if ( glyph )
          glyph->outline = *builder->base;
      }
    
    
      /* check that there is enough space for `count' more points */
      FT_LOCAL_DEF( FT_Error )
      t1_builder_check_points( T1_Builder  builder,
                               FT_Int      count )
      {
        return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
      }
    
    
      /* add a new point, do not check space */
      FT_LOCAL_DEF( void )
      t1_builder_add_point( T1_Builder  builder,
                            FT_Pos      x,
                            FT_Pos      y,
                            FT_Byte     flag )
      {
        FT_Outline*  outline = builder->current;
    
    
        if ( builder->load_points )
        {
          FT_Vector*  point   = outline->points + outline->n_points;
          FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points;
    
    
          point->x = FIXED_TO_INT( x );
          point->y = FIXED_TO_INT( y );
          *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
        }
        outline->n_points++;
      }
    
    
      /* check space for a new on-curve point, then add it */
      FT_LOCAL_DEF( FT_Error )
      t1_builder_add_point1( T1_Builder  builder,
                             FT_Pos      x,
                             FT_Pos      y )
      {
        FT_Error  error;
    
    
        error = t1_builder_check_points( builder, 1 );
        if ( !error )
          t1_builder_add_point( builder, x, y, 1 );
    
        return error;
      }
    
    
      /* check space for a new contour, then add it */
      FT_LOCAL_DEF( FT_Error )
      t1_builder_add_contour( T1_Builder  builder )
      {
        FT_Outline*  outline = builder->current;
        FT_Error     error;
    
    
        if ( !builder->load_points )
        {
          outline->n_contours++;
          return PSaux_Err_Ok;
        }
    
        error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
        if ( !error )
        {
          if ( outline->n_contours > 0 )
            outline->contours[outline->n_contours - 1] =
              (short)( outline->n_points - 1 );
    
          outline->n_contours++;
        }
    
        return error;
      }
    
    
      /* if a path was begun, add its first on-curve point */
      FT_LOCAL_DEF( FT_Error )
      t1_builder_start_point( T1_Builder  builder,
                              FT_Pos      x,
                              FT_Pos      y )
      {
        FT_Error  error = PSaux_Err_Invalid_File_Format;
    
    
        /* test whether we are building a new contour */
    
        if ( builder->parse_state == T1_Parse_Have_Path )
          error = PSaux_Err_Ok;
        else if ( builder->parse_state == T1_Parse_Have_Moveto )
        {
          builder->parse_state = T1_Parse_Have_Path;
          error = t1_builder_add_contour( builder );
          if ( !error )
            error = t1_builder_add_point1( builder, x, y );
        }
    
        return error;
      }
    
    
      /* close the current contour */
      FT_LOCAL_DEF( void )
      t1_builder_close_contour( T1_Builder  builder )
      {
        FT_Outline*  outline = builder->current;
        FT_Int       first;
    
    
        if ( !outline )
          return;
    
        first = outline->n_contours <= 1
                ? 0 : outline->contours[outline->n_contours - 2] + 1;
    
        /* We must not include the last point in the path if it */
        /* is located on the first point.                       */
        if ( outline->n_points > 1 )
        {
          FT_Vector*  p1      = outline->points + first;
          FT_Vector*  p2      = outline->points + outline->n_points - 1;
          FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points - 1;
    
    
          /* `delete' last point only if it coincides with the first */
          /* point and it is not a control point (which can happen). */
          if ( p1->x == p2->x && p1->y == p2->y )
            if ( *control == FT_CURVE_TAG_ON )
              outline->n_points--;
        }
    
        if ( outline->n_contours > 0 )
        {
          /* Don't add contours only consisting of one point, i.e.,  */
          /* check whether the first and the last point is the same. */
          if ( first == outline->n_points - 1 )
          {
            outline->n_contours--;
            outline->n_points--;
          }
          else
            outline->contours[outline->n_contours - 1] =
              (short)( outline->n_points - 1 );
        }
      }
    
    
      /*************************************************************************/
      /*************************************************************************/
      /*****                                                               *****/
      /*****                            OTHER                              *****/
      /*****                                                               *****/
      /*************************************************************************/
      /*************************************************************************/
    
      FT_LOCAL_DEF( void )
      t1_decrypt( FT_Byte*   buffer,
                  FT_Offset  length,
                  FT_UShort  seed )
      {
        PS_Conv_EexecDecode( &buffer,
                             buffer + length,
                             buffer,
                             length,
                             &seed );
      }
    
    
    /* END */