Commit fc65d45a62e1ffec6010cd17f22cfe59668e5592

Nikhil Ramakrishnan 2019-06-30T04:31:04

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

diff --git a/ChangeLog b/ChangeLog
index e256b87..3afc343 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2019-08-27  Nikhil Ramakrishnan  <ramakrishnan.nikhil@gmail.com>
 
+	[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  <ramakrishnan.nikhil@gmail.com>
+
 	* builds/unix/configure.raw: Change argument name to `brotli'.
 
 2019-08-27  Nikhil Ramakrishnan  <ramakrishnan.nikhil@gmail.com>
diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
index b5a6523..67b9178 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 8a5f7c5..ec30602 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 <brotli/decode.h>
+
+#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 5c3cc3d..f7e7f43 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