shithub: freetype+ttf2subf

ref: e3953e5410eaeec7ae49dbda26be6e4b1ca5d26c
dir: /src/sfnt/sfwoff2.c/

View raw version
/****************************************************************************
 *
 * sfwoff2.c
 *
 *   WOFF2 format management (base).
 *
 * Copyright (C) 2019 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 "sfwoff2.h"
#include "woff2tags.h"
#include FT_TRUETYPE_TAGS_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H


#ifdef FT_CONFIG_OPTION_USE_BROTLI

#include <brotli/decode.h>

#endif


  /**************************************************************************
   *
   * 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  sfwoff2


#define READ_255USHORT( var )  FT_SET_ERROR( Read255UShort( stream, &var ) )

#define READ_BASE128( var )    FT_SET_ERROR( ReadBase128( stream, &var ) )

#define ROUND4( var )          ( var + 3 ) & ~3

#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 )

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

#define WRITE_SFNT_BUF( buf, s ) \
          write_buf( &sfnt, &dest_offset, buf, s, memory )

#define N_CONTOUR_STREAM    0
#define N_POINTS_STREAM     1
#define FLAG_STREAM         2
#define GLYPH_STREAM        3
#define COMPOSITE_STREAM    4
#define BBOX_STREAM         5
#define INSTRUCTION_STREAM  6


  static void
  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_tags( const void*  a,
                const void*  b )
  {
    WOFF2_Table  table1 = *(WOFF2_Table*)a;
    WOFF2_Table  table2 = *(WOFF2_Table*)b;

    FT_ULong  tag1 = table1->Tag;
    FT_ULong  tag2 = table2->Tag;


    if ( tag1 > tag2 )
      return 1;
    else if ( tag1 < tag2 )
      return -1;
    else
      return 0;
  }


  static FT_Error
  Read255UShort( FT_Stream   stream,
                 FT_UShort*  value )
  {
    static const FT_Int  oneMoreByteCode1 = 255;
    static const FT_Int  oneMoreByteCode2 = 254;
    static const FT_Int  wordCode         = 253;
    static const FT_Int  lowestUCode      = 253;

    FT_Error             error        = FT_Err_Ok;
    FT_Byte              code;
    FT_Byte              result_byte  = 0;
    FT_UShort            result_short = 0;

    if( FT_READ_BYTE( code ) )
      return error;
    if( code == wordCode )
    {
      /* Read next two bytes and store FT_UShort value */
      if( FT_READ_USHORT( result_short ) )
        return error;
      *value = result_short;
      return FT_Err_Ok;
    }
    else if( code == oneMoreByteCode1 )
    {
      if( FT_READ_BYTE( result_byte ) )
        return error;
      *value = result_byte + lowestUCode;
      return FT_Err_Ok;
    }
    else if( code == oneMoreByteCode2 )
    {
      if( FT_READ_BYTE( result_byte ) )
        return error;
      *value = result_byte + lowestUCode * 2;
      return FT_Err_Ok;
    }
    else
    {
      *value = code;
      return FT_Err_Ok;
    }
  }


  static FT_Error
  ReadBase128( FT_Stream  stream,
               FT_ULong*  value )
  {
    FT_ULong  result = 0;
    FT_Int    i;
    FT_Byte   code;
    FT_Error  error  = FT_Err_Ok;

    for ( i = 0; i < 5; ++i ) {
      code = 0;
      if( FT_READ_BYTE( code ) )
        return error;

      /* Leading zeros are invalid. */
      if ( i == 0 && code == 0x80 )
        return FT_THROW( Invalid_Table );

      /* If any of top seven bits are set then we're about to overflow. */
      if ( result & 0xfe000000 )
        return FT_THROW( Invalid_Table );

      result = ( result << 7 ) | ( code & 0x7f );

      /* Spin until most significant bit of data byte is false. */
      if ( ( code & 0x80 ) == 0 )
      {
        *value = result;
        return FT_Err_Ok;
      }
    }
    /* Make sure not to exceed the size bound. */
    return FT_THROW( Invalid_Table );
  }


  static FT_Error
  write_buf( FT_Byte**  dst_bytes,
             FT_ULong*  offset,
             FT_Byte*   src,
             FT_ULong   size,
             FT_Memory  memory )
  {
    FT_Error  error = FT_Err_Ok;
    /* We are reallocating memory for `dst', so its pointer may change. */
    FT_Byte*  dst   = *dst_bytes;

    /* Check if we are within limits. */
    if( ( *offset + size ) > WOFF2_DEFAULT_MAX_SIZE  )
      return FT_THROW( Array_Too_Large );

    /* DEBUG - Remove later */
    /* FT_TRACE2(( "Reallocating %lu to %lu.\n", *offset, (*offset + size) )); */
    /* Reallocate `dst'. */
    if ( FT_REALLOC( dst,
                     (FT_ULong)( *offset ),
                     (FT_ULong)( *offset + size ) ) )
      goto Exit;

    /* Copy data. */
    ft_memcpy( dst + *offset, src, size );

    *offset += size;
    /* Set pointer of `dst' to its correct value. */
    *dst_bytes = dst;

  Exit:
    return error;
  }


  static FT_Offset
  CollectionHeaderSize( FT_ULong header_version,
                        FT_UShort num_fonts )
  {
    FT_Offset size = 0;
    if (header_version == 0x00020000)
      size += 12;             /* ulDsig{Tag,Length,Offset} */
    if (header_version == 0x00010000 || header_version == 0x00020000) {
      size += 12              /* TTCTag, Version, numFonts */
        + ( 4 * num_fonts );  /* OffsetTable[numFonts]     */
    }
    return size;
  }


  static FT_UInt64
  compute_first_table_offset( const WOFF2_Header  woff2 )
  {
    FT_Int  nn;
    FT_Offset  offset = WOFF2_SFNT_HEADER_SIZE +
                        ( WOFF2_SFNT_ENTRY_SIZE *
                        (FT_Offset)( woff2->num_tables ) );
    if(woff2->header_version)
    {
      offset = CollectionHeaderSize( woff2->header_version,
                                     woff2->num_fonts )
               + WOFF2_SFNT_HEADER_SIZE * woff2->num_fonts;
      for ( nn = 0; nn< woff2->num_fonts; nn++ ) {
        offset += WOFF2_SFNT_ENTRY_SIZE * woff2->ttc_fonts[nn].num_tables;
      }
    }
    return offset;
  }


  static FT_Long
  compute_ULong_sum( FT_Byte*  buf,
                     FT_ULong  size )
  {
    FT_ULong  checksum     = 0;
    FT_ULong  aligned_size = size & ~3;
    FT_ULong  i;
    FT_ULong  v;

    for( i = 0; i < aligned_size; i += 4 )
    {
      checksum += ( buf[i] << 24 ) | ( buf[i+1] << 16 ) |
                  ( buf[i+2] << 8 ) | ( buf[i+3] << 0 );
    }

    /* If size is not aligned to 4, treat as if it is padded with 0s. */
    if( size != aligned_size )
    {
      v = 0;
      for( i = aligned_size ; i < size; ++i )
        v |= buf[i] << ( 24 - 8 * ( i & 3 ) );
      checksum += v;
    }
    return checksum;
  }


  static FT_Error
  woff2_uncompress( FT_Byte*        dst,
                    FT_ULong        dst_size,
                    const FT_Byte*  src,
                    FT_ULong        src_size )
  {
#ifdef FT_CONFIG_OPTION_USE_BROTLI

    FT_ULong             uncompressed_size = dst_size;
    BrotliDecoderResult  result;

    result = BrotliDecoderDecompress(
      src_size, src, &uncompressed_size, dst);

    if( result != BROTLI_DECODER_RESULT_SUCCESS ||
        uncompressed_size != dst_size )
      {
        FT_ERROR(( "woff2_uncompress: Stream length mismatch.\n" ));
        return FT_THROW( Invalid_Table );
      }

    return FT_Err_Ok;

#else /* !FT_CONFIG_OPTION_USE_BROTLI */

    FT_ERROR(( "woff2_uncompress: Brotli support not available.\n" ));
    return FT_THROW( Unimplemented_Feature );

#endif /* !FT_CONFIG_OPTION_USE_BROTLI */
  }


  static WOFF2_Table
  find_table( WOFF2_Table* tables,
              FT_UShort    num_tables,
              FT_ULong     tag )
  {
    FT_Int i;

    for ( i = 0; i < num_tables; i++ )
    {
      if( tables[i]->Tag == tag )
        return tables[i];
    }
    return NULL;
  }


  /* Read `numberOfHMetrics' from `hhea' table. */
  static FT_Error
  read_num_hmetrics( FT_Stream   stream,
                     FT_ULong    table_len,
                     FT_UShort*  num_hmetrics )
  {
    FT_Error   error = FT_Err_Ok;
    FT_UShort  num_metrics;

    if( FT_STREAM_SKIP( 34 )  )
      return FT_THROW( Invalid_Table );

    if( FT_READ_USHORT( num_metrics ) )
      return FT_THROW( Invalid_Table );

    *num_hmetrics = num_metrics;

    /* DEBUG - Remove later */
    FT_TRACE2(( "num_hmetrics = %d\n", *num_hmetrics ));

    return error;
  }


  static FT_Int
  with_sign( FT_Byte  flag,
             FT_Int   base_val )
  {
    /* Precondition: 0 <= base_val < 65536 (to avoid overflow). */
    return ( flag & 1 ) ? base_val : -base_val;
  }


  static FT_Int
  safe_int_addition( FT_Int   a,
                     FT_Int   b,
                     FT_Int*  result )
  {
    if( ( ( a > 0 ) && ( b > FT_INT_MAX - a ) ) ||
        ( ( a < 0 ) && ( b < FT_INT_MIN - a ) ) )
      return FT_THROW( Invalid_Table );

    *result = a + b;
    return FT_Err_Ok;
  }


  static FT_Error
  triplet_decode( const FT_Byte*  flags_in,
                  const FT_Byte*  in,
                  FT_ULong        in_size,
                  FT_ULong        n_points,
                  WOFF2_Point     result,
                  FT_ULong*       in_bytes_used )
  {
    FT_Int  x = 0;
    FT_Int  y = 0;
    FT_Int  dx;
    FT_Int  dy;
    FT_Int  b0, b1, b2;

    FT_ULong  triplet_index = 0;
    FT_ULong  data_bytes;

    FT_Int  i;

    if ( n_points > in_size )
      return FT_THROW( Invalid_Table );

    for ( i = 0; i < n_points; ++i )
    {
      FT_Byte  flag = flags_in[i];
      FT_Bool  on_curve = !( flag >> 7 );
      flag &= 0x7f;
      if( flag < 84 )
        data_bytes = 1;
      else if( flag < 120 )
        data_bytes = 2;
      else if( flag < 124 )
        data_bytes = 3;
      else
        data_bytes = 4;

      /* Overflow checks */
      if ( triplet_index + data_bytes > in_size      ||
          triplet_index + data_bytes < triplet_index )
        return FT_THROW( Invalid_Table );

      if ( flag < 10 )
      {
        dx = 0;
        dy = with_sign( flag,
                        ( ( flag & 14 ) << 7 ) + in[triplet_index] );
      }
      else if ( flag < 20 )
      {
      dx = with_sign( flag,
                      ( ( ( flag - 10 ) & 14 ) << 7 ) + in[triplet_index] );
      dy = 0;
      }
      else if ( flag < 84 )
      {
        b0 = flag - 20;
        b1 = in[triplet_index];
        dx = with_sign( flag,
                        1 + ( b0 & 0x30 ) + ( b1 >> 4 ) );
        dy = with_sign( flag >> 1,
                        1 + ( ( b0 & 0x0c ) << 2 ) + ( b1 & 0x0f ) );
      }
      else if ( flag < 120 )
      {
        b0 = flag - 84;
        dx = with_sign( flag,
                        1 + ( ( b0 / 12 ) << 8 ) + in[triplet_index] );
        dy = with_sign( flag >> 1,
                        1 + ( ( ( b0 % 12 ) >> 2 ) << 8 ) +
                        in[triplet_index + 1] );
      }
      else if ( flag < 124 )
      {
        b2 = in[triplet_index + 1];
        dx = with_sign( flag,
                        ( in[triplet_index] << 4 ) + ( b2 >> 4 ) );
        dy = with_sign( flag >> 1,
                        ( ( b2 & 0x0f ) << 8 ) + in[triplet_index + 2] );
      }
      else
      {
        dx = with_sign( flag,
                        ( in[triplet_index] << 8 ) +
                          in[triplet_index + 1]  );
        dy = with_sign( flag >> 1,
                        ( in[triplet_index + 2] << 8 ) +
                          in[triplet_index + 3]      );
      }
      triplet_index += data_bytes;

      if ( safe_int_addition( x, dx, &x ) )
        return FT_THROW( Invalid_Table );

      if ( safe_int_addition( y, dy, &y ) )
        return FT_THROW( Invalid_Table );

      result[i].x        = x;
      result[i].y        = y;
      result[i].on_curve = on_curve;
    }
    *in_bytes_used = triplet_index;
    return FT_Err_Ok;
  }


  static FT_Error
  store_points( FT_ULong           n_points,
                const WOFF2_Point  points,
                FT_UShort          n_contours,
                FT_UShort          instruction_len,
                FT_Byte*           dst,
                FT_ULong           dst_size,
                FT_ULong*          glyph_size )
  {
    FT_UInt  flag_offset  = 10 + ( 2 * n_contours ) + 2 + instruction_len;
    FT_Int   last_flag    = -1;
    FT_Int   repeat_count =  0;
    FT_Int   last_x       =  0;
    FT_Int   last_y       =  0;
    FT_UInt  x_bytes      =  0;
    FT_UInt  y_bytes      =  0;
    FT_UInt  xy_bytes;
    FT_UInt  i;
    FT_UInt  x_offset;
    FT_UInt  y_offset;

    FT_Byte*  pointer;

    for ( i = 0; i < n_points; ++i )
    {
      const WOFF2_PointRec  point = points[i];

      FT_Int       flag = point.on_curve ? GLYF_ON_CURVE : 0;
      FT_Int       dx   = point.x - last_x;
      FT_Int       dy   = point.y - last_y;

      if ( dx == 0 )
        flag |= GLYF_THIS_X_IS_SAME;
      else if ( dx > -256 && dx < 256 )
      {
        flag |= GLYF_X_SHORT | ( dx > 0 ? GLYF_THIS_X_IS_SAME : 0 );
        x_bytes += 1;
      }
      else
        x_bytes += 2;

      if ( dy == 0 )
        flag |= GLYF_THIS_Y_IS_SAME;
      else if ( dy > -256 && dy < 256 )
      {
        flag |= GLYF_Y_SHORT | ( dy > 0 ? GLYF_THIS_Y_IS_SAME : 0 );
        y_bytes += 1;
      }
      else
        y_bytes += 2;

      if ( flag == last_flag && repeat_count != 255 )
      {
        dst[flag_offset - 1] |= GLYF_REPEAT;
        repeat_count++;
      }
      else
      {
        if ( repeat_count != 0 )
        {
          if ( flag_offset >= dst_size )
            return FT_THROW( Invalid_Table );

          dst[flag_offset++] = repeat_count;
        }
        if ( flag_offset >= dst_size )
          return FT_THROW( Invalid_Table );

        dst[flag_offset++] = flag;
        repeat_count = 0;
      }
      last_x = point.x;
      last_y = point.y;
      last_flag = flag;
    }

    if ( repeat_count != 0 )
    {
      if ( flag_offset >= dst_size )
        return FT_THROW( Invalid_Table );

      dst[flag_offset++] = repeat_count;
    }

    xy_bytes = x_bytes + y_bytes;
    if ( xy_bytes < x_bytes                   ||
         flag_offset + xy_bytes < flag_offset ||
         flag_offset + xy_bytes > dst_size    )
      return FT_THROW( Invalid_Table );

    x_offset = flag_offset;
    y_offset = flag_offset + x_bytes;
    last_x = 0;
    last_y = 0;
    for ( i = 0; i < n_points; ++i )
    {
      FT_Int  dx = points[i].x - last_x;
      FT_Int  dy = points[i].y - last_y;

      if ( dx == 0 ) {;}
      else if ( dx > -256 && dx < 256 )
        dst[x_offset++] = FT_ABS( dx );
      else
      {
        pointer = dst + x_offset;
        WRITE_SHORT( pointer, dx );
        x_offset += 2;
      }

      last_x += dx;

      if ( dy == 0 ) {;}
      else if ( dy > -256 && dy < 256 )
        dst[y_offset++] = FT_ABS( dy );
      else
      {
        pointer = dst + y_offset;
        WRITE_SHORT( pointer, dy );
        y_offset += 2;
      }

      last_y += dy;
    }

    *glyph_size = y_offset;
    return FT_Err_Ok;
  }


  static void
  compute_bbox( FT_ULong           n_points,
                const WOFF2_Point  points,
                FT_Byte*           dst,
                FT_UShort*         src_x_min )
  {
    FT_Int  x_min = 0;
    FT_Int  y_min = 0;
    FT_Int  x_max = 0;
    FT_Int  y_max = 0;
    FT_Int  i;

    FT_ULong  offset;
    FT_Byte*  pointer;

    if ( n_points > 0 )
    {
      x_min = points[0].x;
      y_min = points[0].y;
      x_max = points[0].x;
      y_max = points[0].y;
    }
    for ( i = 1; i < n_points; ++i )
    {
      FT_Int x = points[i].x;
      FT_Int y = points[i].y;

      x_min = FT_MIN( x, x_min );
      y_min = FT_MIN( y, y_min );
      x_max = FT_MAX( x, x_max );
      y_max = FT_MAX( y, y_max );
    }

    /* Write values to `glyf' record. */
    offset = 2;
    pointer = dst + offset;
    WRITE_SHORT( pointer, x_min );
    WRITE_SHORT( pointer, y_min );
    WRITE_SHORT( pointer, x_max );
    WRITE_SHORT( pointer, y_max );

    *src_x_min = (FT_UShort)x_min;
  }


  static FT_Error
  compositeGlyph_size( FT_Stream  stream,
                       FT_ULong   offset,
                       FT_ULong*  size,
                       FT_Bool*   have_instructions )
  {
    FT_Error   error        = FT_Err_Ok;
    FT_ULong   start_offset = offset;
    FT_Bool    we_have_inst = FALSE;
    FT_UShort  flags        = FLAG_MORE_COMPONENTS;

    if( FT_STREAM_SEEK( start_offset ) )
      goto Exit;
    while ( flags & FLAG_MORE_COMPONENTS )
    {
      FT_ULong  arg_size;

      if ( FT_READ_USHORT( flags ) )
        goto Exit;
      we_have_inst |= ( flags & FLAG_WE_HAVE_INSTRUCTIONS ) != 0;
      /* glyph index */
      arg_size = 2;
      if ( flags & FLAG_ARG_1_AND_2_ARE_WORDS )
        arg_size += 4;
      else
        arg_size += 2;

      if ( flags & FLAG_WE_HAVE_A_SCALE )
        arg_size += 2;
      else if ( flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE )
        arg_size += 4;
      else if ( flags & FLAG_WE_HAVE_A_TWO_BY_TWO )
        arg_size += 8;

      if( FT_STREAM_SKIP( arg_size ) )
        goto Exit;
    }

    *size              = FT_STREAM_POS() - start_offset;
    *have_instructions = we_have_inst;

    Exit:
      return error;
  }


  static FT_Error
  reconstruct_glyf( FT_Stream       stream,
                    WOFF2_Table     glyf_table,
                    FT_ULong*       glyf_checksum,
                    WOFF2_Table     loca_table,
                    FT_ULong*       loca_checksum,
                    FT_Byte**       sfnt_bytes,
                    FT_ULong*       out_offset,
                    WOFF2_Info      info,
                    FT_Memory       memory )
  {
    FT_Error  error = FT_Err_Ok;
    FT_Byte*  sfnt  = *sfnt_bytes;

    /* Current position in stream */
    const FT_ULong  pos = FT_STREAM_POS();

    FT_UInt    num_substreams = 7;

    FT_UShort  num_glyphs;
    FT_UShort  index_format;
    FT_ULong   expected_loca_length;
    FT_UInt    offset;
    FT_Int     i;
    FT_ULong   points_size;
    FT_ULong   bitmap_length;
    FT_ULong   glyph_buf_size;
    FT_ULong   bbox_bitmap_offset;

    const FT_ULong  glyf_start  = *out_offset;
    FT_ULong        dest_offset = *out_offset;

    WOFF2_Substream  substreams = NULL;

    FT_ULong*    loca_values       = NULL;
    FT_UShort*   n_points_arr      = NULL;
    FT_Byte*     glyph_buf         = NULL;
    WOFF2_Point  points            = NULL;

    if( FT_NEW_ARRAY( substreams, num_substreams ) )
      goto Fail;

    if( FT_STREAM_SKIP( 4 ) )
      goto Fail;
    if( FT_READ_USHORT( num_glyphs ) )
      goto Fail;
    if( FT_READ_USHORT( index_format ) )
      goto Fail;

    FT_TRACE2(( "Num_glyphs = %u; index_format = %u\n", num_glyphs, index_format ));

    /* Calculate expected length of loca and compare.          */
    /* See https://www.w3.org/TR/WOFF2/#conform-mustRejectLoca */
    /* index_format = 0 => Short version `loca'.               */
    /* index_format = 1 => Long version `loca'.                */
    expected_loca_length = ( index_format ? 4 : 2 )   *
                           ( (FT_ULong)num_glyphs + 1 );
    if( loca_table->dst_length != expected_loca_length )
      goto Fail;

    offset = ( 2 + num_substreams ) * 4;
    if( offset > glyf_table->TransformLength )
      goto Fail;

    for ( i = 0; i < num_substreams; ++i )
    {
      FT_ULong  substream_size;
      if( FT_READ_ULONG( substream_size ) )
        goto Fail;
      if( substream_size > glyf_table->TransformLength - offset )
        goto Fail;

      substreams[i].start  = pos + offset;
      substreams[i].offset = pos + offset;
      substreams[i].size   = substream_size;

      /* DEBUG - Remove later */
      FT_TRACE2(( "Substream %d: offset = %lu; size = %lu;\n",
                  i, substreams[i].offset, substreams[i].size ));
      offset += substream_size;
    }

    if( FT_NEW_ARRAY( loca_values, num_glyphs + 1 ) )
      goto Fail;

    points_size = 0;
    bbox_bitmap_offset = substreams[BBOX_STREAM].offset;

    /* Size of bboxBitmap = 4 * floor((numGlyphs + 31) / 32) */
    bitmap_length = ( ( num_glyphs + 31 ) >> 5 ) << 2;
    substreams[BBOX_STREAM].offset += bitmap_length;

    glyph_buf_size = WOFF2_DEFAULT_GLYPH_BUF;
    if( FT_NEW_ARRAY( glyph_buf, glyph_buf_size ) )
      goto Fail;

    if( FT_NEW_ARRAY( info->x_mins, num_glyphs ) )
      goto Fail;

    for ( i = 0; i < num_glyphs; ++i )
    {
      FT_ULong   glyph_size = 0;
      FT_UShort  n_contours = 0;
      FT_Bool    have_bbox  = FALSE;
      FT_Byte    bbox_bitmap;
      FT_ULong   bbox_offset;
      FT_UShort  x_min;

      /* Set `have_bbox'. */
      bbox_offset = bbox_bitmap_offset + ( i >> 3 );
      if( FT_STREAM_SEEK( bbox_offset ) ||
          FT_READ_BYTE( bbox_bitmap )   )
        goto Fail;
      if( bbox_bitmap & ( 0x80 >> ( i & 7 ) ) )
        have_bbox = TRUE;

      /* Read value from `nContourStream' */
      if( FT_STREAM_SEEK( substreams[N_CONTOUR_STREAM].offset ) ||
          FT_READ_USHORT( n_contours )                          )
        goto Fail;
      substreams[N_CONTOUR_STREAM].offset += 2;

      if( n_contours == 0xffff )
      {
        /* Composite glyph */
        FT_Bool    have_instructions = FALSE;
        FT_UShort  instruction_size  = 0;
        FT_ULong   composite_size;
        FT_ULong   size_needed;
        FT_Byte*   pointer           = NULL;

        /* Composite glyphs must have explicit bbox. */
        if( !have_bbox )
          goto Fail;

        if( compositeGlyph_size( stream,
                                 substreams[COMPOSITE_STREAM].offset,
                                 &composite_size,
                                 &have_instructions) )
          goto Fail;

        if( have_instructions )
        {
          if( FT_STREAM_SEEK( substreams[GLYPH_STREAM].offset ) ||
              READ_255USHORT( instruction_size )                )
            goto Fail;
          substreams[GLYPH_STREAM].offset = FT_STREAM_POS();
        }

        size_needed = 12 + composite_size + instruction_size;
        if( glyph_buf_size < size_needed )
        {
          if( FT_RENEW_ARRAY( glyph_buf, glyph_buf_size, size_needed ) )
            goto Fail;
          glyph_buf_size = size_needed;
        }

        pointer = glyph_buf + glyph_size;
        WRITE_USHORT( pointer, n_contours );
        glyph_size += 2;

        /* Read x_min for current glyph. */
        if( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
            FT_READ_USHORT( x_min )                          )
          goto Fail;
        /* No increment here because we read again. */

        if( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
            FT_STREAM_READ( glyph_buf + glyph_size, 8 )      )
          goto Fail;
        substreams[BBOX_STREAM].offset += 8;
        glyph_size += 8;

        if( FT_STREAM_SEEK( substreams[COMPOSITE_STREAM].offset )    ||
            FT_STREAM_READ( glyph_buf + glyph_size, composite_size ) )
          goto Fail;
        substreams[COMPOSITE_STREAM].offset += composite_size;
        glyph_size += composite_size;

        if( have_instructions )
        {
          pointer = glyph_buf + glyph_size;
          WRITE_USHORT( pointer, instruction_size );
          glyph_size += 2;
          if( FT_STREAM_SEEK( substreams[INSTRUCTION_STREAM].offset )    ||
              FT_STREAM_READ( glyph_buf + glyph_size, instruction_size ) )
            goto Fail;
          substreams[INSTRUCTION_STREAM].offset += instruction_size;
          glyph_size += instruction_size;
        }
      }
      else if( n_contours > 0 )
      {
        /* Simple glyph */
        FT_ULong   total_n_points = 0;
        FT_UShort  n_points_contour;
        FT_UInt    j;
        FT_ULong   flag_size;
        FT_ULong   triplet_size;
        FT_ULong   triplet_bytes_used;
        FT_Byte*   flags_buf;
        FT_Byte*   triplet_buf;
        FT_UShort  instruction_size;
        FT_ULong   size_needed;
        FT_Int     end_point;
        FT_UInt    contour_ix;

        FT_Byte*   pointer = NULL;

        if( FT_NEW_ARRAY( n_points_arr, n_contours ) )
          goto Fail;

        if( FT_STREAM_SEEK( substreams[N_POINTS_STREAM].offset ) )
          goto Fail;
        for( j = 0; j < n_contours; ++j )
        {
          if( READ_255USHORT( n_points_contour ) )
            goto Fail;
          n_points_arr[j] = n_points_contour;
          /* Prevent negative/overflow. */
          if( total_n_points + n_points_contour < total_n_points )
            goto Fail;
          total_n_points += n_points_contour;
        }
        substreams[N_POINTS_STREAM].offset = FT_STREAM_POS();

        flag_size = total_n_points;
        if( flag_size > substreams[FLAG_STREAM].size )
          goto Fail;

        flags_buf   = stream->base + substreams[FLAG_STREAM].offset;
        triplet_buf = stream->base + substreams[GLYPH_STREAM].offset;

        triplet_size = substreams[GLYPH_STREAM].size -
                       ( substreams[GLYPH_STREAM].offset -
                         substreams[GLYPH_STREAM].start  );
        triplet_bytes_used = 0;
        /* Create array to store point information. */
        points_size = total_n_points;
        if( FT_NEW_ARRAY( points, points_size ) )
          goto Fail;

        if( triplet_decode( flags_buf, triplet_buf,
                            triplet_size, total_n_points,
                            points, &triplet_bytes_used ) )
          goto Fail;
        substreams[FLAG_STREAM].offset  += flag_size;
        substreams[GLYPH_STREAM].offset += triplet_bytes_used;

        if( FT_STREAM_SEEK( substreams[GLYPH_STREAM].offset ) ||
            READ_255USHORT( instruction_size )                )
          goto Fail;
        substreams[GLYPH_STREAM].offset = FT_STREAM_POS();

        if( total_n_points >= ( 1 << 27 )   ||
            instruction_size >= ( 1 << 30 ) )
          goto Fail;
        size_needed = 12 + ( 2 * n_contours ) + ( 5 * total_n_points )
                      + instruction_size;
        if( glyph_buf_size < size_needed )
        {
          if( FT_RENEW_ARRAY( glyph_buf, glyph_buf_size, size_needed ) )
            goto Fail;
          glyph_buf_size = size_needed;
        }

        pointer = glyph_buf + glyph_size;
        WRITE_USHORT( pointer, n_contours );
        glyph_size += 2;

        if( have_bbox )
        {
          /* Read x_min for current glyph. */
          if( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
              FT_READ_USHORT( x_min )                          )
            goto Fail;
          /* No increment here because we read again. */

          if( FT_STREAM_SEEK( substreams[BBOX_STREAM].offset ) ||
              FT_STREAM_READ( glyph_buf + glyph_size, 8 )      )
            goto Fail;
          substreams[BBOX_STREAM].offset += 8;
        }
        else
          compute_bbox( total_n_points, points, glyph_buf, &x_min );

        glyph_size = CONTOUR_OFFSET_END_POINT;
        pointer = glyph_buf + glyph_size;
        end_point  = -1;
        for( contour_ix = 0; contour_ix < n_contours; ++contour_ix )
        {
          end_point += n_points_arr[contour_ix];
          if( end_point >= 65536 )
            goto Fail;

          WRITE_SHORT( pointer, end_point );
          glyph_size += 2;
        }

        WRITE_USHORT( pointer, instruction_size );
        glyph_size += 2;
        if( FT_STREAM_SEEK( substreams[INSTRUCTION_STREAM].offset )    ||
            FT_STREAM_READ( glyph_buf + glyph_size, instruction_size ) )
          goto Fail;
        glyph_size += instruction_size;

        if( store_points( total_n_points, points, n_contours,
                          instruction_size, glyph_buf,
                          glyph_buf_size, &glyph_size ) )
          goto Fail;

        FT_FREE( points );
        FT_FREE( n_points_arr );
      }
      else
      {
        /* Empty glyph. */
        /* Must not have a bbox. */
        if( have_bbox )
        {
          FT_ERROR(( "Empty glyph has a bbox.\n" ));
          goto Fail;
        }
      }
      loca_values[i] = dest_offset - glyf_start;
      if( WRITE_SFNT_BUF( glyph_buf, glyph_size ) )
        goto Fail;

      *glyf_checksum += compute_ULong_sum( glyph_buf, glyph_size );

      /* Store x_mins, may be required to reconstruct `hmtx'. */
      if ( n_contours > 0 )
        info->x_mins[i] = x_min;
    }

    glyf_table->dst_length = dest_offset - glyf_table->dst_offset;


    /* TODO Reconstruct `loca' table and set its `dst_length'. */

    /* Set pointer of `sfnt_bytes' to its correct value. */
    *sfnt_bytes = sfnt;
    *out_offset = dest_offset;
    /* DEBUG - Remove later */
    if( !error )
      FT_TRACE2(( "reconstruct_glyf proceeding w/o errors.\n" ));

      FT_FREE( substreams );
      FT_FREE( loca_values );
      FT_FREE( n_points_arr );
      FT_FREE( glyph_buf );
      FT_FREE( points );

      return error;

    Fail:
      if( !error )
        error = FT_THROW( Invalid_Table );

      FT_FREE( substreams );
      FT_FREE( loca_values );
      FT_FREE( n_points_arr );
      FT_FREE( glyph_buf );
      FT_FREE( points );

      return error;
  }


  static FT_Error
  reconstruct_font( FT_Byte*      transformed_buf,
                    FT_ULong      transformed_buf_size,
                    WOFF2_Table*  indices,
                    WOFF2_Header  woff2,
                    WOFF2_Info    info,
                    FT_Byte**     sfnt_bytes,
                    FT_Memory     memory )
  {
    FT_Error    error      = FT_Err_Ok;
    FT_Stream   stream     = NULL;
    FT_Byte*    buf_cursor = NULL;

    /* We are reallocating memory for `sfnt', so its pointer may change. */
    FT_Byte*   sfnt       = *sfnt_bytes;

    FT_UShort  num_tables  = woff2->num_tables;
    FT_ULong   dest_offset = 12 + num_tables * 16UL;

    FT_ULong  checksum      = 0;
    FT_ULong  loca_checksum = 0;
    FT_Int    nn            = 0;
    FT_UShort  num_hmetrics;

    /* Few table checks before reconstruction. */
    /* `glyf' must be present with `loca'. */
    const WOFF2_Table glyf_table = find_table( indices, num_tables,
                                               TTAG_glyf );
    const WOFF2_Table loca_table = find_table( indices, num_tables,
                                               TTAG_loca );

    if( ( !glyf_table && loca_table ) ||
        ( !loca_table && glyf_table ) )
    {
      FT_ERROR(( "Cannot have only one of glyf/loca.\n" ));
      return FT_THROW( Invalid_Table );
    }

    /* Both `glyf' and `loca' must have same transformation. */
    if( glyf_table != NULL )
    {
      if( ( glyf_table->flags & WOFF2_FLAGS_TRANSFORM ) !=
          ( loca_table->flags & WOFF2_FLAGS_TRANSFORM ) )
        {
          FT_ERROR(( "Transformation mismatch in glyf and loca." ));
          return FT_THROW( Invalid_Table );
        }
    }

    /* Create a stream for the uncompressed buffer. */
    if ( FT_NEW( stream ) )
      return FT_THROW( Invalid_Table );
    FT_Stream_OpenMemory( stream, transformed_buf, transformed_buf_size );
    stream->close = stream_close;

    FT_ASSERT( FT_STREAM_POS() == 0 );

    /* Reconstruct/copy tables to output stream. */
    for ( nn = 0; nn < num_tables; nn++ )
    {
      WOFF2_TableRec  table = *( indices[nn] );

      /* DEBUG - Remove later */
      FT_TRACE2(( "Seeking to %d with table size %d.\n",
                  table.src_offset, table.src_length ));
      FT_TRACE2(( "Table tag: %c%c%c%c.\n",
            (FT_Char)( table.Tag >> 24 ),
            (FT_Char)( table.Tag >> 16 ),
            (FT_Char)( table.Tag >> 8  ),
            (FT_Char)( table.Tag       ) ));

      if ( FT_STREAM_SEEK( table.src_offset ) )
        return FT_THROW( Invalid_Table );

      if( table.src_offset + table.src_length > transformed_buf_size )
        return FT_THROW( Invalid_Table );

      /* Get stream size for fields of `hmtx' table. */
      if( table.Tag == TTAG_hhea )
      {
        if( read_num_hmetrics( stream, table.src_length, &num_hmetrics ) )
          return FT_THROW( Invalid_Table );
      }

      info->num_hmetrics = num_hmetrics;

      checksum = 0;
      if( ( table.flags & WOFF2_FLAGS_TRANSFORM ) != WOFF2_FLAGS_TRANSFORM )
      {
        /* Check if `head' is atleast 12 bytes. */
        if( table.Tag == TTAG_head )
        {
          if( table.src_length < 12 )
            return FT_THROW( Invalid_Table );
          buf_cursor = transformed_buf + table.src_offset + 8;
          WRITE_ULONG( buf_cursor, 0 );
        }
        table.dst_offset = dest_offset;
        checksum = compute_ULong_sum( transformed_buf + table.src_offset,
                                     table.src_length );
        /* DEBUG - Remove later */
        FT_TRACE2(( "Checksum = %u\n", checksum ));

        if( WRITE_SFNT_BUF( transformed_buf + table.src_offset,
                            table.src_length ) )
          return FT_THROW( Invalid_Table );
      }
      else{
        /* DEBUG - Remove later */
        FT_TRACE2(( "This table has xform.\n" ));

        if( table.Tag == TTAG_glyf )
        {
          table.dst_offset = dest_offset;

          if( reconstruct_glyf( stream, &table, &checksum,
                                loca_table, &loca_checksum,
                                &sfnt, &dest_offset, info,
                                memory ) )
            return FT_THROW( Invalid_Table );
        }

        /* TODO reconstruct transformed loca and hmtx! */
      }
    }

    /* Set pointer of sfnt stream to its correct value. */
    *sfnt_bytes = sfnt;
    return FT_Err_Ok;

    /* TODO delete the uncompressed stream after everything is done. */
  }


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

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

    WOFF2_HeaderRec  woff2;
    WOFF2_InfoRec    info;
    WOFF2_Table      tables       = NULL;
    WOFF2_Table*     indices      = NULL;
    WOFF2_Table*     temp_indices = NULL;
    WOFF2_Table      last_table;

    FT_Int           nn;
    FT_ULong         j;
    FT_ULong         flags;
    FT_UShort        xform_version;
    FT_ULong         src_offset = 0;

    FT_UInt          glyf_index;
    FT_UInt          loca_index;
    FT_UInt64        first_table_offset;
    FT_UInt64        file_offset;

    FT_Byte*         sfnt        = NULL;
    FT_Stream        sfnt_stream = NULL;
    FT_Byte*         sfnt_header;

    FT_Byte*         uncompressed_buf = NULL;

    static const FT_Frame_Field  woff2_header_fields[] =
    {
#undef  FT_STRUCTURE
#define FT_STRUCTURE  WOFF2_HeaderRec

      FT_FRAME_START( 48 ),
        FT_FRAME_ULONG ( signature ),
        FT_FRAME_ULONG ( flavor ),
        FT_FRAME_ULONG ( length ),
        FT_FRAME_USHORT( num_tables ),
        FT_FRAME_SKIP_BYTES( 2 + 4 ),
        FT_FRAME_ULONG ( totalCompressedSize ),
        FT_FRAME_SKIP_BYTES( 2 * 2 ),
        FT_FRAME_ULONG ( metaOffset ),
        FT_FRAME_ULONG ( metaLength ),
        FT_FRAME_ULONG ( metaOrigLength ),
        FT_FRAME_ULONG ( privOffset ),
        FT_FRAME_ULONG ( privLength ),
      FT_FRAME_END
    };


    /* DEBUG - Remove later */
    FT_TRACE2(( "woff2_open_font: Received Data.\n" ));

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

    /* DEBUG - Remove later. */
    FT_TRACE2(( "Face index = %ld\n", face_index ));

    /* Read WOFF2 Header. */
    if ( FT_STREAM_READ_FIELDS( woff2_header_fields, &woff2 ) )
      return error;

    /* DEBUG - Remove later. */
    FT_TRACE2(( "signature  -> 0x%X\n", woff2.signature ));
    FT_TRACE2(( "flavor     -> 0x%X\n", woff2.flavor ));
    FT_TRACE2(( "length     -> %lu\n", woff2.length ));
    FT_TRACE2(( "num_tables -> %hu\n", woff2.num_tables ));
    FT_TRACE2(( "metaOffset -> %hu\n", woff2.metaOffset ));
    FT_TRACE2(( "metaLength -> %hu\n", woff2.metaLength ));
    FT_TRACE2(( "privOffset -> %hu\n", woff2.privOffset ));
    FT_TRACE2(( "privLength -> %hu\n", woff2.privLength ));

    /* Make sure we don't recurse back here. */
    if ( woff2.flavor == TTAG_wOF2 )
      return FT_THROW( Invalid_Table );

    /* Miscellaneous checks. */
    if ( woff2.length != stream->size                               ||
         woff2.num_tables == 0                                      ||
         48 + woff2.num_tables * 20UL >= woff2.length               ||
         ( woff2.metaOffset == 0 && ( woff2.metaLength != 0     ||
                                      woff2.metaOrigLength != 0 ) ) ||
         ( woff2.metaLength != 0 && woff2.metaOrigLength == 0 )     ||
         ( woff2.metaOffset >= woff2.length )                       ||
         ( woff2.length - woff2.metaOffset < woff2.metaLength )     ||
         ( woff2.privOffset == 0 && woff2.privLength != 0 )         ||
         ( woff2.privOffset >= woff2.length )                       ||
         ( woff2.length - woff2.privOffset < woff2.privLength )     )
    {
      FT_ERROR(( "woff2_font_open: invalid WOFF2 header\n" ));
      return FT_THROW( Invalid_Table );
    }
    /* DEBUG - Remove later. */
    else{
      FT_TRACE2(( "WOFF2 Header is valid.\n" ));
    }

    /* Read table directory. */
    if ( FT_NEW_ARRAY( tables, woff2.num_tables )  ||
         FT_NEW_ARRAY( indices, woff2.num_tables ) )
      goto Exit;

    FT_TRACE2(( "\n"
                "  tag    flags    transform   origLen   transformLen\n"
                "  --------------------------------------------------\n" ));

    for ( nn = 0; nn < woff2.num_tables; nn++ )
    {
      WOFF2_Table  table = tables + nn;
      if( FT_READ_BYTE( table->FlagByte ) )
        goto Exit;

      if ( ( table->FlagByte & 0x3f ) == 0x3f )
      {
        if( FT_READ_ULONG( table->Tag ) )
          goto Exit;
      }
      else
        table->Tag = woff2_known_tags( table->FlagByte & 0x3f );

      flags = 0;
      xform_version = ( table->FlagByte >> 6 ) & 0x03;

      /* 0 means xform for glyph/loca, non-0 for others. */
      if ( table->Tag == TTAG_glyf || table->Tag == TTAG_loca )
      {
        if ( xform_version == 0 )
          flags |= WOFF2_FLAGS_TRANSFORM;
      }
      else if ( xform_version != 0 )
        flags |= WOFF2_FLAGS_TRANSFORM;

      flags |= xform_version;

      if( READ_BASE128( table->dst_length ) )
        goto Exit;

      table->TransformLength = table->dst_length;

      if ( ( flags & WOFF2_FLAGS_TRANSFORM ) != 0 )
      {
        if( READ_BASE128( table->TransformLength ) )
          goto Exit;
        if( table->Tag == TTAG_loca && table->TransformLength )
        {
          FT_ERROR(( "woff_font_open: Invalid loca `transformLength'.\n" ));
          return FT_THROW( Invalid_Table );
        }
      }

      if ( src_offset + table->TransformLength < src_offset )
      {
        FT_ERROR(( "woff_font_open: invalid WOFF2 table directory.\n" ));
        return FT_THROW( Invalid_Table );
      }

      table->src_offset = src_offset;
      table->src_length = table->TransformLength;
      src_offset += table->TransformLength;
      table->flags = flags;

      FT_TRACE2(( "  %c%c%c%c  %08d  %08d    %08ld  %08ld\n",
                  (FT_Char)( table->Tag >> 24 ),
                  (FT_Char)( table->Tag >> 16 ),
                  (FT_Char)( table->Tag >> 8  ),
                  (FT_Char)( table->Tag       ),
                  table->FlagByte & 0x3f,
                  ( table->FlagByte >> 6 ) & 0x03,
                  table->dst_length,
                  table->TransformLength,
                  table->src_length,
                  table->src_offset ));

      indices[nn] = table;
    }

    /* End of last table is uncompressed size. */
    last_table = indices[woff2.num_tables - 1];
    woff2.uncompressed_size = last_table->src_offset
                              + last_table->src_length;
    if( woff2.uncompressed_size < last_table->src_offset )
        return FT_THROW( Invalid_Table );
    /* DEBUG - Remove later. */
    FT_TRACE2(( "Uncompressed size: %ld\n", woff2.uncompressed_size ));

    FT_TRACE2(( "Table directory successfully parsed.\n" ));

    /* Check for and read collection directory. */
    woff2.header_version = 0;
    if( woff2.flavor == TTAG_ttcf ){
      FT_TRACE2(( "Font is a TTC, reading collection directory.\n" ));
      if( FT_READ_ULONG( woff2.header_version ) )
        goto Exit;
      /* DEBUG - Remove later */
      FT_TRACE2(( "Header version: %lx\n", woff2.header_version ));
      if( woff2.header_version != 0x00010000 &&
          woff2.header_version != 0x00020000 )
        return FT_THROW( Invalid_Table );

      if( READ_255USHORT( woff2.num_fonts ) )
        goto Exit;
      /* DEBUG - Remove later */
      FT_TRACE2(( "Number of fonts in TTC: %ld\n", woff2.num_fonts ));

      if( FT_NEW_ARRAY( woff2.ttc_fonts, woff2.num_fonts ) )
        goto Exit;

      for ( nn = 0; nn < woff2.num_fonts; nn++ )
      {
        WOFF2_TtcFont  ttc_font = woff2.ttc_fonts + nn;

        if( READ_255USHORT( ttc_font->num_tables ) )
          goto Exit;
        if( FT_READ_ULONG( ttc_font->flavor ) )
          goto Exit;

        if( FT_NEW_ARRAY( ttc_font->table_indices, ttc_font->num_tables ) )
          goto Exit;
        /* DEBUG - Change to TRACE4 */
        FT_TRACE2(( "Number of tables in font %d: %ld\n",
                    nn, ttc_font->num_tables ));
        /* DEBUG - Change to TRACE5 */
        FT_TRACE2(( "  Indices: " ));

        glyf_index = 0;
        loca_index = 0;

        for ( j = 0; j < ttc_font->num_tables; j++ )
        {
          FT_UShort    table_index;
          WOFF2_Table  table;

          if( READ_255USHORT( table_index ) )
            goto Exit;
          /* DEBUG - Change to TRACE5 */
          FT_TRACE2(("%hu ", table_index));
          ttc_font->table_indices[j] = table_index;

          table = indices[table_index];
          if( table->Tag == TTAG_loca )
            loca_index = table_index;
          if( table->Tag == TTAG_glyf )
            glyf_index = table_index;
        }
        /* DEBUG - Change to TRACE5 */
        FT_TRACE2(( "\n" ));

        /* glyf and loca must be consecutive */
        if( glyf_index > 0 || loca_index > 0 )
        {
          if( glyf_index > loca_index      ||
              loca_index - glyf_index != 1 )
            return FT_THROW( Invalid_Table );
          /* DEBUG - Remove later */
          else
            FT_TRACE2(( "glyf and loca indices are valid.\n" ));
        }
      }
      /* Collection directory reading complete. */
      FT_TRACE2(( "WOFF2 collection dirtectory is valid.\n" ));
    }

    first_table_offset = compute_first_table_offset( &woff2 );
    FT_TRACE2(( "Offset to first table: %ld\n", first_table_offset ));

    woff2.compressed_offset = FT_STREAM_POS();
    file_offset = ROUND4( woff2.compressed_offset +
                          woff2.totalCompressedSize );

    /* Few more checks before we start reading the tables. */
    if( file_offset > woff2.length )
      return FT_THROW( Invalid_Table );

    if ( woff2.metaOffset )
    {
      if ( file_offset != woff2.metaOffset )
        return FT_THROW( Invalid_Table );
      file_offset = ROUND4(woff2.metaOffset + woff2.metaLength);
    }

    if( woff2.privOffset )
    {
      if( file_offset != woff2.privOffset )
        return FT_THROW( Invalid_Table );
      file_offset = ROUND4(woff2.privOffset + woff2.privLength);
    }

    if( file_offset != ( ROUND4( woff2.length ) ) )
      return FT_THROW( Invalid_Table );

    /* Only retain tables of the requested face in a TTC. */
    /* TODO Check whether it is OK for rest of the code to be unaware of the
       fact that we're working with a TTC. */
    if( woff2.header_version )
    {
      WOFF2_TtcFont  ttc_font = woff2.ttc_fonts + face_index;
      /* Create a temporary array. */
      if( FT_NEW_ARRAY( temp_indices,
                        ttc_font->num_tables ) )
        goto Exit;

      /* DEBUG - Remove later */
      FT_TRACE2(( "Storing tables for TTC face index %d.\n", face_index ));
      for ( nn = 0; nn < ttc_font->num_tables; nn++ )
      {
        /* DEBUG - Remove later */
        FT_TRACE2(( "i=%d, table_index=%d\n",
                    nn, ttc_font->table_indices[nn] ));
        temp_indices[nn] = indices[ttc_font->table_indices[nn]];
      }

      /* Resize array to required size. */
      if( FT_RENEW_ARRAY( indices, woff2.num_tables,
                      ttc_font->num_tables ) )
        goto Exit;

      for ( nn = 0; nn < ttc_font->num_tables; nn++ )
        indices[nn] = temp_indices[nn];
      FT_FREE( temp_indices );

      /* Change header values. */
      woff2.flavor     = ttc_font->flavor;
      woff2.num_tables = ttc_font->num_tables;

    }

    /* Write sfnt header. */
    if ( FT_ALLOC( sfnt, 12 + woff2.num_tables * 16UL ) ||
         FT_NEW( sfnt_stream )                         )
      goto Exit;

    sfnt_header = sfnt;

    {
      FT_UInt  searchRange, entrySelector, rangeShift, x;
      /* DEBUG - Remove later */
      FT_TRACE2(( "Writing SFNT offset table.\n" ));

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

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

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

    }

    /* Sort tables by tag. */
    ft_qsort( indices,
              woff2.num_tables,
              sizeof ( WOFF2_Table ),
              compare_tags );

    /* DEBUG - Remove later */
    FT_TRACE2(( "Sorted table indices: \n" ));
    for ( nn = 0; nn < woff2.num_tables; ++nn )
    {
      WOFF2_Table  table = indices[nn];
      /* DEBUG - Remove later */
      FT_TRACE2(( "  Index %d", nn ));
      FT_TRACE2(( " %c%c%c%c\n",
                  (FT_Char)( table->Tag >> 24 ),
                  (FT_Char)( table->Tag >> 16 ),
                  (FT_Char)( table->Tag >> 8  ),
                  (FT_Char)( table->Tag       )));
    }

    if( woff2.uncompressed_size < 1 )
    {
      error = FT_THROW( Invalid_Table );
      goto Exit;
    }

    /* Allocate memory for uncompressed table data. */
    if ( FT_ALLOC( uncompressed_buf, woff2.uncompressed_size ) ||
         FT_FRAME_ENTER( woff2.totalCompressedSize )           )
      goto Exit;

    /* Uncompress the stream. */
    error = woff2_uncompress( uncompressed_buf, woff2.uncompressed_size,
                              stream->cursor, woff2.totalCompressedSize );
    if( error )
        goto Exit;

    FT_FRAME_EXIT();

    reconstruct_font( uncompressed_buf, woff2.uncompressed_size,
                      indices, &woff2, &info, &sfnt, memory );

    /* TODO Write table entries. */

    error = FT_THROW( Unimplemented_Feature );
    /* DEBUG - Remove later */
    FT_TRACE2(( "Reached end without errors.\n" ));
    goto Exit;

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

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

    return error;
  }


#undef READ_255USHORT
#undef READ_BASE128
#undef ROUND4
#undef WRITE_USHORT
#undef WRITE_ULONG
#undef WRITE_SHORT
#undef WRITE_SFNT_BUF

#undef N_CONTOUR_STREAM
#undef N_POINTS_STREAM
#undef FLAG_STREAM
#undef GLYPH_STREAM
#undef COMPOSITE_STREAM
#undef BBOX_STREAM
#undef INSTRUCTION_STREAM


/* END */