[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.

* 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): Store decoded points in the glyph buffer.

(compute_bbox): Derive bounding box for a glyph by computing the minimum
and maximum x and y coordinates in the outline.

(reconstruct_glyf): Main routine to reconstruct transformed `glyf' table.

(reconstruct_font): Call `reconstruct_glyf'.

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

* src/sfnt/woff2tags.h: Move out constants to `sfwoff2.h'.
This commit is contained in:
Nikhil Ramakrishnan 2019-07-13 03:14:17 +05:30
parent 7bf104e759
commit f5a1a74c8d
4 changed files with 915 additions and 50 deletions

View File

@ -182,6 +182,34 @@ FT_BEGIN_HEADER
} WOFF2_HeaderRec, *WOFF2_Header;
/**************************************************************************
*
* @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:
@ -199,7 +227,7 @@ FT_BEGIN_HEADER
{
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 */
@ -211,6 +239,60 @@ FT_BEGIN_HEADER
} 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
#endif /* WOFFTYPES_H_ */

View File

@ -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,32 +127,32 @@
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;
}
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;
}
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,23 +362,743 @@
*num_hmetrics = num_metrics;
/* DEBUG - Remove later */
FT_TRACE2(( "num_hmetrics = %d\n", *num_hmetrics ));
return error;
}
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 )
static FT_Int
with_sign( FT_Byte flag,
FT_Int base_val )
{
FT_Error error = FT_Err_Ok;
FT_Stream stream = NULL;
FT_Byte* buf_cursor = NULL;
/* 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;
@ -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,9 +1625,10 @@
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 */
@ -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 */

View File

@ -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,

View File

@ -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 );