Пример #1
0
  tt_face_done( FT_Face  ttface )           /* TT_Face */
  {
    TT_Face       face   = (TT_Face)ttface;
    FT_Memory     memory = face->root.memory;
    FT_Stream     stream = face->root.stream;

    SFNT_Service  sfnt   = (SFNT_Service)face->sfnt;


    /* for `extended TrueType formats' (i.e. compressed versions) */
    if ( face->extra.finalizer )
      face->extra.finalizer( face->extra.data );

    if ( sfnt )
      sfnt->done_face( face );

    /* freeing the locations table */
    tt_face_done_loca( face );

    /* freeing the CVT */
    FT_FREE( face->cvt );
    face->cvt_size = 0;

    /* freeing the programs */
    FT_FRAME_RELEASE( face->font_program );
    FT_FRAME_RELEASE( face->cvt_program );
    face->font_program_size = 0;
    face->cvt_program_size  = 0;

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    tt_done_blend( memory, face->blend );
    face->blend = NULL;
#endif
  }
Пример #2
0
  TT_Face_Done( TT_Face  face )
  {
    FT_Memory     memory = face->root.memory;
    FT_Stream     stream = face->root.stream;

    SFNT_Service  sfnt   = (SFNT_Service)face->sfnt;


    /* for `extended TrueType formats' (i.e. compressed versions) */
    if ( face->extra.finalizer )
      face->extra.finalizer( face->extra.data );

    if ( sfnt )
      sfnt->done_face( face );

    /* freeing the locations table */
    FT_FREE( face->glyph_locations );
    face->num_locations = 0;

    /* freeing the CVT */
    FT_FREE( face->cvt );
    face->cvt_size = 0;

    /* freeing the programs */
    FT_FRAME_RELEASE( face->font_program );
    FT_FRAME_RELEASE( face->cvt_program );
    face->font_program_size = 0;
    face->cvt_program_size  = 0;
  }
Пример #3
0
  static void
  fnt_font_done( FNT_Font   font,
                 FT_Stream  stream )
  {
    if ( font->fnt_frame )
      FT_FRAME_RELEASE( font->fnt_frame );

    font->fnt_size  = 0;
    font->fnt_frame = 0;
  }
Пример #4
0
  static void
  CFF_Done_FD_Select( CFF_FDSelect  fdselect,
                      FT_Stream     stream )
  {
    if ( fdselect->data )
      FT_FRAME_RELEASE( fdselect->data );

    fdselect->data_size   = 0;
    fdselect->format      = 0;
    fdselect->range_count = 0;
  }
Пример #5
0
  cff_index_forget_element( CFF_Index  idx,
                            FT_Byte**  pbytes )
  {
    if ( idx->bytes == 0 )
    {
      FT_Stream  stream = idx->stream;


      FT_FRAME_RELEASE( *pbytes );
    }
  }
Пример #6
0
  tt_face_done_kern( TT_Face  face )
  {
    FT_Stream  stream = face->root.stream;


    FT_FRAME_RELEASE( face->kern_table );
    face->kern_table_size = 0;
    face->num_kern_tables = 0;
    face->kern_avail_bits = 0;
    face->kern_order_bits = 0;
  }
Пример #7
0
  CID_Done_Parser( CID_Parser*  parser )
  {
    /* always free the private dictionary */
    if ( parser->postscript )
    {
      FT_Stream  stream = parser->stream;


      FT_FRAME_RELEASE( parser->postscript );
    }
    parser->root.funcs.done( &parser->root );
  }
Пример #8
0
  tt_face_free_colr( TT_Face  face )
  {
    FT_Stream  stream = face->root.stream;
    FT_Memory  memory = face->root.memory;

    Colr*  colr = (Colr*)face->colr;


    if ( colr )
    {
      FT_FRAME_RELEASE( colr->table );
      FT_FREE( colr );
    }
  }
Пример #9
0
  static void
  cff_index_done( CFF_Index  idx )
  {
    if ( idx->stream )
    {
      FT_Stream  stream = idx->stream;
      FT_Memory  memory = stream->memory;


      if ( idx->bytes )
        FT_FRAME_RELEASE( idx->bytes );

      FT_FREE( idx->offsets );
      FT_MEM_ZERO( idx, sizeof ( *idx ) );
    }
  }
Пример #10
0
static void
fnt_font_done( FNT_Face face )
{
    FT_Memory  memory = FT_FACE( face )->memory;
    FT_Stream  stream = FT_FACE( face )->stream;
    FNT_Font   font   = face->font;


    if ( !font )
        return;

    if ( font->fnt_frame )
        FT_FRAME_RELEASE( font->fnt_frame );

    FT_FREE( font );
    face->font = 0;
}
Пример #11
0
  tt_face_free_bdf_props( TT_Face  face )
  {
    TT_BDF  bdf = &face->bdf;


    if ( bdf->loaded )
    {
      FT_Stream  stream = FT_FACE(face)->stream;


      if ( bdf->table != NULL )
        FT_FRAME_RELEASE( bdf->table );

      bdf->table_end    = NULL;
      bdf->strings      = NULL;
      bdf->strings_size = 0;
    }
  }
Пример #12
0
sfnt_done_face(TT_Face face)
{
    FT_Memory    memory;
    SFNT_Service sfnt;


    if (!face)
        return;

    memory = face->root.memory;
    sfnt   = (SFNT_Service)face->sfnt;

    if (sfnt)
    {
        /* destroy the postscript names table if it is loaded */
        if (sfnt->free_psnames)
            sfnt->free_psnames(face);

        /* destroy the embedded bitmaps table if it is loaded */
        if (sfnt->free_eblc)
            sfnt->free_eblc(face);
    }

#ifdef TT_CONFIG_OPTION_BDF
    /* freeing the embedded BDF properties */
    tt_face_free_bdf_props(face);
#endif

    /* freeing the kerning table */
    tt_face_done_kern(face);

    /* freeing the collection table */
    FT_FREE(face->ttc_header.offsets);
    face->ttc_header.count = 0;

    /* freeing table directory */
    FT_FREE(face->dir_tables);
    face->num_tables = 0;

    {
        FT_Stream stream = FT_FACE_STREAM(face);


        /* simply release the 'cmap' table frame */
        FT_FRAME_RELEASE(face->cmap_table);
        face->cmap_size = 0;
    }

    /* freeing the horizontal metrics */
#ifndef FT_CONFIG_OPTION_OLD_INTERNALS
    {
        FT_Stream stream = FT_FACE_STREAM(face);


        FT_FRAME_RELEASE(face->horz_metrics);
        FT_FRAME_RELEASE(face->vert_metrics);
        face->horz_metrics_size = 0;
        face->vert_metrics_size = 0;
    }
#else
    FT_FREE(face->horizontal.long_metrics);
    FT_FREE(face->horizontal.short_metrics);
#endif

    /* freeing the vertical ones, if any */
    if (face->vertical_info)
    {
        FT_FREE(face->vertical.long_metrics);
        FT_FREE(face->vertical.short_metrics);
        face->vertical_info = 0;
    }

    /* freeing the gasp table */
    FT_FREE(face->gasp.gaspRanges);
    face->gasp.numRanges = 0;

    /* freeing the name table */
    if (sfnt)
        sfnt->free_name(face);

    /* freeing family and style name */
    FT_FREE(face->root.family_name);
    FT_FREE(face->root.style_name);

    /* freeing sbit size table */
    FT_FREE(face->root.available_sizes);
    face->root.num_fixed_sizes = 0;

    FT_FREE(face->postscript_name);

    face->sfnt = 0;
}
Пример #13
0
  static FT_Error
  tt_face_load_bdf_props( TT_Face    face,
                          FT_Stream  stream )
  {
    TT_BDF    bdf = &face->bdf;
    FT_ULong  length;
    FT_Error  error;


    FT_ZERO( bdf );

    error = tt_face_goto_table( face, TTAG_BDF, stream, &length );
    if ( error                                  ||
         length < 8                             ||
         FT_FRAME_EXTRACT( length, bdf->table ) )
    {
      error = FT_THROW( Invalid_Table );
      goto Exit;
    }

    bdf->table_end = bdf->table + length;

    {
      FT_Byte*   p           = bdf->table;
      FT_UInt    version     = FT_NEXT_USHORT( p );
      FT_UInt    num_strikes = FT_NEXT_USHORT( p );
      FT_ULong   strings     = FT_NEXT_ULONG ( p );
      FT_UInt    count;
      FT_Byte*   strike;


      if ( version != 0x0001                 ||
           strings < 8                       ||
           ( strings - 8 ) / 4 < num_strikes ||
           strings + 1 > length              )
      {
        goto BadTable;
      }

      bdf->num_strikes  = num_strikes;
      bdf->strings      = bdf->table + strings;
      bdf->strings_size = length - strings;

      count  = bdf->num_strikes;
      p      = bdf->table + 8;
      strike = p + count * 4;


      for ( ; count > 0; count-- )
      {
        FT_UInt  num_items = FT_PEEK_USHORT( p + 2 );

        /*
         *  We don't need to check the value sets themselves, since this
         *  is done later.
         */
        strike += 10 * num_items;

        p += 4;
      }

      if ( strike > bdf->strings )
        goto BadTable;
    }

    bdf->loaded = 1;

  Exit:
    return error;

  BadTable:
    FT_FRAME_RELEASE( bdf->table );
    FT_ZERO( bdf );
    error = FT_THROW( Invalid_Table );
    goto Exit;
  }
Пример #14
0
  cid_parser_new( CID_Parser*    parser,
                  FT_Stream      stream,
                  FT_Memory      memory,
                  PSAux_Service  psaux )
  {
    FT_Error  error;
    FT_ULong  base_offset, offset, ps_len;
    FT_Byte   *cur, *limit;
    FT_Byte   *arg1, *arg2;


    FT_ZERO( parser );
    psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory );

    parser->stream = stream;

    base_offset = FT_STREAM_POS();

    /* first of all, check the font format in the header */
    if ( FT_FRAME_ENTER( 31 ) )
      goto Exit;

    if ( ft_strncmp( (char *)stream->cursor,
                     "%!PS-Adobe-3.0 Resource-CIDFont", 31 ) )
    {
      FT_TRACE2(( "  not a CID-keyed font\n" ));
      error = FT_THROW( Unknown_File_Format );
    }

    FT_FRAME_EXIT();
    if ( error )
      goto Exit;

  Again:
    /* now, read the rest of the file until we find */
    /* `StartData' or `/sfnts'                      */
    {
      /*
       * The algorithm is as follows (omitting the case with less than 256
       * bytes to fill for simplicity).
       *
       * 1. Fill the buffer with 256 + STARTDATA_LEN bytes.
       *
       * 2. Search for the STARTDATA and SFNTS strings at positions
       *    buffer[0], buffer[1], ...,
       *    buffer[255 + STARTDATA_LEN - SFNTS_LEN].
       *
       * 3. Move the last STARTDATA_LEN bytes to buffer[0].
       *
       * 4. Fill the buffer with 256 bytes, starting at STARTDATA_LEN.
       *
       * 5. Repeat with step 2.
       *
       */
      FT_Byte  buffer[256 + STARTDATA_LEN + 1];

      /* values for the first loop */
      FT_ULong  read_len    = 256 + STARTDATA_LEN;
      FT_ULong  read_offset = 0;
      FT_Byte*  p           = buffer;


      for ( offset = FT_STREAM_POS(); ; offset += 256 )
      {
        FT_ULong  stream_len;


        stream_len = stream->size - FT_STREAM_POS();

        read_len = FT_MIN( read_len, stream_len );
        if ( FT_STREAM_READ( p, read_len ) )
          goto Exit;

        /* ensure that we do not compare with data beyond the buffer */
        p[read_len] = '\0';

        limit = p + read_len - SFNTS_LEN;

        for ( p = buffer; p < limit; p++ )
        {
          if ( p[0] == 'S'                                           &&
               ft_strncmp( (char*)p, STARTDATA, STARTDATA_LEN ) == 0 )
          {
            /* save offset of binary data after `StartData' */
            offset += (FT_ULong)( p - buffer ) + STARTDATA_LEN + 1;
            goto Found;
          }
          else if ( p[1] == 's'                                   &&
                    ft_strncmp( (char*)p, SFNTS, SFNTS_LEN ) == 0 )
          {
            offset += (FT_ULong)( p - buffer ) + SFNTS_LEN + 1;
            goto Found;
          }
        }

        if ( read_offset + read_len < STARTDATA_LEN )
        {
          FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" ));
          error = FT_THROW( Invalid_File_Format );
          goto Exit;
        }

        FT_MEM_MOVE( buffer,
                     buffer + read_offset + read_len - STARTDATA_LEN,
                     STARTDATA_LEN );

        /* values for the next loop */
        read_len    = 256;
        read_offset = STARTDATA_LEN;
        p           = buffer + read_offset;
      }
    }

  Found:
    /* We have found the start of the binary data or the `/sfnts' token. */
    /* Now rewind and extract the frame corresponding to this PostScript */
    /* section.                                                          */

    ps_len = offset - base_offset;
    if ( FT_STREAM_SEEK( base_offset )                  ||
         FT_FRAME_EXTRACT( ps_len, parser->postscript ) )
      goto Exit;

    parser->data_offset    = offset;
    parser->postscript_len = ps_len;
    parser->root.base      = parser->postscript;
    parser->root.cursor    = parser->postscript;
    parser->root.limit     = parser->root.cursor + ps_len;
    parser->num_dict       = -1;

    /* Finally, we check whether `StartData' or `/sfnts' was real --  */
    /* it could be in a comment or string.  We also get the arguments */
    /* of `StartData' to find out whether the data is represented in  */
    /* binary or hex format.                                          */

    arg1 = parser->root.cursor;
    cid_parser_skip_PS_token( parser );
    cid_parser_skip_spaces  ( parser );
    arg2 = parser->root.cursor;
    cid_parser_skip_PS_token( parser );
    cid_parser_skip_spaces  ( parser );

    limit = parser->root.limit;
    cur   = parser->root.cursor;

    while ( cur <= limit - SFNTS_LEN )
    {
      if ( parser->root.error )
      {
        error = parser->root.error;
        goto Exit;
      }

      if ( cur[0] == 'S'                                           &&
           cur <= limit - STARTDATA_LEN                            &&
           ft_strncmp( (char*)cur, STARTDATA, STARTDATA_LEN ) == 0 )
      {
        if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 )
        {
          FT_Long  tmp = ft_strtol( (const char *)arg2, NULL, 10 );


          if ( tmp < 0 )
          {
            FT_ERROR(( "cid_parser_new: invalid length of hex data\n" ));
            error = FT_THROW( Invalid_File_Format );
          }
          else
            parser->binary_length = (FT_ULong)tmp;
        }

        goto Exit;
      }
      else if ( cur[1] == 's'                                   &&
                ft_strncmp( (char*)cur, SFNTS, SFNTS_LEN ) == 0 )
      {
        FT_TRACE2(( "cid_parser_new: cannot handle Type 11 fonts\n" ));
        error = FT_THROW( Unknown_File_Format );
        goto Exit;
      }

      cid_parser_skip_PS_token( parser );
      cid_parser_skip_spaces  ( parser );
      arg1 = arg2;
      arg2 = cur;
      cur  = parser->root.cursor;
    }

    /* we haven't found the correct `StartData'; go back and continue */
    /* searching                                                      */
    FT_FRAME_RELEASE( parser->postscript );
    if ( !FT_STREAM_SEEK( offset ) )
      goto Again;

  Exit:
    return error;
  }
Пример #15
0
  cid_parser_new( CID_Parser*    parser,
                  FT_Stream      stream,
                  FT_Memory      memory,
                  PSAux_Service  psaux )
  {
    FT_Error  error;
    FT_ULong  base_offset, offset, ps_len;
    FT_Byte   buffer[256 + 10];
    FT_Int    buff_len;
    FT_Byte   *cur, *limit;
    FT_Byte   *arg1, *arg2;


    FT_MEM_ZERO( parser, sizeof ( *parser ) );
    psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory );

    parser->stream = stream;

    base_offset = FT_STREAM_POS();

    /* first of all, check the font format in the header */
    if ( FT_FRAME_ENTER( 31 ) )
      goto Exit;

    if ( ft_strncmp( (char *)stream->cursor,
                     "%!PS-Adobe-3.0 Resource-CIDFont", 31 ) )
    {
      FT_TRACE2(( "[not a valid CID-keyed font]\n" ));
      error = CID_Err_Unknown_File_Format;
    }

    FT_FRAME_EXIT();
    if ( error )
      goto Exit;

  Again:
    /* now, read the rest of the file until we find a `StartData' */
    buff_len = 256;
    for (;;)
    {
      FT_Byte*  p;
      FT_ULong  top_position;


      /* fill input buffer */
      limit     = buffer + 256;
      buff_len -= 256;
      if ( buff_len > 0 )
        FT_MEM_MOVE( buffer, limit, buff_len );

      p = buffer + buff_len;

      if ( FT_STREAM_READ( p, 256 + 10 - buff_len ) )
        goto Exit;

      top_position = FT_STREAM_POS() - buff_len;
      buff_len     = 256 + 10;

      /* look for `StartData' */
      for ( p = buffer; p < limit; p++ )
      {
        if ( p[0] == 'S' && ft_strncmp( (char*)p, "StartData", 9 ) == 0 )
        {
          /* save offset of binary data after `StartData' */
          offset = (FT_ULong)( top_position - ( limit - p ) + 10 );
          goto Found;
        }
      }
    }

  Found:
    /* we have found the start of the binary data.  We will now     */
    /* rewind and extract the frame corresponding to the PostScript */
    /* section                                                      */

    ps_len = offset - base_offset;
    if ( FT_STREAM_SEEK( base_offset )                  ||
         FT_FRAME_EXTRACT( ps_len, parser->postscript ) )
      goto Exit;

    parser->data_offset    = offset;
    parser->postscript_len = ps_len;
    parser->root.base      = parser->postscript;
    parser->root.cursor    = parser->postscript;
    parser->root.limit     = parser->root.cursor + ps_len;
    parser->num_dict       = -1;

    /* Finally, we check whether `StartData' was real -- it could be  */
    /* in a comment or string.  We also get its arguments to find out */
    /* whether the data is represented in binary or hex format.       */

    arg1 = parser->root.cursor;
    cid_parser_skip_PS_token( parser );
    cid_parser_skip_spaces  ( parser );
    arg2 = parser->root.cursor;
    cid_parser_skip_PS_token( parser );
    cid_parser_skip_spaces  ( parser );

    limit = parser->root.limit;
    cur   = parser->root.cursor;

    while ( cur < limit )
    {
      if ( parser->root.error )
        break;

      if ( *cur == 'S' && ft_strncmp( (char*)cur, "StartData", 9 ) == 0 )
      {
        if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 )
          parser->binary_length = ft_atol( (const char *)arg2 );

        limit = parser->root.limit;
        cur   = parser->root.cursor;
        goto Exit;
      }

      cid_parser_skip_PS_token( parser );
      cid_parser_skip_spaces  ( parser );
      arg1 = arg2;
      arg2 = cur;
      cur  = parser->root.cursor;
    }

    /* we haven't found the correct `StartData'; go back and continue */
    /* searching                                                      */
    FT_FRAME_RELEASE( parser->postscript );
    if ( !FT_STREAM_SEEK( offset ) )
      goto Again;

  Exit:
    return error;
  }
Пример #16
0
cid_parser_new( CID_Parser*    parser,
                FT_Stream      stream,
                FT_Memory      memory,
                PSAux_Service  psaux )
{
    FT_Error  error;
    FT_ULong  base_offset, offset, ps_len;
    FT_Byte   *cur, *limit;
    FT_Byte   *arg1, *arg2;


    FT_MEM_ZERO( parser, sizeof ( *parser ) );
    psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory );

    parser->stream = stream;

    base_offset = FT_STREAM_POS();

    /* first of all, check the font format in the header */
    if ( FT_FRAME_ENTER( 31 ) )
        goto Exit;

    if ( ft_strncmp( (char *)stream->cursor,
                     "%!PS-Adobe-3.0 Resource-CIDFont", 31 ) )
    {
        FT_TRACE2(( "  not a CID-keyed font\n" ));
        error = FT_THROW( Unknown_File_Format );
    }

    FT_FRAME_EXIT();
    if ( error )
        goto Exit;

Again:
    /* now, read the rest of the file until we find */
    /* `StartData' or `/sfnts'                      */
    {
        FT_Byte   buffer[256 + 10];
        FT_ULong  read_len = 256 + 10;
        FT_Byte*  p        = buffer;


        for ( offset = FT_STREAM_POS(); ; offset += 256 )
        {
            FT_ULong  stream_len;


            stream_len = stream->size - FT_STREAM_POS();
            if ( stream_len == 0 )
            {
                FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" ));
                error = FT_THROW( Invalid_File_Format );
                goto Exit;
            }

            read_len = FT_MIN( read_len, stream_len );
            if ( FT_STREAM_READ( p, read_len ) )
                goto Exit;

            if ( read_len < 256 )
                p[read_len]  = '\0';

            limit = p + read_len - 10;

            for ( p = buffer; p < limit; p++ )
            {
                if ( p[0] == 'S' && ft_strncmp( (char*)p, "StartData", 9 ) == 0 )
                {
                    /* save offset of binary data after `StartData' */
                    offset += (FT_ULong)( p - buffer + 10 );
                    goto Found;
                }
                else if ( p[1] == 's' && ft_strncmp( (char*)p, "/sfnts", 6 ) == 0 )
                {
                    offset += (FT_ULong)( p - buffer + 7 );
                    goto Found;
                }
            }

            FT_MEM_MOVE( buffer, p, 10 );
            read_len = 256;
            p = buffer + 10;
        }
    }

Found:
    /* We have found the start of the binary data or the `/sfnts' token. */
    /* Now rewind and extract the frame corresponding to this PostScript */
    /* section.                                                          */

    ps_len = offset - base_offset;
    if ( FT_STREAM_SEEK( base_offset )                  ||
            FT_FRAME_EXTRACT( ps_len, parser->postscript ) )
        goto Exit;

    parser->data_offset    = offset;
    parser->postscript_len = ps_len;
    parser->root.base      = parser->postscript;
    parser->root.cursor    = parser->postscript;
    parser->root.limit     = parser->root.cursor + ps_len;
    parser->num_dict       = -1;

    /* Finally, we check whether `StartData' or `/sfnts' was real --  */
    /* it could be in a comment or string.  We also get the arguments */
    /* of `StartData' to find out whether the data is represented in  */
    /* binary or hex format.                                          */

    arg1 = parser->root.cursor;
    cid_parser_skip_PS_token( parser );
    cid_parser_skip_spaces  ( parser );
    arg2 = parser->root.cursor;
    cid_parser_skip_PS_token( parser );
    cid_parser_skip_spaces  ( parser );

    limit = parser->root.limit;
    cur   = parser->root.cursor;

    while ( cur < limit )
    {
        if ( parser->root.error )
        {
            error = parser->root.error;
            goto Exit;
        }

        if ( cur[0] == 'S' && ft_strncmp( (char*)cur, "StartData", 9 ) == 0 )
        {
            if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 )
            {
                FT_Long  tmp = ft_atol( (const char *)arg2 );


                if ( tmp < 0 )
                {
                    FT_ERROR(( "cid_parser_new: invalid length of hex data\n" ));
                    error = FT_THROW( Invalid_File_Format );
                }
                else
                    parser->binary_length = (FT_ULong)tmp;
            }

            goto Exit;
        }
        else if ( cur[1] == 's' && ft_strncmp( (char*)cur, "/sfnts", 6 ) == 0 )
        {
            FT_TRACE2(( "cid_parser_new: cannot handle Type 11 fonts\n" ));
            error = FT_THROW( Unknown_File_Format );
            goto Exit;
        }

        cid_parser_skip_PS_token( parser );
        cid_parser_skip_spaces  ( parser );
        arg1 = arg2;
        arg2 = cur;
        cur  = parser->root.cursor;
    }

    /* we haven't found the correct `StartData'; go back and continue */
    /* searching                                                      */
    FT_FRAME_RELEASE( parser->postscript );
    if ( !FT_STREAM_SEEK( offset ) )
        goto Again;

Exit:
    return error;
}
Пример #17
0
  tt_face_load_colr( TT_Face    face,
                     FT_Stream  stream )
  {
    FT_Error   error;
    FT_Memory  memory = face->root.memory;

    FT_Byte*  table = NULL;
    FT_Byte*  p     = NULL;

    Colr*  colr = NULL;

    FT_ULong  base_glyph_offset, layer_offset;
    FT_ULong  table_size;


    /* `COLR' always needs `CPAL' */
    if ( !face->cpal )
      return FT_THROW( Invalid_File_Format );

    error = face->goto_table( face, TTAG_COLR, stream, &table_size );
    if ( error )
      goto NoColr;

    if ( table_size < COLR_HEADER_SIZE )
      goto InvalidTable;

    if ( FT_FRAME_EXTRACT( table_size, table ) )
      goto NoColr;

    p = table;

    if ( FT_NEW( colr ) )
      goto NoColr;

    colr->version = FT_NEXT_USHORT( p );
    if ( colr->version != 0 )
      goto InvalidTable;

    colr->num_base_glyphs = FT_NEXT_USHORT( p );
    base_glyph_offset     = FT_NEXT_ULONG( p );

    if ( base_glyph_offset >= table_size )
      goto InvalidTable;
    if ( colr->num_base_glyphs * BASE_GLYPH_SIZE >
           table_size - base_glyph_offset )
      goto InvalidTable;

    layer_offset     = FT_NEXT_ULONG( p );
    colr->num_layers = FT_NEXT_USHORT( p );

    if ( layer_offset >= table_size )
      goto InvalidTable;
    if ( colr->num_layers * LAYER_SIZE > table_size - layer_offset )
      goto InvalidTable;

    colr->base_glyphs = (FT_Byte*)( table + base_glyph_offset );
    colr->layers      = (FT_Byte*)( table + layer_offset      );
    colr->table       = table;
    colr->table_size  = table_size;

    face->colr = colr;

    return FT_Err_Ok;

  InvalidTable:
    error = FT_THROW( Invalid_Table );

  NoColr:
    FT_FRAME_RELEASE( table );
    FT_FREE( colr );

    return error;
  }