shithub: freetype+ttf2subf

Download patch

ref: e3953e5410eaeec7ae49dbda26be6e4b1ca5d26c
parent: 81cf5326d59282265ccc0911050005a4b5d20a99
author: Nikhil Ramakrishnan <[email protected]>
date: Fri Jul 12 23:14:17 EDT 2019

[woff2] Reconstruct transformed `glyf' table.

Reconstruct `glyf' table if it is transformed in the uncompressed
table stream.  Also add necessary structures, macros and functions.

* include/freetype/internal/wofftypes.h (WOFF2_InfoRec,
WOFF2_SubstreamRec, WOFF2_PointRec): New structures.
(WOFF2_TableRec): s/OrigLength/dst_length/.

* src/sfnt/sfwoff2.c (READ_255USHORT, READ_BASE128): Use
`FT_SET_ERROR' to set implicit `error' variable.

(WRITE_SHORT): New macro.

(N_CONTOUR_STREAM, N_POINTS_STREAM, FLAG_STREAM, GLYPH_STREAM,
COMPOSITE_STREAM, BBOX_STREAM, INSTRUCTION_STREAM): New macros to
refer to substreams of the transformed `glyf' tables.

(Read255UShort, ReadBase128): Return errors set by `FT_READ_XXX'
macros.

(with_sign, safe_int_addition): New functions to add sign to values
based on a flag and perform safe addition respectively.

(triplet_decode): Decode variable-length (flag, xCoordinate,
yCoordinate) triplet for a simple glyph.   See

  https://www.w3.org/TR/WOFF2/#triplet_decoding

(store_points, compute_bbox, composteGlyph_size, reconstruct_glyf):
New functions.

(reconstruct_font): Call `reconstruct_glyf'.

* src/sfnt/sfwoff2.h: Add required constants.

* src/sfnt/woff2tags.h: Move out constants to `sfwoff2.h'.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,45 @@
 2019-08-27  Nikhil Ramakrishnan  <[email protected]>
 
+	[woff2] Reconstruct transformed `glyf' table.
+
+	Reconstruct `glyf' table if it is transformed in the uncompressed
+	table stream.  Also add necessary structures, macros and functions.
+
+	* include/freetype/internal/wofftypes.h (WOFF2_InfoRec,
+	WOFF2_SubstreamRec, WOFF2_PointRec): New structures.
+	(WOFF2_TableRec): s/OrigLength/dst_length/.
+
+	* src/sfnt/sfwoff2.c (READ_255USHORT, READ_BASE128): Use
+	`FT_SET_ERROR' to set implicit `error' variable.
+
+	(WRITE_SHORT): New macro.
+
+	(N_CONTOUR_STREAM, N_POINTS_STREAM, FLAG_STREAM, GLYPH_STREAM,
+	COMPOSITE_STREAM, BBOX_STREAM, INSTRUCTION_STREAM): New macros to
+	refer to substreams of the transformed `glyf' tables.
+
+	(Read255UShort, ReadBase128): Return errors set by `FT_READ_XXX'
+	macros.
+
+	(with_sign, safe_int_addition): New functions to add sign to values
+	based on a flag and perform safe addition respectively.
+
+	(triplet_decode): Decode variable-length (flag, xCoordinate,
+	yCoordinate) triplet for a simple glyph.   See
+
+	  https://www.w3.org/TR/WOFF2/#triplet_decoding
+
+	(store_points, compute_bbox, composteGlyph_size, reconstruct_glyf):
+	New functions.
+
+	(reconstruct_font): Call `reconstruct_glyf'.
+
+	* src/sfnt/sfwoff2.h: Add required constants.
+
+	* src/sfnt/woff2tags.h: Move out constants to `sfwoff2.h'.
+
+2019-08-27  Nikhil Ramakrishnan  <[email protected]>
+
 	[woff2] Copy un-transformed tables to sfnt stream.
 
 	Copy un-transformed tables to the sfnt stream.
--- a/include/freetype/internal/wofftypes.h
+++ b/include/freetype/internal/wofftypes.h
@@ -185,6 +185,34 @@
   /**************************************************************************
    *
    * @struct:
+   *   WOFF2_InfoRec
+   *
+   * @description:
+   *   Metadata for WOFF2 font that may be required for reconstruction of
+   *   sfnt tables.
+   *
+   * @fields:
+   *   num_glyphs ::
+   *     Number of glyphs in the font.
+   *
+   *   num_hmetrics ::
+   *     `numberOfHMetrics' field in the `hhea' table.
+   *
+   *   x_mins ::
+   *     `xMin' values of glyph bounding box.
+   */
+  typedef struct  WOFF2_InfoRec_
+  {
+    FT_UShort  num_glyphs;
+    FT_UShort  num_hmetrics;
+    FT_Short*  x_mins;
+
+  } WOFF2_InfoRec, *WOFF2_Info;
+
+
+  /**************************************************************************
+   *
+   * @struct:
    *   WOFF2_TableRec
    *
    * @description:
@@ -199,7 +227,7 @@
   {
     FT_Byte   FlagByte;           /* table type and flags      */
     FT_ULong  Tag;                /* table file offset         */
-    FT_ULong  OrigLength;         /* uncompressed table length */
+    FT_ULong  dst_length;         /* uncompressed table length */
     FT_ULong  TransformLength;    /* transformed length        */
 
     FT_ULong  flags;              /* calculated flags          */
@@ -209,6 +237,60 @@
     FT_ULong  dst_offset;         /* uncompressed table offset */
 
   } WOFF2_TableRec, *WOFF2_Table;
+
+
+  /**************************************************************************
+   *
+   * @struct:
+   *   WOFF2_SubstreamRec
+   *
+   * @description:
+   *   This structure stores information about a substream in the transformed
+   *   `glyf' table in a WOFF2 stream.
+   *
+   * @fields:
+   *   start ::
+   *     Beginning of the substream relative to uncompressed table stream.
+   *
+   *   offset ::
+   *     Offset of the substream relative to uncompressed table stream.
+   *
+   *   size ::
+   *     Size of the substream.
+   */
+  typedef struct  WOFF2_SubstreamRec_
+  {
+    FT_ULong  start;
+    FT_ULong  offset;
+    FT_ULong  size;
+  } WOFF2_SubstreamRec, *WOFF2_Substream;
+
+
+  /**************************************************************************
+   *
+   * @struct:
+   *   WOFF2_PointRec
+   *
+   * @description:
+   *   This structure stores information about a point in the transformed
+   *   `glyf' table in a WOFF2 stream.
+   *
+   * @fields:
+   *   x ::
+   *     x-coordinate.
+   *
+   *   y ::
+   *     y-coordinate.
+   *
+   *   on_curve ::
+   *     on-curve.
+   */
+  typedef struct  WOFF2_PointRec_
+  {
+    FT_Int   x;
+    FT_Int   y;
+    FT_Bool  on_curve;
+  } WOFF2_PointRec, *WOFF2_Point;
 
 
 FT_END_HEADER
--- a/src/sfnt/sfwoff2.c
+++ b/src/sfnt/sfwoff2.c
@@ -40,9 +40,9 @@
 #define FT_COMPONENT  sfwoff2
 
 
-#define READ_255USHORT( var )  Read255UShort( stream, &var )
+#define READ_255USHORT( var )  FT_SET_ERROR( Read255UShort( stream, &var ) )
 
-#define READ_BASE128( var )    ReadBase128( stream, &var )
+#define READ_BASE128( var )    FT_SET_ERROR( ReadBase128( stream, &var ) )
 
 #define ROUND4( var )          ( var + 3 ) & ~3
 
@@ -64,9 +64,26 @@
                                              \
           } 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 )
   {
@@ -110,18 +127,18 @@
     static const FT_Int  wordCode         = 253;
     static const FT_Int  lowestUCode      = 253;
 
-    FT_Error             error;
+    FT_Error             error        = FT_Err_Ok;
     FT_Byte              code;
-    FT_Byte              result_byte      = 0;
-    FT_UShort            result_short     = 0;
+    FT_Byte              result_byte  = 0;
+    FT_UShort            result_short = 0;
 
     if( FT_READ_BYTE( code ) )
-      return FT_THROW( Invalid_Table );
+      return error;
     if( code == wordCode )
     {
       /* Read next two bytes and store FT_UShort value */
       if( FT_READ_USHORT( result_short ) )
-        return FT_THROW( Invalid_Table );
+        return error;
       *value = result_short;
       return FT_Err_Ok;
     }
@@ -128,7 +145,7 @@
     else if( code == oneMoreByteCode1 )
     {
       if( FT_READ_BYTE( result_byte ) )
-        return FT_THROW( Invalid_Table );
+        return error;
       *value = result_byte + lowestUCode;
       return FT_Err_Ok;
     }
@@ -135,7 +152,7 @@
     else if( code == oneMoreByteCode2 )
     {
       if( FT_READ_BYTE( result_byte ) )
-        return FT_THROW( Invalid_Table );
+        return error;
       *value = result_byte + lowestUCode * 2;
       return FT_Err_Ok;
     }
@@ -154,27 +171,26 @@
     FT_ULong  result = 0;
     FT_Int    i;
     FT_Byte   code;
-    FT_Error  error;
+    FT_Error  error  = FT_Err_Ok;
 
     for ( i = 0; i < 5; ++i ) {
       code = 0;
       if( FT_READ_BYTE( code ) )
-        return FT_THROW( Invalid_Table );
+        return error;
 
       /* Leading zeros are invalid. */
-      if ( i == 0 && code == 0x80 ) {
+      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 ){
+      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 ) {
+      if ( ( code & 0x80 ) == 0 )
+      {
         *value = result;
         return FT_Err_Ok;
       }
@@ -200,11 +216,11 @@
       return FT_THROW( Array_Too_Large );
 
     /* DEBUG - Remove later */
-    FT_TRACE2(( "Reallocating %lu to %lu.\n", *offset, (*offset + size) ));
+    /* FT_TRACE2(( "Reallocating %lu to %lu.\n", *offset, (*offset + size) )); */
     /* Reallocate `dst'. */
     if ( FT_REALLOC( dst,
-                     (FT_ULong)(*offset),
-                     (FT_ULong)(*offset + size ) ) )
+                     (FT_ULong)( *offset ),
+                     (FT_ULong)( *offset + size ) ) )
       goto Exit;
 
     /* Copy data. */
@@ -253,6 +269,7 @@
     return offset;
   }
 
+
   static FT_Long
   compute_ULong_sum( FT_Byte*  buf,
                      FT_ULong  size )
@@ -268,7 +285,7 @@
                   ( 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 is not aligned to 4, treat as if it is padded with 0s. */
     if( size != aligned_size )
     {
       v = 0;
@@ -345,6 +362,7 @@
 
     *num_hmetrics = num_metrics;
 
+    /* DEBUG - Remove later */
     FT_TRACE2(( "num_hmetrics = %d\n", *num_hmetrics ));
 
     return error;
@@ -351,17 +369,736 @@
   }
 
 
+  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
-  reconstruct_font( FT_Byte*       transformed_buf,
-                    FT_ULong       transformed_buf_size,
-                    WOFF2_Table*   indices,
-                    WOFF2_Header   woff2,
-                    FT_Byte**      sfnt_bytes,
-                    FT_Memory      memory )
+  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_Error   error  = FT_Err_Ok;
-    FT_Stream  stream = NULL;
-    FT_Byte*   buf_cursor = NULL;
+    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;
 
@@ -398,9 +1135,6 @@
         }
     }
 
-    FT_UNUSED( loca_checksum );
-    FT_UNUSED( checksum );
-
     /* Create a stream for the uncompressed buffer. */
     if ( FT_NEW( stream ) )
       return FT_THROW( Invalid_Table );
@@ -436,6 +1170,8 @@
           return FT_THROW( Invalid_Table );
       }
 
+      info->num_hmetrics = num_hmetrics;
+
       checksum = 0;
       if( ( table.flags & WOFF2_FLAGS_TRANSFORM ) != WOFF2_FLAGS_TRANSFORM )
       {
@@ -461,14 +1197,26 @@
         /* DEBUG - Remove later */
         FT_TRACE2(( "This table has xform.\n" ));
 
-        /* TODO reconstruct transformed font tables! */
-      }
+        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. */
   }
 
 
@@ -484,6 +1232,7 @@
     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;
@@ -613,10 +1362,10 @@
 
       flags |= xform_version;
 
-      if( READ_BASE128( table->OrigLength ) )
+      if( READ_BASE128( table->dst_length ) )
         goto Exit;
 
-      table->TransformLength = table->OrigLength;
+      table->TransformLength = table->dst_length;
 
       if ( ( flags & WOFF2_FLAGS_TRANSFORM ) != 0 )
       {
@@ -647,7 +1396,7 @@
                   (FT_Char)( table->Tag       ),
                   table->FlagByte & 0x3f,
                   ( table->FlagByte >> 6 ) & 0x03,
-                  table->OrigLength,
+                  table->dst_length,
                   table->TransformLength,
                   table->src_length,
                   table->src_offset ));
@@ -876,10 +1625,11 @@
 
     FT_FRAME_EXIT();
 
-    /* TODO Write table entries. */
     reconstruct_font( uncompressed_buf, woff2.uncompressed_size,
-                      indices, &woff2, &sfnt, memory );
+                      indices, &woff2, &info, &sfnt, memory );
 
+    /* TODO Write table entries. */
+
     error = FT_THROW( Unimplemented_Feature );
     /* DEBUG - Remove later */
     FT_TRACE2(( "Reached end without errors.\n" ));
@@ -906,7 +1656,16 @@
 #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 */
--- a/src/sfnt/sfwoff2.h
+++ b/src/sfnt/sfwoff2.h
@@ -28,6 +28,39 @@
 FT_BEGIN_HEADER
 
 
+  /* Leave the first byte open to store flag_byte. */
+#define WOFF2_FLAGS_TRANSFORM   1 << 8
+
+#define WOFF2_SFNT_HEADER_SIZE  12
+#define WOFF2_SFNT_ENTRY_SIZE   16
+
+  /* Suggested max size for output. */
+#define WOFF2_DEFAULT_MAX_SIZE  30 * 1024 * 1024
+
+  /* 98% of Google Fonts have no glyph above 5k bytes. */
+#define WOFF2_DEFAULT_GLYPH_BUF  5120
+
+  /* Composite glyph flags                                     */
+  /* See CompositeGlyph.java in `sfntly' for full definitions  */
+#define FLAG_ARG_1_AND_2_ARE_WORDS     1 << 0
+#define FLAG_WE_HAVE_A_SCALE           1 << 3
+#define FLAG_MORE_COMPONENTS           1 << 5
+#define FLAG_WE_HAVE_AN_X_AND_Y_SCALE  1 << 6
+#define FLAG_WE_HAVE_A_TWO_BY_TWO      1 << 7
+#define FLAG_WE_HAVE_INSTRUCTIONS      1 << 8
+
+  /* Simple glyph flags */
+#define GLYF_ON_CURVE        1 << 0
+#define GLYF_X_SHORT         1 << 1
+#define GLYF_Y_SHORT         1 << 2
+#define GLYF_REPEAT          1 << 3
+#define GLYF_THIS_X_IS_SAME  1 << 4
+#define GLYF_THIS_Y_IS_SAME  1 << 5
+
+  /* Other constants */
+#define CONTOUR_OFFSET_END_POINT  10
+
+
   FT_LOCAL( FT_Error )
   woff2_open_font( FT_Stream  stream,
                    TT_Face    face,
--- a/src/sfnt/woff2tags.h
+++ b/src/sfnt/woff2tags.h
@@ -27,15 +27,6 @@
 FT_BEGIN_HEADER
 
 
-  /* Leave the first byte open to store flag_byte. */
-#define WOFF2_FLAGS_TRANSFORM   1 << 8
-
-#define WOFF2_SFNT_HEADER_SIZE  12
-#define WOFF2_SFNT_ENTRY_SIZE   16
-
-  /* Suggested max size for output. */
-#define WOFF2_DEFAULT_MAX_SIZE  30 * 1024 * 1024
-
   FT_LOCAL( FT_ULong )
   woff2_known_tags( FT_Byte  index );