text:
/****************************************************************************
 *
 * ttcmap.h
 *
 *   TrueType character mapping table (cmap) support (specification).
 *
 * Copyright (C) 2002-2020 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.
 *
 */

#ifndef TTCMAP_H_
#define TTCMAP_H_

#include <ft2build.h>
#include FT_INTERNAL_TRUETYPE_TYPES_H
#include FT_INTERNAL_VALIDATE_H
#include FT_SERVICE_TT_CMAP_H

FT_BEGIN_HEADER

#define TT_CMAP_FLAG_UNSORTED     1
#define TT_CMAP_FLAG_OVERLAPPING  2

  typedef struct  TT_CMapRec_
  {
    FT_CMapRec  cmap;
    FT_Byte*    data;           /* pointer to in-memory cmap table */
    FT_Int      flags;          /* for format 4 only               */

  } TT_CMapRec, *TT_CMap;

  typedef const struct TT_CMap_ClassRec_*  TT_CMap_Class;

  typedef FT_Error
  (*TT_CMap_ValidateFunc)( FT_Byte*      data,
                           FT_Validator  valid );

  typedef struct  TT_CMap_ClassRec_
  {
    FT_CMap_ClassRec      clazz;
    FT_UInt               format;
    TT_CMap_ValidateFunc  validate;
    TT_CMap_Info_GetFunc  get_cmap_info;

  } TT_CMap_ClassRec;

#define FT_DEFINE_TT_CMAP( class_,             \
                           size_,              \
                           init_,              \
                           done_,              \
                           char_index_,        \
                           char_next_,         \
                           char_var_index_,    \
                           char_var_default_,  \
                           variant_list_,      \
                           charvariant_list_,  \
                           variantchar_list_,  \
                           format_,            \
                           validate_,          \
                           get_cmap_info_ )    \
  FT_CALLBACK_TABLE_DEF                        \
  const TT_CMap_ClassRec  class_ =             \
  {                                            \
    { size_,                                   \
      init_,                                   \
      done_,                                   \
      char_index_,                             \
      char_next_,                              \
      char_var_index_,                         \
      char_var_default_,                       \
      variant_list_,                           \
      charvariant_list_,                       \
      variantchar_list_                        \
    },                                         \
                                               \
    format_,                                   \
    validate_,                                 \
    get_cmap_info_                             \
  };

  typedef struct  TT_ValidatorRec_
  {
    FT_ValidatorRec  validator;
    FT_UInt          num_glyphs;

  } TT_ValidatorRec, *TT_Validator;

#define TT_VALIDATOR( x )          ( (TT_Validator)( x ) )
#define TT_VALID_GLYPH_COUNT( x )  TT_VALIDATOR( x )->num_glyphs

  FT_CALLBACK_TABLE const TT_CMap_ClassRec  tt_cmap_unicode_class_rec;

  FT_LOCAL( FT_Error )
  tt_face_build_cmaps( TT_Face  face );

  /* used in tt-cmaps service */
  FT_LOCAL( FT_Error )
  tt_get_cmap_info( FT_CharMap    charmap,
                    TT_CMapInfo  *cmap_info );

FT_END_HEADER

#endif /* TTCMAP_H_ */

/* END */


text:
/****************************************************************************
 *
 * ttcpal.h
 *
 *   TrueType and OpenType color palette support (specification).
 *
 * Copyright (C) 2018-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
 *
 * 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.
 *
 */

#ifndef __TTCPAL_H__
#define __TTCPAL_H__

#include <ft2build.h>
#include "ttload.h"

FT_BEGIN_HEADER

  FT_LOCAL( FT_Error )
  tt_face_load_cpal( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( void )
  tt_face_free_cpal( TT_Face  face );

  FT_LOCAL( FT_Error )
  tt_face_palette_set( TT_Face  face,
                       FT_UInt  palette_index );

FT_END_HEADER

#endif /* __TTCPAL_H__ */

/* END */

repo_language: C

mime_type: text/x-c


text:
/****************************************************************************
 *
 * sfdriver.h
 *
 *   High-level SFNT driver interface (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef SFDRIVER_H_
#define SFDRIVER_H_

#include <ft2build.h>
#include FT_MODULE_H

FT_BEGIN_HEADER

  FT_DECLARE_MODULE( sfnt_module_class )

FT_END_HEADER

#endif /* SFDRIVER_H_ */

/* END */


text:
/****************************************************************************
 *
 * ttcolr.h
 *
 *   TrueType and OpenType colored glyph layer support (specification).
 *
 * Copyright (C) 2018-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * Originally written by Shao Yu Zhang <shaozhang@fb.com>.
 *
 * 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.
 *
 */

#ifndef __TTCOLR_H__
#define __TTCOLR_H__

#include <ft2build.h>
#include "ttload.h"

FT_BEGIN_HEADER

  FT_LOCAL( FT_Error )
  tt_face_load_colr( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( void )
  tt_face_free_colr( TT_Face  face );

  FT_LOCAL( FT_Bool )
  tt_face_get_colr_layer( TT_Face            face,
                          FT_UInt            base_glyph,
                          FT_UInt           *aglyph_index,
                          FT_UInt           *acolor_index,
                          FT_LayerIterator*  iterator );

  FT_LOCAL( FT_Error )
  tt_face_colr_blend_layer( TT_Face       face,
                            FT_UInt       color_index,
                            FT_GlyphSlot  dstSlot,
                            FT_GlyphSlot  srcSlot );

FT_END_HEADER

#endif /* __TTCOLR_H__ */

/* END */


text:
/****************************************************************************
 *
 * sfwoff.c
 *
 *   WOFF format management (base).
 *
 * Copyright (C) 1996-2020 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 "sfwoff.h"
#include FT_TRUETYPE_TAGS_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_GZIP_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  sfwoff

#define WRITE_USHORT( p, v )                \
          do                                \
          {                                 \
            *(p)++ = (FT_Byte)( (v) >> 8 ); \
            *(p)++ = (FT_Byte)( (v) >> 0 ); \
                                            \
          } while ( 0 )

#define WRITE_ULONG( p, v )                  \
          do                                 \
          {                                  \
            *(p)++ = (FT_Byte)( (v) >> 24 ); \
            *(p)++ = (FT_Byte)( (v) >> 16 ); \
            *(p)++ = (FT_Byte)( (v) >>  8 ); \
            *(p)++ = (FT_Byte)( (v) >>  0 ); \
                                             \
          } while ( 0 )

  static void
  sfnt_stream_close( FT_Stream  stream )
  {
    FT_Memory  memory = stream->memory;

    FT_FREE( stream->base );

    stream->size  = 0;
    stream->base  = NULL;
    stream->close = NULL;
  }

  FT_CALLBACK_DEF( int )
  compare_offsets( const void*  a,
                   const void*  b )
  {
    WOFF_Table  table1 = *(WOFF_Table*)a;
    WOFF_Table  table2 = *(WOFF_Table*)b;

    FT_ULong  offset1 = table1->Offset;
    FT_ULong  offset2 = table2->Offset;

    if ( offset1 > offset2 )
      return 1;
    else if ( offset1 < offset2 )
      return -1;
    else
      return 0;
  }

  /* Replace `face->root.stream' with a stream containing the extracted */
  /* SFNT of a WOFF font.                                               */

  FT_LOCAL_DEF( FT_Error )
  woff_open_font( FT_Stream  stream,
                  TT_Face    face )
  {
    FT_Memory       memory = stream->memory;
    FT_Error        error  = FT_Err_Ok;

    WOFF_HeaderRec  woff;
    WOFF_Table      tables  = NULL;
    WOFF_Table*     indices = NULL;

    FT_ULong        woff_offset;

    FT_Byte*        sfnt        = NULL;
    FT_Stream       sfnt_stream = NULL;

    FT_Byte*        sfnt_header;
    FT_ULong        sfnt_offset;

    FT_Int          nn;
    FT_ULong        old_tag = 0;

    static const FT_Frame_Field  woff_header_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  WOFF_HeaderRec

      FT_FRAME_START( 44 ),
        FT_FRAME_ULONG ( signature ),
        FT_FRAME_ULONG ( flavor ),
        FT_FRAME_ULONG ( length ),
        FT_FRAME_USHORT( num_tables ),
        FT_FRAME_USHORT( reserved ),
        FT_FRAME_ULONG ( totalSfntSize ),
        FT_FRAME_USHORT( majorVersion ),
        FT_FRAME_USHORT( minorVersion ),
        FT_FRAME_ULONG ( metaOffset ),
        FT_FRAME_ULONG ( metaLength ),
        FT_FRAME_ULONG ( metaOrigLength ),
        FT_FRAME_ULONG ( privOffset ),
        FT_FRAME_ULONG ( privLength ),
      FT_FRAME_END
    };

    FT_ASSERT( stream == face->root.stream );
    FT_ASSERT( FT_STREAM_POS() == 0 );

    if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) )
      return error;

    /* Make sure we don't recurse back here or hit TTC code. */
    if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf )
      return FT_THROW( Invalid_Table );

    /* Miscellaneous checks. */
    if ( woff.length != stream->size                              ||
         woff.num_tables == 0                                     ||
         44 + woff.num_tables * 20UL >= woff.length               ||
         12 + woff.num_tables * 16UL >= woff.totalSfntSize        ||
         ( woff.totalSfntSize & 3 ) != 0                          ||
         ( woff.metaOffset == 0 && ( woff.metaLength != 0     ||
                                     woff.metaOrigLength != 0 ) ) ||
         ( woff.metaLength != 0 && woff.metaOrigLength == 0 )     ||
         ( woff.privOffset == 0 && woff.privLength != 0 )         )
    {
      FT_ERROR(( "woff_font_open: invalid WOFF header\n" ));
      return FT_THROW( Invalid_Table );
    }

    /* Don't trust `totalSfntSize' before thorough checks. */
    if ( FT_ALLOC( sfnt, 12 + woff.num_tables * 16UL ) ||
         FT_NEW( sfnt_stream )                         )
      goto Exit;

    sfnt_header = sfnt;

    /* Write sfnt header. */
    {
      FT_UInt  searchRange, entrySelector, rangeShift, x;

      x             = woff.num_tables;
      entrySelector = 0;
      while ( x )
      {
        x            >>= 1;
        entrySelector += 1;
      }
      entrySelector--;

      searchRange = ( 1 << entrySelector ) * 16;
      rangeShift  = woff.num_tables * 16 - searchRange;

      WRITE_ULONG ( sfnt_header, woff.flavor );
      WRITE_USHORT( sfnt_header, woff.num_tables );
      WRITE_USHORT( sfnt_header, searchRange );
      WRITE_USHORT( sfnt_header, entrySelector );
      WRITE_USHORT( sfnt_header, rangeShift );
    }

    /* While the entries in the sfnt header must be sorted by the */
    /* tag value, the tables themselves are not.  We thus have to */
    /* sort them by offset and check that they don't overlap.     */

    if ( FT_NEW_ARRAY( tables, woff.num_tables )  ||
         FT_NEW_ARRAY( indices, woff.num_tables ) )
      goto Exit;

    FT_TRACE2(( "\n"
                "  tag    offset    compLen  origLen  checksum\n"
                "  -------------------------------------------\n" ));

    if ( FT_FRAME_ENTER( 20L * woff.num_tables ) )
      goto Exit;

    for ( nn = 0; nn < woff.num_tables; nn++ )
    {
      WOFF_Table  table = tables + nn;

      table->Tag        = FT_GET_TAG4();
      table->Offset     = FT_GET_ULONG();
      table->CompLength = FT_GET_ULONG();
      table->OrigLength = FT_GET_ULONG();
      table->CheckSum   = FT_GET_ULONG();

      FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx  %08lx\n",
                  (FT_Char)( table->Tag >> 24 ),
                  (FT_Char)( table->Tag >> 16 ),
                  (FT_Char)( table->Tag >> 8  ),
                  (FT_Char)( table->Tag       ),
                  table->Offset,
                  table->CompLength,
                  table->OrigLength,
                  table->CheckSum ));

      if ( table->Tag <= old_tag )
      {
        FT_FRAME_EXIT();

        FT_ERROR(( "woff_font_open: table tags are not sorted\n" ));
        error = FT_THROW( Invalid_Table );
        goto Exit;
      }

      old_tag     = table->Tag;
      indices[nn] = table;
    }

    FT_FRAME_EXIT();

    /* Sort by offset. */

    ft_qsort( indices,
              woff.num_tables,
              sizeof ( WOFF_Table ),
              compare_offsets );

    /* Check offsets and lengths. */

    woff_offset = 44 + woff.num_tables * 20L;
    sfnt_offset = 12 + woff.num_tables * 16L;

    for ( nn = 0; nn < woff.num_tables; nn++ )
    {
      WOFF_Table  table = indices[nn];

      if ( table->Offset != woff_offset                         ||
           table->CompLength > woff.length                      ||
           table->Offset > woff.length - table->CompLength      ||
           table->OrigLength > woff.totalSfntSize               ||
           sfnt_offset > woff.totalSfntSize - table->OrigLength ||
           table->CompLength > table->OrigLength                )
      {
        FT_ERROR(( "woff_font_open: invalid table offsets\n" ));
        error = FT_THROW( Invalid_Table );
        goto Exit;
      }

      table->OrigOffset = sfnt_offset;

      /* The offsets must be multiples of 4. */
      woff_offset += ( table->CompLength + 3 ) & ~3U;
      sfnt_offset += ( table->OrigLength + 3 ) & ~3U;
    }

    /*
     * Final checks!
     *
     * We don't decode and check the metadata block.
     * We don't check table checksums either.
     * But other than those, I think we implement all
     * `MUST' checks from the spec.
     */

    if ( woff.metaOffset )
    {
      if ( woff.metaOffset != woff_offset                  ||
           woff.metaOffset + woff.metaLength > woff.length )
      {
        FT_ERROR(( "woff_font_open:"
                   " invalid `metadata' offset or length\n" ));
        error = FT_THROW( Invalid_Table );
        goto Exit;
      }

      /* We have padding only ... */
      woff_offset += woff.metaLength;
    }

    if ( woff.privOffset )
    {
      /* ... if it isn't the last block. */
      woff_offset = ( woff_offset + 3 ) & ~3U;

      if ( woff.privOffset != woff_offset                  ||
           woff.privOffset + woff.privLength > woff.length )
      {
        FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" ));
        error = FT_THROW( Invalid_Table );
        goto Exit;
      }

      /* No padding for the last block. */
      woff_offset += woff.privLength;
    }

    if ( sfnt_offset != woff.totalSfntSize ||
         woff_offset != woff.length        )
    {
      FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" ));
      error = FT_THROW( Invalid_Table );
      goto Exit;
    }

    /* Now use `totalSfntSize'. */
    if ( FT_REALLOC( sfnt,
                     12 + woff.num_tables * 16UL,
                     woff.totalSfntSize ) )
      goto Exit;

    sfnt_header = sfnt + 12;

    /* Write the tables. */

    for ( nn = 0; nn < woff.num_tables; nn++ )
    {
      WOFF_Table  table = tables + nn;

      /* Write SFNT table entry. */
      WRITE_ULONG( sfnt_header, table->Tag );
      WRITE_ULONG( sfnt_header, table->CheckSum );
      WRITE_ULONG( sfnt_header, table->OrigOffset );
      WRITE_ULONG( sfnt_header, table->OrigLength );

      /* Write table data. */
      if ( FT_STREAM_SEEK( table->Offset )     ||
           FT_FRAME_ENTER( table->CompLength ) )
        goto Exit;

      if ( table->CompLength == table->OrigLength )
      {
        /* Uncompressed data; just copy. */
        ft_memcpy( sfnt + table->OrigOffset,
                   stream->cursor,
                   table->OrigLength );
      }
      else
      {
#ifdef FT_CONFIG_OPTION_USE_ZLIB

        /* Uncompress with zlib. */
        FT_ULong  output_len = table->OrigLength;

        error = FT_Gzip_Uncompress( memory,
                                    sfnt + table->OrigOffset, &output_len,
                                    stream->cursor, table->CompLength );
        if ( error )
          goto Exit1;
        if ( output_len != table->OrigLength )
        {
          FT_ERROR(( "woff_font_open: compressed table length mismatch\n" ));
          error = FT_THROW( Invalid_Table );
          goto Exit1;
        }

#else /* !FT_CONFIG_OPTION_USE_ZLIB */

        error = FT_THROW( Unimplemented_Feature );
        goto Exit1;

#endif /* !FT_CONFIG_OPTION_USE_ZLIB */
      }

      FT_FRAME_EXIT();

      /* We don't check whether the padding bytes in the WOFF file are     */
      /* actually '\0'.  For the output, however, we do set them properly. */
      sfnt_offset = table->OrigOffset + table->OrigLength;
      while ( sfnt_offset & 3 )
      {
        sfnt[sfnt_offset] = '\0';
        sfnt_offset++;
      }
    }

    /* Ok!  Finally ready.  Swap out stream and return. */
    FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize );
    sfnt_stream->memory = stream->memory;
    sfnt_stream->close  = sfnt_stream_close;

    FT_Stream_Free(
      face->root.stream,
      ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 );

    face->root.stream = sfnt_stream;

    face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;

  Exit:
    FT_FREE( tables );
    FT_FREE( indices );

    if ( error )
    {
      FT_FREE( sfnt );
      FT_Stream_Close( sfnt_stream );
      FT_FREE( sfnt_stream );
    }

    return error;

  Exit1:
    FT_FRAME_EXIT();
    goto Exit;
  }

#undef WRITE_USHORT
#undef WRITE_ULONG

/* END */


text:
/****************************************************************************
 *
 * woff2tags.c
 *
 *   WOFF2 Font table tags (base).
 *
 * Copyright (C) 2019-2020 by
 * Nikhil Ramakrishnan, 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_TRUETYPE_TAGS_H

  /*
   * Return tag from index in the order given in WOFF2 specification.
   *
   * See
   *
   *       https://www.w3.org/TR/WOFF2/#table_dir_format
   *
   * for details.
   */
  FT_LOCAL_DEF( FT_ULong )
  woff2_known_tags( FT_Byte  index )
  {
    const FT_ULong  known_tags[63] =
    {
      FT_MAKE_TAG('c', 'm', 'a', 'p'),  /*  0  */
      FT_MAKE_TAG('h', 'e', 'a', 'd'),  /*  1  */
      FT_MAKE_TAG('h', 'h', 'e', 'a'),  /*  2  */
      FT_MAKE_TAG('h', 'm', 't', 'x'),  /*  3  */
      FT_MAKE_TAG('m', 'a', 'x', 'p'),  /*  4  */
      FT_MAKE_TAG('n', 'a', 'm', 'e'),  /*  5  */
      FT_MAKE_TAG('O', 'S', '/', '2'),  /*  6  */
      FT_MAKE_TAG('p', 'o', 's', 't'),  /*  7  */
      FT_MAKE_TAG('c', 'v', 't', ' '),  /*  8  */
      FT_MAKE_TAG('f', 'p', 'g', 'm'),  /*  9  */
      FT_MAKE_TAG('g', 'l', 'y', 'f'),  /*  10 */
      FT_MAKE_TAG('l', 'o', 'c', 'a'),  /*  11 */
      FT_MAKE_TAG('p', 'r', 'e', 'p'),  /*  12 */
      FT_MAKE_TAG('C', 'F', 'F', ' '),  /*  13 */
      FT_MAKE_TAG('V', 'O', 'R', 'G'),  /*  14 */
      FT_MAKE_TAG('E', 'B', 'D', 'T'),  /*  15 */
      FT_MAKE_TAG('E', 'B', 'L', 'C'),  /*  16 */
      FT_MAKE_TAG('g', 'a', 's', 'p'),  /*  17 */
      FT_MAKE_TAG('h', 'd', 'm', 'x'),  /*  18 */
      FT_MAKE_TAG('k', 'e', 'r', 'n'),  /*  19 */
      FT_MAKE_TAG('L', 'T', 'S', 'H'),  /*  20 */
      FT_MAKE_TAG('P', 'C', 'L', 'T'),  /*  21 */
      FT_MAKE_TAG('V', 'D', 'M', 'X'),  /*  22 */
      FT_MAKE_TAG('v', 'h', 'e', 'a'),  /*  23 */
      FT_MAKE_TAG('v', 'm', 't', 'x'),  /*  24 */
      FT_MAKE_TAG('B', 'A', 'S', 'E'),  /*  25 */
      FT_MAKE_TAG('G', 'D', 'E', 'F'),  /*  26 */
      FT_MAKE_TAG('G', 'P', 'O', 'S'),  /*  27 */
      FT_MAKE_TAG('G', 'S', 'U', 'B'),  /*  28 */
      FT_MAKE_TAG('E', 'B', 'S', 'C'),  /*  29 */
      FT_MAKE_TAG('J', 'S', 'T', 'F'),  /*  30 */
      FT_MAKE_TAG('M', 'A', 'T', 'H'),  /*  31 */
      FT_MAKE_TAG('C', 'B', 'D', 'T'),  /*  32 */
      FT_MAKE_TAG('C', 'B', 'L', 'C'),  /*  33 */
      FT_MAKE_TAG('C', 'O', 'L', 'R'),  /*  34 */
      FT_MAKE_TAG('C', 'P', 'A', 'L'),  /*  35 */
      FT_MAKE_TAG('S', 'V', 'G', ' '),  /*  36 */
      FT_MAKE_TAG('s', 'b', 'i', 'x'),  /*  37 */
      FT_MAKE_TAG('a', 'c', 'n', 't'),  /*  38 */
      FT_MAKE_TAG('a', 'v', 'a', 'r'),  /*  39 */
      FT_MAKE_TAG('b', 'd', 'a', 't'),  /*  40 */
      FT_MAKE_TAG('b', 'l', 'o', 'c'),  /*  41 */
      FT_MAKE_TAG('b', 's', 'l', 'n'),  /*  42 */
      FT_MAKE_TAG('c', 'v', 'a', 'r'),  /*  43 */
      FT_MAKE_TAG('f', 'd', 's', 'c'),  /*  44 */
      FT_MAKE_TAG('f', 'e', 'a', 't'),  /*  45 */
      FT_MAKE_TAG('f', 'm', 't', 'x'),  /*  46 */
      FT_MAKE_TAG('f', 'v', 'a', 'r'),  /*  47 */
      FT_MAKE_TAG('g', 'v', 'a', 'r'),  /*  48 */
      FT_MAKE_TAG('h', 's', 't', 'y'),  /*  49 */
      FT_MAKE_TAG('j', 'u', 's', 't'),  /*  50 */
      FT_MAKE_TAG('l', 'c', 'a', 'r'),  /*  51 */
      FT_MAKE_TAG('m', 'o', 'r', 't'),  /*  52 */
      FT_MAKE_TAG('m', 'o', 'r', 'x'),  /*  53 */
      FT_MAKE_TAG('o', 'p', 'b', 'd'),  /*  54 */
      FT_MAKE_TAG('p', 'r', 'o', 'p'),  /*  55 */
      FT_MAKE_TAG('t', 'r', 'a', 'k'),  /*  56 */
      FT_MAKE_TAG('Z', 'a', 'p', 'f'),  /*  57 */
      FT_MAKE_TAG('S', 'i', 'l', 'f'),  /*  58 */
      FT_MAKE_TAG('G', 'l', 'a', 't'),  /*  59 */
      FT_MAKE_TAG('G', 'l', 'o', 'c'),  /*  60 */
      FT_MAKE_TAG('F', 'e', 'a', 't'),  /*  61 */
      FT_MAKE_TAG('S', 'i', 'l', 'l'),  /*  62 */
    };

    if ( index > 62 )
      return 0;

    return known_tags[index];
  }

/* END */


text:
/****************************************************************************
 *
 * ttkern.h
 *
 *   Load the basic TrueType kerning table.  This doesn't handle
 *   kerning data within the GPOS table at the moment.
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef TTKERN_H_
#define TTKERN_H_

#include <ft2build.h>
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_TRUETYPE_TYPES_H

FT_BEGIN_HEADER

  FT_LOCAL( FT_Error  )
  tt_face_load_kern( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( void )
  tt_face_done_kern( TT_Face  face );

  FT_LOCAL( FT_Int )
  tt_face_get_kerning( TT_Face     face,
                       FT_UInt     left_glyph,
                       FT_UInt     right_glyph );

#define TT_FACE_HAS_KERNING( face )  ( (face)->kern_avail_bits != 0 )

FT_END_HEADER

#endif /* TTKERN_H_ */

/* END */


text:
/****************************************************************************
 *
 * ttload.h
 *
 *   Load the basic TrueType tables, i.e., tables that can be either in
 *   TTF or OTF fonts (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef TTLOAD_H_
#define TTLOAD_H_

#include <ft2build.h>
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_TRUETYPE_TYPES_H

FT_BEGIN_HEADER

  FT_LOCAL( TT_Table  )
  tt_face_lookup_table( TT_Face   face,
                        FT_ULong  tag );

  FT_LOCAL( FT_Error )
  tt_face_goto_table( TT_Face    face,
                      FT_ULong   tag,
                      FT_Stream  stream,
                      FT_ULong*  length );

  FT_LOCAL( FT_Error )
  tt_face_load_font_dir( TT_Face    face,
                         FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_any( TT_Face    face,
                    FT_ULong   tag,
                    FT_Long    offset,
                    FT_Byte*   buffer,
                    FT_ULong*  length );

  FT_LOCAL( FT_Error )
  tt_face_load_head( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_cmap( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_maxp( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_name( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_os2( TT_Face    face,
                    FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_post( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_pclt( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( void )
  tt_face_free_name( TT_Face  face );

  FT_LOCAL( FT_Error )
  tt_face_load_gasp( TT_Face    face,
                     FT_Stream  stream );

#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS

  FT_LOCAL( FT_Error )
  tt_face_load_bhed( TT_Face    face,
                     FT_Stream  stream );

#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */

FT_END_HEADER

#endif /* TTLOAD_H_ */

/* END */


text:
/****************************************************************************
 *
 * ttload.c
 *
 *   Load the basic TrueType tables, i.e., tables that can be either in
 *   TTF or OTF fonts (body).
 *
 * Copyright (C) 1996-2020 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_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_TAGS_H
#include "ttload.h"

#include "sferrors.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  ttload

  /**************************************************************************
   *
   * @Function:
   *   tt_face_lookup_table
   *
   * @Description:
   *   Looks for a TrueType table by name.
   *
   * @Input:
   *   face ::
   *     A face object handle.
   *
   *   tag ::
   *     The searched tag.
   *
   * @Return:
   *   A pointer to the table directory entry.  0 if not found.
   */
  FT_LOCAL_DEF( TT_Table  )
  tt_face_lookup_table( TT_Face   face,
                        FT_ULong  tag  )
  {
    TT_Table  entry;
    TT_Table  limit;
#ifdef FT_DEBUG_LEVEL_TRACE
    FT_Bool   zero_length = FALSE;
#endif

    FT_TRACE4(( "tt_face_lookup_table: %08p, `%c%c%c%c' -- ",
                face,
                (FT_Char)( tag >> 24 ),
                (FT_Char)( tag >> 16 ),
                (FT_Char)( tag >> 8  ),
                (FT_Char)( tag       ) ));

    entry = face->dir_tables;
    limit = entry + face->num_tables;

    for ( ; entry < limit; entry++ )
    {
      /* For compatibility with Windows, we consider    */
      /* zero-length tables the same as missing tables. */
      if ( entry->Tag == tag )
      {
        if ( entry->Length != 0 )
        {
          FT_TRACE4(( "found table.\n" ));
          return entry;
        }
#ifdef FT_DEBUG_LEVEL_TRACE
        zero_length = TRUE;
#endif
      }
    }

#ifdef FT_DEBUG_LEVEL_TRACE
    if ( zero_length )
      FT_TRACE4(( "ignoring empty table\n" ));
    else
      FT_TRACE4(( "could not find table\n" ));
#endif

    return NULL;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_goto_table
   *
   * @Description:
   *   Looks for a TrueType table by name, then seek a stream to it.
   *
   * @Input:
   *   face ::
   *     A face object handle.
   *
   *   tag ::
   *     The searched tag.
   *
   *   stream ::
   *     The stream to seek when the table is found.
   *
   * @Output:
   *   length ::
   *     The length of the table if found, undefined otherwise.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_goto_table( TT_Face    face,
                      FT_ULong   tag,
                      FT_Stream  stream,
                      FT_ULong*  length )
  {
    TT_Table  table;
    FT_Error  error;

    table = tt_face_lookup_table( face, tag );
    if ( table )
    {
      if ( length )
        *length = table->Length;

      if ( FT_STREAM_SEEK( table->Offset ) )
        goto Exit;
    }
    else
      error = FT_THROW( Table_Missing );

  Exit:
    return error;
  }

  /* Here, we                                                         */
  /*                                                                  */
  /* - check that `num_tables' is valid (and adjust it if necessary); */
  /*   also return the number of valid table entries                  */
  /*                                                                  */
  /* - look for a `head' table, check its size, and parse it to check */
  /*   whether its `magic' field is correctly set                     */
  /*                                                                  */
  /* - errors (except errors returned by stream handling)             */
  /*                                                                  */
  /*     SFNT_Err_Unknown_File_Format:                                */
  /*       no table is defined in directory, it is not sfnt-wrapped   */
  /*       data                                                       */
  /*     SFNT_Err_Table_Missing:                                      */
  /*       table directory is valid, but essential tables             */
  /*       (head/bhed/SING) are missing                               */
  /*                                                                  */
  static FT_Error
  check_table_dir( SFNT_Header  sfnt,
                   FT_Stream    stream,
                   FT_UShort*   valid )
  {
    FT_Error   error;
    FT_UShort  nn, valid_entries = 0;
    FT_UInt    has_head = 0, has_sing = 0, has_meta = 0;
    FT_ULong   offset = sfnt->offset + 12;

    static const FT_Frame_Field  table_dir_entry_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_TableRec

      FT_FRAME_START( 16 ),
        FT_FRAME_ULONG( Tag ),
        FT_FRAME_ULONG( CheckSum ),
        FT_FRAME_ULONG( Offset ),
        FT_FRAME_ULONG( Length ),
      FT_FRAME_END
    };

    if ( FT_STREAM_SEEK( offset ) )
      goto Exit;

    for ( nn = 0; nn < sfnt->num_tables; nn++ )
    {
      TT_TableRec  table;

      if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) )
      {
        nn--;
        FT_TRACE2(( "check_table_dir:"
                    " can read only %d table%s in font (instead of %d)\n",
                    nn, nn == 1 ? "" : "s", sfnt->num_tables ));
        sfnt->num_tables = nn;
        break;
      }

      /* we ignore invalid tables */

      if ( table.Offset > stream->size )
      {
        FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn ));
        continue;
      }
      else if ( table.Length > stream->size - table.Offset )
      {
        /* Some tables have such a simple structure that clipping its     */
        /* contents is harmless.  This also makes FreeType less sensitive */
        /* to invalid table lengths (which programs like Acroread seem to */
        /* ignore in general).                                            */

        if ( table.Tag == TTAG_hmtx ||
             table.Tag == TTAG_vmtx )
          valid_entries++;
        else
        {
          FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn ));
          continue;
        }
      }
      else
        valid_entries++;

      if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed )
      {
        FT_UInt32  magic;

#ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
        if ( table.Tag == TTAG_head )
#endif
          has_head = 1;

        /*
         * The table length should be 0x36, but certain font tools make it
         * 0x38, so we will just check that it is greater.
         *
         * Note that according to the specification, the table must be
         * padded to 32-bit lengths, but this doesn't apply to the value of
         * its `Length' field!
         *
         */
        if ( table.Length < 0x36 )
        {
          FT_TRACE2(( "check_table_dir:"
                      " `head' or `bhed' table too small\n" ));
          error = FT_THROW( Table_Missing );
          goto Exit;
        }

        if ( FT_STREAM_SEEK( table.Offset + 12 ) ||
             FT_READ_ULONG( magic )              )
          goto Exit;

        if ( magic != 0x5F0F3CF5UL )
          FT_TRACE2(( "check_table_dir:"
                      " invalid magic number in `head' or `bhed' table\n"));

        if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) )
          goto Exit;
      }
      else if ( table.Tag == TTAG_SING )
        has_sing = 1;
      else if ( table.Tag == TTAG_META )
        has_meta = 1;
    }

    *valid = valid_entries;

    if ( !valid_entries )
    {
      FT_TRACE2(( "check_table_dir: no valid tables found\n" ));
      error = FT_THROW( Unknown_File_Format );
      goto Exit;
    }

    /* if `sing' and `meta' tables are present, there is no `head' table */
    if ( has_head || ( has_sing && has_meta ) )
    {
      error = FT_Err_Ok;
      goto Exit;
    }
    else
    {
      FT_TRACE2(( "check_table_dir:" ));
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
      FT_TRACE2(( " neither `head', `bhed', nor `sing' table found\n" ));
#else
      FT_TRACE2(( " neither `head' nor `sing' table found\n" ));
#endif
      error = FT_THROW( Table_Missing );
    }

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_font_dir
   *
   * @Description:
   *   Loads the header of a SFNT font file.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     The input stream.
   *
   * @Output:
   *   sfnt ::
   *     The SFNT header.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   *
   * @Note:
   *   The stream cursor must be at the beginning of the font directory.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_font_dir( TT_Face    face,
                         FT_Stream  stream )
  {
    SFNT_HeaderRec  sfnt;
    FT_Error        error;
    FT_Memory       memory = stream->memory;
    FT_UShort       nn, valid_entries = 0;

    static const FT_Frame_Field  offset_table_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  SFNT_HeaderRec

      FT_FRAME_START( 8 ),
        FT_FRAME_USHORT( num_tables ),
        FT_FRAME_USHORT( search_range ),
        FT_FRAME_USHORT( entry_selector ),
        FT_FRAME_USHORT( range_shift ),
      FT_FRAME_END
    };

    FT_TRACE2(( "tt_face_load_font_dir: %08p\n", face ));

    /* read the offset table */

    sfnt.offset = FT_STREAM_POS();

    if ( FT_READ_ULONG( sfnt.format_tag )                    ||
         FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) )
      goto Exit;

    /* many fonts don't have these fields set correctly */
#if 0
    if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 )        ||
         sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 )
      return FT_THROW( Unknown_File_Format );
#endif

    /* load the table directory */

    FT_TRACE2(( "-- Number of tables: %10u\n",    sfnt.num_tables ));
    FT_TRACE2(( "-- Format version:   0x%08lx\n", sfnt.format_tag ));

    if ( sfnt.format_tag != TTAG_OTTO )
    {
      /* check first */
      error = check_table_dir( &sfnt, stream, &valid_entries );
      if ( error )
      {
        FT_TRACE2(( "tt_face_load_font_dir:"
                    " invalid table directory for TrueType\n" ));
        goto Exit;
      }
    }
    else
    {
      valid_entries = sfnt.num_tables;
      if ( !valid_entries )
      {
        FT_TRACE2(( "tt_face_load_font_dir: no valid tables found\n" ));
        error = FT_THROW( Unknown_File_Format );
        goto Exit;
      }
    }

    face->num_tables = valid_entries;
    face->format_tag = sfnt.format_tag;

    if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) )
      goto Exit;

    if ( FT_STREAM_SEEK( sfnt.offset + 12 )      ||
         FT_FRAME_ENTER( sfnt.num_tables * 16L ) )
      goto Exit;

    FT_TRACE2(( "\n"
                "  tag    offset    length   checksum\n"
                "  ----------------------------------\n" ));

    valid_entries = 0;
    for ( nn = 0; nn < sfnt.num_tables; nn++ )
    {
      TT_TableRec  entry;
      FT_UShort    i;
      FT_Bool      duplicate;

      entry.Tag      = FT_GET_TAG4();
      entry.CheckSum = FT_GET_ULONG();
      entry.Offset   = FT_GET_ULONG();
      entry.Length   = FT_GET_ULONG();

      /* ignore invalid tables that can't be sanitized */

      if ( entry.Offset > stream->size )
        continue;
      else if ( entry.Length > stream->size - entry.Offset )
      {
        if ( entry.Tag == TTAG_hmtx ||
             entry.Tag == TTAG_vmtx )
        {
#ifdef FT_DEBUG_LEVEL_TRACE
          FT_ULong  old_length = entry.Length;
#endif

          /* make metrics table length a multiple of 4 */
          entry.Length = ( stream->size - entry.Offset ) & ~3U;

          FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx"
                      " (sanitized; original length %08lx)",
                      (FT_Char)( entry.Tag >> 24 ),
                      (FT_Char)( entry.Tag >> 16 ),
                      (FT_Char)( entry.Tag >> 8  ),
                      (FT_Char)( entry.Tag       ),
                      entry.Offset,
                      entry.Length,
                      entry.CheckSum,
                      old_length ));
        }
        else
          continue;
      }
#ifdef FT_DEBUG_LEVEL_TRACE
      else
        FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx",
                    (FT_Char)( entry.Tag >> 24 ),
                    (FT_Char)( entry.Tag >> 16 ),
                    (FT_Char)( entry.Tag >> 8  ),
                    (FT_Char)( entry.Tag       ),
                    entry.Offset,
                    entry.Length,
                    entry.CheckSum ));
#endif

      /* ignore duplicate tables – the first one wins */
      duplicate = 0;
      for ( i = 0; i < valid_entries; i++ )
      {
        if ( face->dir_tables[i].Tag == entry.Tag )
        {
          duplicate = 1;
          break;
        }
      }
      if ( duplicate )
      {
        FT_TRACE2(( "  (duplicate, ignored)\n" ));
        continue;
      }
      else
      {
        FT_TRACE2(( "\n" ));

        /* we finally have a valid entry */
        face->dir_tables[valid_entries++] = entry;
      }
    }

    /* final adjustment to number of tables */
    face->num_tables = valid_entries;

    FT_FRAME_EXIT();

    FT_TRACE2(( "table directory loaded\n\n" ));

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_any
   *
   * @Description:
   *   Loads any font table into client memory.
   *
   * @Input:
   *   face ::
   *     The face object to look for.
   *
   *   tag ::
   *     The tag of table to load.  Use the value 0 if you want
   *     to access the whole font file, else set this parameter
   *     to a valid TrueType table tag that you can forge with
   *     the MAKE_TT_TAG macro.
   *
   *   offset ::
   *     The starting offset in the table (or the file if
   *     tag == 0).
   *
   *   length ::
   *     The address of the decision variable:
   *
   *     If length == NULL:
   *       Loads the whole table.  Returns an error if
   *       `offset' == 0!
   *
   *     If *length == 0:
   *       Exits immediately; returning the length of the given
   *       table or of the font file, depending on the value of
   *       `tag'.
   *
   *     If *length != 0:
   *       Loads the next `length' bytes of table or font,
   *       starting at offset `offset' (in table or font too).
   *
   * @Output:
   *   buffer ::
   *     The address of target buffer.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_any( TT_Face    face,
                    FT_ULong   tag,
                    FT_Long    offset,
                    FT_Byte*   buffer,
                    FT_ULong*  length )
  {
    FT_Error   error;
    FT_Stream  stream;
    TT_Table   table;
    FT_ULong   size;

    if ( tag != 0 )
    {
      /* look for tag in font directory */
      table = tt_face_lookup_table( face, tag );
      if ( !table )
      {
        error = FT_THROW( Table_Missing );
        goto Exit;
      }

      offset += table->Offset;
      size    = table->Length;
    }
    else
      /* tag == 0 -- the user wants to access the font file directly */
      size = face->root.stream->size;

    if ( length && *length == 0 )
    {
      *length = size;

      return FT_Err_Ok;
    }

    if ( length )
      size = *length;

    stream = face->root.stream;
    /* the `if' is syntactic sugar for picky compilers */
    if ( FT_STREAM_READ_AT( offset, buffer, size ) )
      goto Exit;

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_generic_header
   *
   * @Description:
   *   Loads the TrueType table `head' or `bhed'.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     The input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  static FT_Error
  tt_face_load_generic_header( TT_Face    face,
                               FT_Stream  stream,
                               FT_ULong   tag )
  {
    FT_Error    error;
    TT_Header*  header;

    static const FT_Frame_Field  header_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_Header

      FT_FRAME_START( 54 ),
        FT_FRAME_ULONG ( Table_Version ),
        FT_FRAME_ULONG ( Font_Revision ),
        FT_FRAME_LONG  ( CheckSum_Adjust ),
        FT_FRAME_LONG  ( Magic_Number ),
        FT_FRAME_USHORT( Flags ),
        FT_FRAME_USHORT( Units_Per_EM ),
        FT_FRAME_ULONG ( Created[0] ),
        FT_FRAME_ULONG ( Created[1] ),
        FT_FRAME_ULONG ( Modified[0] ),
        FT_FRAME_ULONG ( Modified[1] ),
        FT_FRAME_SHORT ( xMin ),
        FT_FRAME_SHORT ( yMin ),
        FT_FRAME_SHORT ( xMax ),
        FT_FRAME_SHORT ( yMax ),
        FT_FRAME_USHORT( Mac_Style ),
        FT_FRAME_USHORT( Lowest_Rec_PPEM ),
        FT_FRAME_SHORT ( Font_Direction ),
        FT_FRAME_SHORT ( Index_To_Loc_Format ),
        FT_FRAME_SHORT ( Glyph_Data_Format ),
      FT_FRAME_END
    };

    error = face->goto_table( face, tag, stream, 0 );
    if ( error )
      goto Exit;

    header = &face->header;

    if ( FT_STREAM_READ_FIELDS( header_fields, header ) )
      goto Exit;

    FT_TRACE3(( "Units per EM: %4u\n", header->Units_Per_EM ));
    FT_TRACE3(( "IndexToLoc:   %4d\n", header->Index_To_Loc_Format ));

  Exit:
    return error;
  }

  FT_LOCAL_DEF( FT_Error )
  tt_face_load_head( TT_Face    face,
                     FT_Stream  stream )
  {
    return tt_face_load_generic_header( face, stream, TTAG_head );
  }

#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS

  FT_LOCAL_DEF( FT_Error )
  tt_face_load_bhed( TT_Face    face,
                     FT_Stream  stream )
  {
    return tt_face_load_generic_header( face, stream, TTAG_bhed );
  }

#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_maxp
   *
   * @Description:
   *   Loads the maximum profile into a face object.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     The input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_maxp( TT_Face    face,
                     FT_Stream  stream )
  {
    FT_Error        error;
    TT_MaxProfile*  maxProfile = &face->max_profile;

    static const FT_Frame_Field  maxp_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_MaxProfile

      FT_FRAME_START( 6 ),
        FT_FRAME_LONG  ( version ),
        FT_FRAME_USHORT( numGlyphs ),
      FT_FRAME_END
    };

    static const FT_Frame_Field  maxp_fields_extra[] =
    {
      FT_FRAME_START( 26 ),
        FT_FRAME_USHORT( maxPoints ),
        FT_FRAME_USHORT( maxContours ),
        FT_FRAME_USHORT( maxCompositePoints ),
        FT_FRAME_USHORT( maxCompositeContours ),
        FT_FRAME_USHORT( maxZones ),
        FT_FRAME_USHORT( maxTwilightPoints ),
        FT_FRAME_USHORT( maxStorage ),
        FT_FRAME_USHORT( maxFunctionDefs ),
        FT_FRAME_USHORT( maxInstructionDefs ),
        FT_FRAME_USHORT( maxStackElements ),
        FT_FRAME_USHORT( maxSizeOfInstructions ),
        FT_FRAME_USHORT( maxComponentElements ),
        FT_FRAME_USHORT( maxComponentDepth ),
      FT_FRAME_END
    };

    error = face->goto_table( face, TTAG_maxp, stream, 0 );
    if ( error )
      goto Exit;

    if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) )
      goto Exit;

    maxProfile->maxPoints             = 0;
    maxProfile->maxContours           = 0;
    maxProfile->maxCompositePoints    = 0;
    maxProfile->maxCompositeContours  = 0;
    maxProfile->maxZones              = 0;
    maxProfile->maxTwilightPoints     = 0;
    maxProfile->maxStorage            = 0;
    maxProfile->maxFunctionDefs       = 0;
    maxProfile->maxInstructionDefs    = 0;
    maxProfile->maxStackElements      = 0;
    maxProfile->maxSizeOfInstructions = 0;
    maxProfile->maxComponentElements  = 0;
    maxProfile->maxComponentDepth     = 0;

    if ( maxProfile->version >= 0x10000L )
    {
      if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) )
        goto Exit;

      /* XXX: an adjustment that is necessary to load certain */
      /*      broken fonts like `Keystrokes MT' :-(           */
      /*                                                      */
      /*   We allocate 64 function entries by default when    */
      /*   the maxFunctionDefs value is smaller.              */

      if ( maxProfile->maxFunctionDefs < 64 )
        maxProfile->maxFunctionDefs = 64;

      /* we add 4 phantom points later */
      if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) )
      {
        FT_TRACE0(( "tt_face_load_maxp:"
                    " too much twilight points in `maxp' table;\n"
                    "                  "
                    " some glyphs might be rendered incorrectly\n" ));

        maxProfile->maxTwilightPoints = 0xFFFFU - 4;
      }
    }

    FT_TRACE3(( "numGlyphs: %u\n", maxProfile->numGlyphs ));

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_name
   *
   * @Description:
   *   Loads the name records.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     The input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_name( TT_Face    face,
                     FT_Stream  stream )
  {
    FT_Error      error;
    FT_Memory     memory = stream->memory;
    FT_ULong      table_pos, table_len;
    FT_ULong      storage_start, storage_limit;
    TT_NameTable  table;

    static const FT_Frame_Field  name_table_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_NameTableRec

      FT_FRAME_START( 6 ),
        FT_FRAME_USHORT( format ),
        FT_FRAME_USHORT( numNameRecords ),
        FT_FRAME_USHORT( storageOffset ),
      FT_FRAME_END
    };

    static const FT_Frame_Field  name_record_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_NameRec

      /* no FT_FRAME_START */
        FT_FRAME_USHORT( platformID ),
        FT_FRAME_USHORT( encodingID ),
        FT_FRAME_USHORT( languageID ),
        FT_FRAME_USHORT( nameID ),
        FT_FRAME_USHORT( stringLength ),
        FT_FRAME_USHORT( stringOffset ),
      FT_FRAME_END
    };

    static const FT_Frame_Field  langTag_record_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_LangTagRec

      /* no FT_FRAME_START */
        FT_FRAME_USHORT( stringLength ),
        FT_FRAME_USHORT( stringOffset ),
      FT_FRAME_END
    };

    table         = &face->name_table;
    table->stream = stream;

    error = face->goto_table( face, TTAG_name, stream, &table_len );
    if ( error )
      goto Exit;

    table_pos = FT_STREAM_POS();

    if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
      goto Exit;

    /* Some popular Asian fonts have an invalid `storageOffset' value (it */
    /* should be at least `6 + 12*numNameRecords').  However, the string  */
    /* offsets, computed as `storageOffset + entry->stringOffset', are    */
    /* valid pointers within the name table...                            */
    /*                                                                    */
    /* We thus can't check `storageOffset' right now.                     */
    /*                                                                    */
    storage_start = table_pos + 6 + 12 * table->numNameRecords;
    storage_limit = table_pos + table_len;

    if ( storage_start > storage_limit )
    {
      FT_ERROR(( "tt_face_load_name: invalid `name' table\n" ));
      error = FT_THROW( Name_Table_Missing );
      goto Exit;
    }

    /* `name' format 1 contains additional language tag records, */
    /* which we load first                                       */
    if ( table->format == 1 )
    {
      if ( FT_STREAM_SEEK( storage_start )            ||
           FT_READ_USHORT( table->numLangTagRecords ) )
        goto Exit;

      storage_start += 2 + 4 * table->numLangTagRecords;

      /* allocate language tag records array */
      if ( FT_NEW_ARRAY( table->langTags, table->numLangTagRecords ) ||
           FT_FRAME_ENTER( table->numLangTagRecords * 4 )            )
        goto Exit;

      /* load language tags */
      {
        TT_LangTag  entry = table->langTags;
        TT_LangTag  limit = FT_OFFSET( entry, table->numLangTagRecords );

        for ( ; entry < limit; entry++ )
        {
          (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );

          /* check that the langTag string is within the table */
          entry->stringOffset += table_pos + table->storageOffset;
          if ( entry->stringOffset                       < storage_start ||
               entry->stringOffset + entry->stringLength > storage_limit )
          {
            /* invalid entry; ignore it */
            entry->stringLength = 0;
          }
        }
      }

      FT_FRAME_EXIT();

      (void)FT_STREAM_SEEK( table_pos + 6 );
    }

    /* allocate name records array */
    if ( FT_NEW_ARRAY( table->names, table->numNameRecords ) ||
         FT_FRAME_ENTER( table->numNameRecords * 12 )        )
      goto Exit;

    /* load name records */
    {
      TT_Name  entry = table->names;
      FT_UInt  count = table->numNameRecords;

      for ( ; count > 0; count-- )
      {
        if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
          continue;

        /* check that the name is not empty */
        if ( entry->stringLength == 0 )
          continue;

        /* check that the name string is within the table */
        entry->stringOffset += table_pos + table->storageOffset;
        if ( entry->stringOffset                       < storage_start ||
             entry->stringOffset + entry->stringLength > storage_limit )
        {
          /* invalid entry; ignore it */
          continue;
        }

        /* assure that we have a valid language tag ID, and   */
        /* that the corresponding langTag entry is valid, too */
        if ( table->format == 1 && entry->languageID >= 0x8000U )
        {
          if ( entry->languageID - 0x8000U >= table->numLangTagRecords    ||
               !table->langTags[entry->languageID - 0x8000U].stringLength )
          {
            /* invalid entry; ignore it */
            continue;
          }
        }

        entry++;
      }

      /* reduce array size to the actually used elements */
      count = (FT_UInt)( entry - table->names );
      (void)FT_RENEW_ARRAY( table->names,
                            table->numNameRecords,
                            count );
      table->numNameRecords = count;
    }

    FT_FRAME_EXIT();

    /* everything went well, update face->num_names */
    face->num_names = (FT_UShort)table->numNameRecords;

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_free_name
   *
   * @Description:
   *   Frees the name records.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   */
  FT_LOCAL_DEF( void )
  tt_face_free_name( TT_Face  face )
  {
    FT_Memory     memory = face->root.driver->root.memory;
    TT_NameTable  table  = &face->name_table;

    if ( table->names )
    {
      TT_Name  entry = table->names;
      TT_Name  limit = entry + table->numNameRecords;

      for ( ; entry < limit; entry++ )
        FT_FREE( entry->string );

      FT_FREE( table->names );
    }

    if ( table->langTags )
    {
      TT_LangTag  entry = table->langTags;
      TT_LangTag  limit = entry + table->numLangTagRecords;

      for ( ; entry < limit; entry++ )
        FT_FREE( entry->string );

      FT_FREE( table->langTags );
    }

    table->numNameRecords    = 0;
    table->numLangTagRecords = 0;
    table->format            = 0;
    table->storageOffset     = 0;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_cmap
   *
   * @Description:
   *   Loads the cmap directory in a face object.  The cmaps themselves
   *   are loaded on demand in the `ttcmap.c' module.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     A handle to the input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */

  FT_LOCAL_DEF( FT_Error )
  tt_face_load_cmap( TT_Face    face,
                     FT_Stream  stream )
  {
    FT_Error  error;

    error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
    if ( error )
      goto Exit;

    if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
      face->cmap_size = 0;

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_os2
   *
   * @Description:
   *   Loads the OS2 table.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     A handle to the input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_os2( TT_Face    face,
                    FT_Stream  stream )
  {
    FT_Error  error;
    TT_OS2*   os2;

    static const FT_Frame_Field  os2_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_OS2

      FT_FRAME_START( 78 ),
        FT_FRAME_USHORT( version ),
        FT_FRAME_SHORT ( xAvgCharWidth ),
        FT_FRAME_USHORT( usWeightClass ),
        FT_FRAME_USHORT( usWidthClass ),
        FT_FRAME_SHORT ( fsType ),
        FT_FRAME_SHORT ( ySubscriptXSize ),
        FT_FRAME_SHORT ( ySubscriptYSize ),
        FT_FRAME_SHORT ( ySubscriptXOffset ),
        FT_FRAME_SHORT ( ySubscriptYOffset ),
        FT_FRAME_SHORT ( ySuperscriptXSize ),
        FT_FRAME_SHORT ( ySuperscriptYSize ),
        FT_FRAME_SHORT ( ySuperscriptXOffset ),
        FT_FRAME_SHORT ( ySuperscriptYOffset ),
        FT_FRAME_SHORT ( yStrikeoutSize ),
        FT_FRAME_SHORT ( yStrikeoutPosition ),
        FT_FRAME_SHORT ( sFamilyClass ),
        FT_FRAME_BYTE  ( panose[0] ),
        FT_FRAME_BYTE  ( panose[1] ),
        FT_FRAME_BYTE  ( panose[2] ),
        FT_FRAME_BYTE  ( panose[3] ),
        FT_FRAME_BYTE  ( panose[4] ),
        FT_FRAME_BYTE  ( panose[5] ),
        FT_FRAME_BYTE  ( panose[6] ),
        FT_FRAME_BYTE  ( panose[7] ),
        FT_FRAME_BYTE  ( panose[8] ),
        FT_FRAME_BYTE  ( panose[9] ),
        FT_FRAME_ULONG ( ulUnicodeRange1 ),
        FT_FRAME_ULONG ( ulUnicodeRange2 ),
        FT_FRAME_ULONG ( ulUnicodeRange3 ),
        FT_FRAME_ULONG ( ulUnicodeRange4 ),
        FT_FRAME_BYTE  ( achVendID[0] ),
        FT_FRAME_BYTE  ( achVendID[1] ),
        FT_FRAME_BYTE  ( achVendID[2] ),
        FT_FRAME_BYTE  ( achVendID[3] ),

        FT_FRAME_USHORT( fsSelection ),
        FT_FRAME_USHORT( usFirstCharIndex ),
        FT_FRAME_USHORT( usLastCharIndex ),
        FT_FRAME_SHORT ( sTypoAscender ),
        FT_FRAME_SHORT ( sTypoDescender ),
        FT_FRAME_SHORT ( sTypoLineGap ),
        FT_FRAME_USHORT( usWinAscent ),
        FT_FRAME_USHORT( usWinDescent ),
      FT_FRAME_END
    };

    /* `OS/2' version 1 and newer */
    static const FT_Frame_Field  os2_fields_extra1[] =
    {
      FT_FRAME_START( 8 ),
        FT_FRAME_ULONG( ulCodePageRange1 ),
        FT_FRAME_ULONG( ulCodePageRange2 ),
      FT_FRAME_END
    };

    /* `OS/2' version 2 and newer */
    static const FT_Frame_Field  os2_fields_extra2[] =
    {
      FT_FRAME_START( 10 ),
        FT_FRAME_SHORT ( sxHeight ),
        FT_FRAME_SHORT ( sCapHeight ),
        FT_FRAME_USHORT( usDefaultChar ),
        FT_FRAME_USHORT( usBreakChar ),
        FT_FRAME_USHORT( usMaxContext ),
      FT_FRAME_END
    };

    /* `OS/2' version 5 and newer */
    static const FT_Frame_Field  os2_fields_extra5[] =
    {
      FT_FRAME_START( 4 ),
        FT_FRAME_USHORT( usLowerOpticalPointSize ),
        FT_FRAME_USHORT( usUpperOpticalPointSize ),
      FT_FRAME_END
    };

    /* We now support old Mac fonts where the OS/2 table doesn't  */
    /* exist.  Simply put, we set the `version' field to 0xFFFF   */
    /* and test this value each time we need to access the table. */
    error = face->goto_table( face, TTAG_OS2, stream, 0 );
    if ( error )
      goto Exit;

    os2 = &face->os2;

    if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
      goto Exit;

    os2->ulCodePageRange1        = 0;
    os2->ulCodePageRange2        = 0;
    os2->sxHeight                = 0;
    os2->sCapHeight              = 0;
    os2->usDefaultChar           = 0;
    os2->usBreakChar             = 0;
    os2->usMaxContext            = 0;
    os2->usLowerOpticalPointSize = 0;
    os2->usUpperOpticalPointSize = 0xFFFF;

    if ( os2->version >= 0x0001 )
    {
      /* only version 1 tables */
      if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) )
        goto Exit;

      if ( os2->version >= 0x0002 )
      {
        /* only version 2 tables */
        if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
          goto Exit;

        if ( os2->version >= 0x0005 )
        {
          /* only version 5 tables */
          if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) )
            goto Exit;
        }
      }
    }

    FT_TRACE3(( "sTypoAscender:  %4d\n",   os2->sTypoAscender ));
    FT_TRACE3(( "sTypoDescender: %4d\n",   os2->sTypoDescender ));
    FT_TRACE3(( "usWinAscent:    %4u\n",   os2->usWinAscent ));
    FT_TRACE3(( "usWinDescent:   %4u\n",   os2->usWinDescent ));
    FT_TRACE3(( "fsSelection:    0x%2x\n", os2->fsSelection ));

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_postscript
   *
   * @Description:
   *   Loads the Postscript table.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     A handle to the input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_post( TT_Face    face,
                     FT_Stream  stream )
  {
    FT_Error        error;
    TT_Postscript*  post = &face->postscript;

    static const FT_Frame_Field  post_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_Postscript

      FT_FRAME_START( 32 ),
        FT_FRAME_LONG ( FormatType ),
        FT_FRAME_LONG ( italicAngle ),
        FT_FRAME_SHORT( underlinePosition ),
        FT_FRAME_SHORT( underlineThickness ),
        FT_FRAME_ULONG( isFixedPitch ),
        FT_FRAME_ULONG( minMemType42 ),
        FT_FRAME_ULONG( maxMemType42 ),
        FT_FRAME_ULONG( minMemType1 ),
        FT_FRAME_ULONG( maxMemType1 ),
      FT_FRAME_END
    };

    error = face->goto_table( face, TTAG_post, stream, 0 );
    if ( error )
      return error;

    if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
      return error;

    /* we don't load the glyph names, we do that in another */
    /* module (ttpost).                                     */

    FT_TRACE3(( "FormatType:   0x%x\n", post->FormatType ));
    FT_TRACE3(( "isFixedPitch:   %s\n", post->isFixedPitch
                                        ? "  yes" : "   no" ));

    return FT_Err_Ok;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_pclt
   *
   * @Description:
   *   Loads the PCL 5 Table.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     A handle to the input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_pclt( TT_Face    face,
                     FT_Stream  stream )
  {
    static const FT_Frame_Field  pclt_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  TT_PCLT

      FT_FRAME_START( 54 ),
        FT_FRAME_ULONG ( Version ),
        FT_FRAME_ULONG ( FontNumber ),
        FT_FRAME_USHORT( Pitch ),
        FT_FRAME_USHORT( xHeight ),
        FT_FRAME_USHORT( Style ),
        FT_FRAME_USHORT( TypeFamily ),
        FT_FRAME_USHORT( CapHeight ),
        FT_FRAME_USHORT( SymbolSet ),
        FT_FRAME_BYTES ( TypeFace, 16 ),
        FT_FRAME_BYTES ( CharacterComplement, 8 ),
        FT_FRAME_BYTES ( FileName, 6 ),
        FT_FRAME_CHAR  ( StrokeWeight ),
        FT_FRAME_CHAR  ( WidthType ),
        FT_FRAME_BYTE  ( SerifStyle ),
        FT_FRAME_BYTE  ( Reserved ),
      FT_FRAME_END
    };

    FT_Error  error;
    TT_PCLT*  pclt = &face->pclt;

    /* optional table */
    error = face->goto_table( face, TTAG_PCLT, stream, 0 );
    if ( error )
      goto Exit;

    if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
      goto Exit;

  Exit:
    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_face_load_gasp
   *
   * @Description:
   *   Loads the `gasp' table into a face object.
   *
   * @Input:
   *   face ::
   *     A handle to the target face object.
   *
   *   stream ::
   *     The input stream.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  FT_LOCAL_DEF( FT_Error )
  tt_face_load_gasp( TT_Face    face,
                     FT_Stream  stream )
  {
    FT_Error   error;
    FT_Memory  memory = stream->memory;

    FT_UInt        j,num_ranges;
    TT_GaspRange   gaspranges = NULL;

    /* the gasp table is optional */
    error = face->goto_table( face, TTAG_gasp, stream, 0 );
    if ( error )
      goto Exit;

    if ( FT_FRAME_ENTER( 4L ) )
      goto Exit;

    face->gasp.version   = FT_GET_USHORT();
    face->gasp.numRanges = FT_GET_USHORT();

    FT_FRAME_EXIT();

    /* only support versions 0 and 1 of the table */
    if ( face->gasp.version >= 2 )
    {
      face->gasp.numRanges = 0;
      error = FT_THROW( Invalid_Table );
      goto Exit;
    }

    num_ranges = face->gasp.numRanges;
    FT_TRACE3(( "numRanges: %u\n", num_ranges ));

    if ( FT_QNEW_ARRAY( face->gasp.gaspRanges, num_ranges ) ||
         FT_FRAME_ENTER( num_ranges * 4L )                  )
      goto Exit;

    gaspranges = face->gasp.gaspRanges;

    for ( j = 0; j < num_ranges; j++ )
    {
      gaspranges[j].maxPPEM  = FT_GET_USHORT();
      gaspranges[j].gaspFlag = FT_GET_USHORT();

      FT_TRACE3(( "gaspRange %d: rangeMaxPPEM %5d, rangeGaspBehavior 0x%x\n",
                  j,
                  gaspranges[j].maxPPEM,
                  gaspranges[j].gaspFlag ));
    }

    FT_FRAME_EXIT();

  Exit:
    return error;
  }

/* END */