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'.
--- 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 );