From fc65d45a62e1ffec6010cd17f22cfe59668e5592 Mon Sep 17 00:00:00 2001 From: Nikhil Ramakrishnan Date: Sun, 30 Jun 2019 04:31:04 +0530 Subject: [PATCH] [woff2] Uncompress Brotli streams and `face_index' support. WOFF2 compressed stream is now uncompressed if Brotli is available. This data is stored in a separate buffer (uncompressed_buf) because it does not contain direct table data. Certain tables have transformations applied to them, and they must be reconstructed before we can write those tables to the SFNT stream. `face_index' is now being passed as a parameter to `woff2_open_font'. * src/sfnt/sfobjs.c (sfnt_open_font): Add parameter `face_instance_index'. * src/sfnt/sfwoff2.c (woff2_uncompress): New function. (woff2_open_font): Call `woff2_uncompress'. (compute_first_table_offset): Fix return type. * src/sfnt/sfwoff2.h (woff2_open_font): Modify declaration. --- ChangeLog | 22 ++++++++++++++ src/sfnt/sfobjs.c | 7 +++-- src/sfnt/sfwoff2.c | 74 +++++++++++++++++++++++++++++++++++++++++----- src/sfnt/sfwoff2.h | 3 +- 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index e256b87c8..3afc3436a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2019-08-27 Nikhil Ramakrishnan + + [woff2] Uncompress Brotli streams and `face_index' support. + + WOFF2 compressed stream is now uncompressed if Brotli is available. + This data is stored in a separate buffer (uncompressed_buf) because + it does not contain direct table data. Certain tables have + transformations applied to them, and they must be reconstructed + before we can write those tables to the SFNT stream. + + `face_index' is now being passed as a parameter to + `woff2_open_font'. + + * src/sfnt/sfobjs.c (sfnt_open_font): Add parameter + `face_instance_index'. + + * src/sfnt/sfwoff2.c (woff2_uncompress): New function. + (woff2_open_font): Call `woff2_uncompress'. + (compute_first_table_offset): Fix return type. + + * src/sfnt/sfwoff2.h (woff2_open_font): Modify declaration. + 2019-08-27 Nikhil Ramakrishnan * builds/unix/configure.raw: Change argument name to `brotli'. diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index b5a6523cf..67b917822 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -342,7 +342,8 @@ /* synthesized into a TTC with one offset table. */ static FT_Error sfnt_open_font( FT_Stream stream, - TT_Face face ) + TT_Face face, + FT_Int face_instance_index ) { FT_Memory memory = stream->memory; FT_Error error; @@ -393,7 +394,7 @@ if ( FT_STREAM_SEEK( offset ) ) return error; - error = woff2_open_font( stream, face ); + error = woff2_open_font( stream, face, face_instance_index ); if ( error ) return error; @@ -531,7 +532,7 @@ FT_TRACE2(( "SFNT driver\n" )); - error = sfnt_open_font( stream, face ); + error = sfnt_open_font( stream, face, face_instance_index ); if ( error ) return error; diff --git a/src/sfnt/sfwoff2.c b/src/sfnt/sfwoff2.c index 8a5f7c5b3..ec3060200 100644 --- a/src/sfnt/sfwoff2.c +++ b/src/sfnt/sfwoff2.c @@ -23,6 +23,13 @@ #include FT_INTERNAL_STREAM_H +#ifdef FT_CONFIG_OPTION_USE_BROTLI + +#include + +#endif + + /************************************************************************** * * The macro FT_COMPONENT is used in trace mode. It is an implicit @@ -96,7 +103,7 @@ return FT_THROW( Invalid_Table ); if( code == wordCode ) { - /* Read next two bytes and store UInt16 value */ + /* Read next two bytes and store FT_UShort value */ if( FT_READ_USHORT( result_short ) ) return FT_THROW( Invalid_Table ); *value = result_short; @@ -176,7 +183,7 @@ } - static FT_Error + static FT_UInt64 compute_first_table_offset( const WOFF2_Header woff2 ) { FT_Int nn; @@ -196,12 +203,42 @@ } + static FT_Error + woff2_uncompress( FT_Byte* dst, + FT_ULong dst_size, + const FT_Byte* src, + FT_ULong src_size ) + { +#ifdef FT_CONFIG_OPTION_USE_BROTLI + + FT_ULong uncompressed_size = dst_size; + BrotliDecoderResult result; + + result = BrotliDecoderDecompress( + src_size, src, &uncompressed_size, dst); + + if( result != BROTLI_DECODER_RESULT_SUCCESS || + uncompressed_size != dst_size ) + return FT_THROW( Invalid_Table ); + + return FT_Err_Ok; + +#else /* !FT_CONFIG_OPTION_USE_BROTLI */ + + FT_ERROR(( "woff2_uncompress: Brotli support not available.\n" )); + return FT_THROW( Unimplemented_Feature ); + +#endif /* !FT_CONFIG_OPTION_USE_BROTLI */ + } + + /* Replace `face->root.stream' with a stream containing the extracted */ /* SFNT of a WOFF2 font. */ FT_LOCAL_DEF( FT_Error ) woff2_open_font( FT_Stream stream, - TT_Face face ) + TT_Face face, + FT_Int face_index ) { FT_Memory memory = stream->memory; FT_Error error = FT_Err_Ok; @@ -227,6 +264,8 @@ FT_Stream sfnt_stream = NULL; FT_Byte* sfnt_header; + FT_Byte* uncompressed_buf = NULL; + static const FT_Frame_Field woff2_header_fields[] = { #undef FT_STRUCTURE @@ -256,7 +295,7 @@ FT_ASSERT( FT_STREAM_POS() == 0 ); /* DEBUG - Remove later. */ - FT_TRACE2(( "Face index = %ld\n", face->root.face_index )); + FT_TRACE2(( "Face index = %ld\n", face_index )); /* Read WOFF2 Header. */ if ( FT_STREAM_READ_FIELDS( woff2_header_fields, &woff2 ) ) @@ -306,7 +345,6 @@ " tag flags transform origLen transformLen\n" " --------------------------------------------------\n" )); - /* TODO check whether there is sufficient input before FT_READ_*. */ for ( nn = 0; nn < woff2.num_tables; nn++ ) { WOFF2_Table table = tables + nn; @@ -455,7 +493,7 @@ return FT_THROW( Invalid_Table ); /* DEBUG - Remove later */ else - FT_TRACE2(( "glyf and loca are valid.\n" )); + FT_TRACE2(( "glyf and loca indices are valid.\n" )); } } /* Collection directory reading complete. */ @@ -470,7 +508,7 @@ file_offset = ROUND4( woff2.compressed_offset + woff2.totalCompressedSize ); - /* Few more checks before we start reading the tables */ + /* Few more checks before we start reading the tables. */ if( file_offset > woff2.length ) return FT_THROW( Invalid_Table ); @@ -491,6 +529,7 @@ if( file_offset != ( ROUND4( woff2.length ) ) ) return FT_THROW( Invalid_Table ); + /* TODO Add support for uncompression of TTC fonts. */ /* Redirect a TTC to exit for now. */ if( woff2.header_version ) { @@ -551,6 +590,27 @@ (FT_Char)( table->Tag ))); } + if( woff2.uncompressed_size < 1 ) + { + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* Allocate memory for uncompressed table data. */ + if ( FT_ALLOC( uncompressed_buf, woff2.uncompressed_size ) || + FT_FRAME_ENTER( woff2.totalCompressedSize ) ) + goto Exit; + + /* Uncompress the stream. */ + error = woff2_uncompress( uncompressed_buf, woff2.uncompressed_size, + stream->cursor, woff2.totalCompressedSize ); + if( error ) + goto Exit; + + FT_FRAME_EXIT(); + + /* TODO Write table entries. */ + error = FT_THROW( Unimplemented_Feature ); /* DEBUG - Remove later */ FT_TRACE2(( "Reached end without errors.\n" )); diff --git a/src/sfnt/sfwoff2.h b/src/sfnt/sfwoff2.h index 5c3cc3d72..f7e7f43f6 100644 --- a/src/sfnt/sfwoff2.h +++ b/src/sfnt/sfwoff2.h @@ -30,7 +30,8 @@ FT_BEGIN_HEADER FT_LOCAL( FT_Error ) woff2_open_font( FT_Stream stream, - TT_Face face ); + TT_Face face, + FT_Int face_index ); FT_END_HEADER