From 7b275a5af161c71a169dcec65f2f6998c9f2d6d5 Mon Sep 17 00:00:00 2001 From: Nikhil Ramakrishnan Date: Thu, 30 May 2019 23:57:34 +0530 Subject: [sfnt] Separate WOFF sources and headers. Move WOFF sources and headers to separate files. * include/freetype/internal/wofftypes.h, src/sfnt/sfwoff.c, src/sfnt/sfwoff.h: New files. * include/freetype/internal/fttrace.h: Register `sfwoff.c'. * include/freetype/internal/internal.h: Define FT_INTERNAL_WOFF_TYPES_H. * include/freetype/internal/sfnt.h: Include FT_INTERNAL_WOFF_TYPES_H. * include/freetype/internal/tttypes.h: Move out WOFF structures. * src/sfnt/rules.mk: Add `sfwoff.c'. * src/sfnt/sfnt.c: Include `sfwoff.c'. * src/sfnt/sfobjs.c: Include `sfwoff.h', move out WOFF sources. --- ChangeLog | 24 ++ include/freetype/internal/fttrace.h | 1 + include/freetype/internal/internal.h | 1 + include/freetype/internal/sfnt.h | 1 + include/freetype/internal/tttypes.h | 75 ------ include/freetype/internal/wofftypes.h | 112 +++++++++ src/sfnt/rules.mk | 1 + src/sfnt/sfnt.c | 1 + src/sfnt/sfobjs.c | 399 +------------------------------ src/sfnt/sfwoff.c | 434 ++++++++++++++++++++++++++++++++++ src/sfnt/sfwoff.h | 41 ++++ 11 files changed, 617 insertions(+), 473 deletions(-) create mode 100644 include/freetype/internal/wofftypes.h create mode 100644 src/sfnt/sfwoff.c create mode 100644 src/sfnt/sfwoff.h diff --git a/ChangeLog b/ChangeLog index 6513ddd08..55efb87dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2019-05-30 Nikhil Ramakrishnan + + [sfnt] Separate WOFF sources and headers. + + Move WOFF sources and headers to separate files. + + * include/freetype/internal/wofftypes.h, src/sfnt/sfwoff.c, + src/sfnt/sfwoff.h: New files. + + * include/freetype/internal/fttrace.h: Register `sfwoff.c'. + + * include/freetype/internal/internal.h: Define + FT_INTERNAL_WOFF_TYPES_H. + + * include/freetype/internal/sfnt.h: Include FT_INTERNAL_WOFF_TYPES_H. + + * include/freetype/internal/tttypes.h: Move out WOFF structures. + + * src/sfnt/rules.mk: Add `sfwoff.c'. + + * src/sfnt/sfnt.c: Include `sfwoff.c'. + + * src/sfnt/sfobjs.c: Include `sfwoff.h', move out WOFF sources. + 2019-05-30 Werner Lemberg [base] Fix `make multi'. diff --git a/include/freetype/internal/fttrace.h b/include/freetype/internal/fttrace.h index 8089babfb..f5f959804 100644 --- a/include/freetype/internal/fttrace.h +++ b/include/freetype/internal/fttrace.h @@ -48,6 +48,7 @@ FT_TRACE_DEF( cache ) /* cache sub-system (ftcache.c, etc.) */ /* SFNT driver components */ FT_TRACE_DEF( sfdriver ) /* SFNT font driver (sfdriver.c) */ FT_TRACE_DEF( sfobjs ) /* SFNT object handler (sfobjs.c) */ +FT_TRACE_DEF( sfwoff ) /* WOFF format handler (sfwoff.c) */ FT_TRACE_DEF( ttbdf ) /* TrueType embedded BDF (ttbdf.c) */ FT_TRACE_DEF( ttcmap ) /* charmap handler (ttcmap.c) */ FT_TRACE_DEF( ttcolr ) /* glyph layer table (ttcolr.c) */ diff --git a/include/freetype/internal/internal.h b/include/freetype/internal/internal.h index 173d8ad90..3c8830f7e 100644 --- a/include/freetype/internal/internal.h +++ b/include/freetype/internal/internal.h @@ -40,6 +40,7 @@ #define FT_INTERNAL_TRUETYPE_TYPES_H #define FT_INTERNAL_TYPE1_TYPES_H +#define FT_INTERNAL_WOFF_TYPES_H #define FT_INTERNAL_POSTSCRIPT_AUX_H #define FT_INTERNAL_POSTSCRIPT_HINTS_H diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h index 225f40df6..b19241c30 100644 --- a/include/freetype/internal/sfnt.h +++ b/include/freetype/internal/sfnt.h @@ -23,6 +23,7 @@ #include #include FT_INTERNAL_DRIVER_H #include FT_INTERNAL_TRUETYPE_TYPES_H +#include FT_INTERNAL_WOFF_TYPES_H FT_BEGIN_HEADER diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index cb03368b3..23db240e7 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -150,81 +150,6 @@ FT_BEGIN_HEADER } TT_TableRec, *TT_Table; - /************************************************************************** - * - * @struct: - * WOFF_HeaderRec - * - * @description: - * WOFF file format header. - * - * @fields: - * See - * - * https://www.w3.org/TR/WOFF/#WOFFHeader - */ - typedef struct WOFF_HeaderRec_ - { - FT_ULong signature; - FT_ULong flavor; - FT_ULong length; - FT_UShort num_tables; - FT_UShort reserved; - FT_ULong totalSfntSize; - FT_UShort majorVersion; - FT_UShort minorVersion; - FT_ULong metaOffset; - FT_ULong metaLength; - FT_ULong metaOrigLength; - FT_ULong privOffset; - FT_ULong privLength; - - } WOFF_HeaderRec, *WOFF_Header; - - - /************************************************************************** - * - * @struct: - * WOFF_TableRec - * - * @description: - * This structure describes a given table of a WOFF font. - * - * @fields: - * Tag :: - * A four-bytes tag describing the table. - * - * Offset :: - * The offset of the table from the start of the WOFF font in its - * resource. - * - * CompLength :: - * Compressed table length (in bytes). - * - * OrigLength :: - * Uncompressed table length (in bytes). - * - * CheckSum :: - * The table checksum. This value can be ignored. - * - * OrigOffset :: - * The uncompressed table file offset. This value gets computed while - * constructing the (uncompressed) SFNT header. It is not contained in - * the WOFF file. - */ - typedef struct WOFF_TableRec_ - { - FT_ULong Tag; /* table ID */ - FT_ULong Offset; /* table file offset */ - FT_ULong CompLength; /* compressed table length */ - FT_ULong OrigLength; /* uncompressed table length */ - FT_ULong CheckSum; /* uncompressed checksum */ - - FT_ULong OrigOffset; /* uncompressed table file offset */ - /* (not in the WOFF file) */ - } WOFF_TableRec, *WOFF_Table; - - /************************************************************************** * * @struct: diff --git a/include/freetype/internal/wofftypes.h b/include/freetype/internal/wofftypes.h new file mode 100644 index 000000000..ba55bf883 --- /dev/null +++ b/include/freetype/internal/wofftypes.h @@ -0,0 +1,112 @@ +/**************************************************************************** + * + * wofftypes.h + * + * Basic WOFF/WOFF2 type definitions and interface (specification + * only). + * + * Copyright (C) 1996-2019 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + + +#ifndef WOFFTYPES_H_ +#define WOFFTYPES_H_ + + +#include +#include FT_TRUETYPE_TABLES_H +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @struct: + * WOFF_HeaderRec + * + * @description: + * WOFF file format header. + * + * @fields: + * See + * + * https://www.w3.org/TR/WOFF/#WOFFHeader + */ + typedef struct WOFF_HeaderRec_ + { + FT_ULong signature; + FT_ULong flavor; + FT_ULong length; + FT_UShort num_tables; + FT_UShort reserved; + FT_ULong totalSfntSize; + FT_UShort majorVersion; + FT_UShort minorVersion; + FT_ULong metaOffset; + FT_ULong metaLength; + FT_ULong metaOrigLength; + FT_ULong privOffset; + FT_ULong privLength; + + } WOFF_HeaderRec, *WOFF_Header; + + + /************************************************************************** + * + * @struct: + * WOFF_TableRec + * + * @description: + * This structure describes a given table of a WOFF font. + * + * @fields: + * Tag :: + * A four-bytes tag describing the table. + * + * Offset :: + * The offset of the table from the start of the WOFF font in its + * resource. + * + * CompLength :: + * Compressed table length (in bytes). + * + * OrigLength :: + * Uncompressed table length (in bytes). + * + * CheckSum :: + * The table checksum. This value can be ignored. + * + * OrigOffset :: + * The uncompressed table file offset. This value gets computed while + * constructing the (uncompressed) SFNT header. It is not contained in + * the WOFF file. + */ + typedef struct WOFF_TableRec_ + { + FT_ULong Tag; /* table ID */ + FT_ULong Offset; /* table file offset */ + FT_ULong CompLength; /* compressed table length */ + FT_ULong OrigLength; /* uncompressed table length */ + FT_ULong CheckSum; /* uncompressed checksum */ + + FT_ULong OrigOffset; /* uncompressed table file offset */ + /* (not in the WOFF file) */ + } WOFF_TableRec, *WOFF_Table; + + +FT_END_HEADER + +#endif /* WOFFTYPES_H_ */ + + +/* END */ diff --git a/src/sfnt/rules.mk b/src/sfnt/rules.mk index ff9e7c611..ee3314eac 100644 --- a/src/sfnt/rules.mk +++ b/src/sfnt/rules.mk @@ -31,6 +31,7 @@ SFNT_COMPILE := $(CC) $(ANSIFLAGS) \ SFNT_DRV_SRC := $(SFNT_DIR)/pngshim.c \ $(SFNT_DIR)/sfdriver.c \ $(SFNT_DIR)/sfobjs.c \ + $(SFNT_DIR)/sfwoff.c \ $(SFNT_DIR)/ttbdf.c \ $(SFNT_DIR)/ttcmap.c \ $(SFNT_DIR)/ttcolr.c \ diff --git a/src/sfnt/sfnt.c b/src/sfnt/sfnt.c index 5a503f30c..b4faf34a3 100644 --- a/src/sfnt/sfnt.c +++ b/src/sfnt/sfnt.c @@ -22,6 +22,7 @@ #include "pngshim.c" #include "sfdriver.c" #include "sfobjs.c" +#include "sfwoff.c" #include "ttbdf.c" #include "ttcmap.c" #include "ttcolr.c" diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 0b43d251b..6edf3ae1d 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -21,13 +21,13 @@ #include "ttload.h" #include "ttcmap.h" #include "ttkern.h" +#include "sfwoff.h" #include FT_INTERNAL_SFNT_H #include FT_INTERNAL_DEBUG_H #include FT_TRUETYPE_IDS_H #include FT_TRUETYPE_TAGS_H #include FT_SERVICE_POSTSCRIPT_CMAPS_H #include FT_SFNT_NAMES_H -#include FT_GZIP_H #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT #include FT_SERVICE_MULTIPLE_MASTERS_H @@ -337,403 +337,6 @@ } -#define WRITE_USHORT( p, v ) \ - do \ - { \ - *(p)++ = (FT_Byte)( (v) >> 8 ); \ - *(p)++ = (FT_Byte)( (v) >> 0 ); \ - \ - } while ( 0 ) - -#define WRITE_ULONG( p, v ) \ - do \ - { \ - *(p)++ = (FT_Byte)( (v) >> 24 ); \ - *(p)++ = (FT_Byte)( (v) >> 16 ); \ - *(p)++ = (FT_Byte)( (v) >> 8 ); \ - *(p)++ = (FT_Byte)( (v) >> 0 ); \ - \ - } while ( 0 ) - - - static void - sfnt_stream_close( FT_Stream stream ) - { - FT_Memory memory = stream->memory; - - - FT_FREE( stream->base ); - - stream->size = 0; - stream->base = NULL; - stream->close = NULL; - } - - - FT_CALLBACK_DEF( int ) - compare_offsets( const void* a, - const void* b ) - { - WOFF_Table table1 = *(WOFF_Table*)a; - WOFF_Table table2 = *(WOFF_Table*)b; - - FT_ULong offset1 = table1->Offset; - FT_ULong offset2 = table2->Offset; - - - if ( offset1 > offset2 ) - return 1; - else if ( offset1 < offset2 ) - return -1; - else - return 0; - } - - - /* Replace `face->root.stream' with a stream containing the extracted */ - /* SFNT of a WOFF font. */ - - static FT_Error - woff_open_font( FT_Stream stream, - TT_Face face ) - { - FT_Memory memory = stream->memory; - FT_Error error = FT_Err_Ok; - - WOFF_HeaderRec woff; - WOFF_Table tables = NULL; - WOFF_Table* indices = NULL; - - FT_ULong woff_offset; - - FT_Byte* sfnt = NULL; - FT_Stream sfnt_stream = NULL; - - FT_Byte* sfnt_header; - FT_ULong sfnt_offset; - - FT_Int nn; - FT_ULong old_tag = 0; - - static const FT_Frame_Field woff_header_fields[] = - { -#undef FT_STRUCTURE -#define FT_STRUCTURE WOFF_HeaderRec - - FT_FRAME_START( 44 ), - FT_FRAME_ULONG ( signature ), - FT_FRAME_ULONG ( flavor ), - FT_FRAME_ULONG ( length ), - FT_FRAME_USHORT( num_tables ), - FT_FRAME_USHORT( reserved ), - FT_FRAME_ULONG ( totalSfntSize ), - FT_FRAME_USHORT( majorVersion ), - FT_FRAME_USHORT( minorVersion ), - FT_FRAME_ULONG ( metaOffset ), - FT_FRAME_ULONG ( metaLength ), - FT_FRAME_ULONG ( metaOrigLength ), - FT_FRAME_ULONG ( privOffset ), - FT_FRAME_ULONG ( privLength ), - FT_FRAME_END - }; - - - FT_ASSERT( stream == face->root.stream ); - FT_ASSERT( FT_STREAM_POS() == 0 ); - - if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) ) - return error; - - /* Make sure we don't recurse back here or hit TTC code. */ - if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf ) - return FT_THROW( Invalid_Table ); - - /* Miscellaneous checks. */ - if ( woff.length != stream->size || - woff.num_tables == 0 || - 44 + woff.num_tables * 20UL >= woff.length || - 12 + woff.num_tables * 16UL >= woff.totalSfntSize || - ( woff.totalSfntSize & 3 ) != 0 || - ( woff.metaOffset == 0 && ( woff.metaLength != 0 || - woff.metaOrigLength != 0 ) ) || - ( woff.metaLength != 0 && woff.metaOrigLength == 0 ) || - ( woff.privOffset == 0 && woff.privLength != 0 ) ) - { - FT_ERROR(( "woff_font_open: invalid WOFF header\n" )); - return FT_THROW( Invalid_Table ); - } - - /* Don't trust `totalSfntSize' before thorough checks. */ - if ( FT_ALLOC( sfnt, 12 + woff.num_tables * 16UL ) || - FT_NEW( sfnt_stream ) ) - goto Exit; - - sfnt_header = sfnt; - - /* Write sfnt header. */ - { - FT_UInt searchRange, entrySelector, rangeShift, x; - - - x = woff.num_tables; - entrySelector = 0; - while ( x ) - { - x >>= 1; - entrySelector += 1; - } - entrySelector--; - - searchRange = ( 1 << entrySelector ) * 16; - rangeShift = woff.num_tables * 16 - searchRange; - - WRITE_ULONG ( sfnt_header, woff.flavor ); - WRITE_USHORT( sfnt_header, woff.num_tables ); - WRITE_USHORT( sfnt_header, searchRange ); - WRITE_USHORT( sfnt_header, entrySelector ); - WRITE_USHORT( sfnt_header, rangeShift ); - } - - /* While the entries in the sfnt header must be sorted by the */ - /* tag value, the tables themselves are not. We thus have to */ - /* sort them by offset and check that they don't overlap. */ - - if ( FT_NEW_ARRAY( tables, woff.num_tables ) || - FT_NEW_ARRAY( indices, woff.num_tables ) ) - goto Exit; - - FT_TRACE2(( "\n" - " tag offset compLen origLen checksum\n" - " -------------------------------------------\n" )); - - if ( FT_FRAME_ENTER( 20L * woff.num_tables ) ) - goto Exit; - - for ( nn = 0; nn < woff.num_tables; nn++ ) - { - WOFF_Table table = tables + nn; - - table->Tag = FT_GET_TAG4(); - table->Offset = FT_GET_ULONG(); - table->CompLength = FT_GET_ULONG(); - table->OrigLength = FT_GET_ULONG(); - table->CheckSum = FT_GET_ULONG(); - - FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx %08lx\n", - (FT_Char)( table->Tag >> 24 ), - (FT_Char)( table->Tag >> 16 ), - (FT_Char)( table->Tag >> 8 ), - (FT_Char)( table->Tag ), - table->Offset, - table->CompLength, - table->OrigLength, - table->CheckSum )); - - if ( table->Tag <= old_tag ) - { - FT_FRAME_EXIT(); - - FT_ERROR(( "woff_font_open: table tags are not sorted\n" )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - - old_tag = table->Tag; - indices[nn] = table; - } - - FT_FRAME_EXIT(); - - /* Sort by offset. */ - - ft_qsort( indices, - woff.num_tables, - sizeof ( WOFF_Table ), - compare_offsets ); - - /* Check offsets and lengths. */ - - woff_offset = 44 + woff.num_tables * 20L; - sfnt_offset = 12 + woff.num_tables * 16L; - - for ( nn = 0; nn < woff.num_tables; nn++ ) - { - WOFF_Table table = indices[nn]; - - - if ( table->Offset != woff_offset || - table->CompLength > woff.length || - table->Offset > woff.length - table->CompLength || - table->OrigLength > woff.totalSfntSize || - sfnt_offset > woff.totalSfntSize - table->OrigLength || - table->CompLength > table->OrigLength ) - { - FT_ERROR(( "woff_font_open: invalid table offsets\n" )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - - table->OrigOffset = sfnt_offset; - - /* The offsets must be multiples of 4. */ - woff_offset += ( table->CompLength + 3 ) & ~3U; - sfnt_offset += ( table->OrigLength + 3 ) & ~3U; - } - - /* - * Final checks! - * - * We don't decode and check the metadata block. - * We don't check table checksums either. - * But other than those, I think we implement all - * `MUST' checks from the spec. - */ - - if ( woff.metaOffset ) - { - if ( woff.metaOffset != woff_offset || - woff.metaOffset + woff.metaLength > woff.length ) - { - FT_ERROR(( "woff_font_open:" - " invalid `metadata' offset or length\n" )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - - /* We have padding only ... */ - woff_offset += woff.metaLength; - } - - if ( woff.privOffset ) - { - /* ... if it isn't the last block. */ - woff_offset = ( woff_offset + 3 ) & ~3U; - - if ( woff.privOffset != woff_offset || - woff.privOffset + woff.privLength > woff.length ) - { - FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - - /* No padding for the last block. */ - woff_offset += woff.privLength; - } - - if ( sfnt_offset != woff.totalSfntSize || - woff_offset != woff.length ) - { - FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - - /* Now use `totalSfntSize'. */ - if ( FT_REALLOC( sfnt, - 12 + woff.num_tables * 16UL, - woff.totalSfntSize ) ) - goto Exit; - - sfnt_header = sfnt + 12; - - /* Write the tables. */ - - for ( nn = 0; nn < woff.num_tables; nn++ ) - { - WOFF_Table table = tables + nn; - - - /* Write SFNT table entry. */ - WRITE_ULONG( sfnt_header, table->Tag ); - WRITE_ULONG( sfnt_header, table->CheckSum ); - WRITE_ULONG( sfnt_header, table->OrigOffset ); - WRITE_ULONG( sfnt_header, table->OrigLength ); - - /* Write table data. */ - if ( FT_STREAM_SEEK( table->Offset ) || - FT_FRAME_ENTER( table->CompLength ) ) - goto Exit; - - if ( table->CompLength == table->OrigLength ) - { - /* Uncompressed data; just copy. */ - ft_memcpy( sfnt + table->OrigOffset, - stream->cursor, - table->OrigLength ); - } - else - { -#ifdef FT_CONFIG_OPTION_USE_ZLIB - - /* Uncompress with zlib. */ - FT_ULong output_len = table->OrigLength; - - - error = FT_Gzip_Uncompress( memory, - sfnt + table->OrigOffset, &output_len, - stream->cursor, table->CompLength ); - if ( error ) - goto Exit; - if ( output_len != table->OrigLength ) - { - FT_ERROR(( "woff_font_open: compressed table length mismatch\n" )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - -#else /* !FT_CONFIG_OPTION_USE_ZLIB */ - - error = FT_THROW( Unimplemented_Feature ); - goto Exit; - -#endif /* !FT_CONFIG_OPTION_USE_ZLIB */ - } - - FT_FRAME_EXIT(); - - /* We don't check whether the padding bytes in the WOFF file are */ - /* actually '\0'. For the output, however, we do set them properly. */ - sfnt_offset = table->OrigOffset + table->OrigLength; - while ( sfnt_offset & 3 ) - { - sfnt[sfnt_offset] = '\0'; - sfnt_offset++; - } - } - - /* Ok! Finally ready. Swap out stream and return. */ - FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize ); - sfnt_stream->memory = stream->memory; - sfnt_stream->close = sfnt_stream_close; - - FT_Stream_Free( - face->root.stream, - ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 ); - - face->root.stream = sfnt_stream; - - face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; - - Exit: - FT_FREE( tables ); - FT_FREE( indices ); - - if ( error ) - { - FT_FREE( sfnt ); - FT_Stream_Close( sfnt_stream ); - FT_FREE( sfnt_stream ); - } - - return error; - } - - -#undef WRITE_USHORT -#undef WRITE_ULONG - - /* Fill in face->ttc_header. If the font is not a TTC, it is */ /* synthesized into a TTC with one offset table. */ static FT_Error diff --git a/src/sfnt/sfwoff.c b/src/sfnt/sfwoff.c new file mode 100644 index 000000000..ca4821a20 --- /dev/null +++ b/src/sfnt/sfwoff.c @@ -0,0 +1,434 @@ +/**************************************************************************** + * + * sfwoff.c + * + * WOFF format management (base). + * + * Copyright (C) 1996-2019 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + + +#include +#include "sfwoff.h" +#include FT_TRUETYPE_TAGS_H +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_GZIP_H + + + /************************************************************************** + * + * The macro FT_COMPONENT is used in trace mode. It is an implicit + * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log + * messages during execution. + */ +#undef FT_COMPONENT +#define FT_COMPONENT sfwoff + + +#define WRITE_USHORT( p, v ) \ + do \ + { \ + *(p)++ = (FT_Byte)( (v) >> 8 ); \ + *(p)++ = (FT_Byte)( (v) >> 0 ); \ + \ + } while ( 0 ) + +#define WRITE_ULONG( p, v ) \ + do \ + { \ + *(p)++ = (FT_Byte)( (v) >> 24 ); \ + *(p)++ = (FT_Byte)( (v) >> 16 ); \ + *(p)++ = (FT_Byte)( (v) >> 8 ); \ + *(p)++ = (FT_Byte)( (v) >> 0 ); \ + \ + } while ( 0 ) + + + static void + sfnt_stream_close( FT_Stream stream ) + { + FT_Memory memory = stream->memory; + + + FT_FREE( stream->base ); + + stream->size = 0; + stream->base = NULL; + stream->close = NULL; + } + + + FT_CALLBACK_DEF( int ) + compare_offsets( const void* a, + const void* b ) + { + WOFF_Table table1 = *(WOFF_Table*)a; + WOFF_Table table2 = *(WOFF_Table*)b; + + FT_ULong offset1 = table1->Offset; + FT_ULong offset2 = table2->Offset; + + + if ( offset1 > offset2 ) + return 1; + else if ( offset1 < offset2 ) + return -1; + else + return 0; + } + + + /* Replace `face->root.stream' with a stream containing the extracted */ + /* SFNT of a WOFF font. */ + + FT_LOCAL_DEF( FT_Error ) + woff_open_font( FT_Stream stream, + TT_Face face ) + { + FT_Memory memory = stream->memory; + FT_Error error = FT_Err_Ok; + + WOFF_HeaderRec woff; + WOFF_Table tables = NULL; + WOFF_Table* indices = NULL; + + FT_ULong woff_offset; + + FT_Byte* sfnt = NULL; + FT_Stream sfnt_stream = NULL; + + FT_Byte* sfnt_header; + FT_ULong sfnt_offset; + + FT_Int nn; + FT_ULong old_tag = 0; + + static const FT_Frame_Field woff_header_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE WOFF_HeaderRec + + FT_FRAME_START( 44 ), + FT_FRAME_ULONG ( signature ), + FT_FRAME_ULONG ( flavor ), + FT_FRAME_ULONG ( length ), + FT_FRAME_USHORT( num_tables ), + FT_FRAME_USHORT( reserved ), + FT_FRAME_ULONG ( totalSfntSize ), + FT_FRAME_USHORT( majorVersion ), + FT_FRAME_USHORT( minorVersion ), + FT_FRAME_ULONG ( metaOffset ), + FT_FRAME_ULONG ( metaLength ), + FT_FRAME_ULONG ( metaOrigLength ), + FT_FRAME_ULONG ( privOffset ), + FT_FRAME_ULONG ( privLength ), + FT_FRAME_END + }; + + + FT_ASSERT( stream == face->root.stream ); + FT_ASSERT( FT_STREAM_POS() == 0 ); + + if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) ) + return error; + + /* Make sure we don't recurse back here or hit TTC code. */ + if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf ) + return FT_THROW( Invalid_Table ); + + /* Miscellaneous checks. */ + if ( woff.length != stream->size || + woff.num_tables == 0 || + 44 + woff.num_tables * 20UL >= woff.length || + 12 + woff.num_tables * 16UL >= woff.totalSfntSize || + ( woff.totalSfntSize & 3 ) != 0 || + ( woff.metaOffset == 0 && ( woff.metaLength != 0 || + woff.metaOrigLength != 0 ) ) || + ( woff.metaLength != 0 && woff.metaOrigLength == 0 ) || + ( woff.privOffset == 0 && woff.privLength != 0 ) ) + { + FT_ERROR(( "woff_font_open: invalid WOFF header\n" )); + return FT_THROW( Invalid_Table ); + } + + /* Don't trust `totalSfntSize' before thorough checks. */ + if ( FT_ALLOC( sfnt, 12 + woff.num_tables * 16UL ) || + FT_NEW( sfnt_stream ) ) + goto Exit; + + sfnt_header = sfnt; + + /* Write sfnt header. */ + { + FT_UInt searchRange, entrySelector, rangeShift, x; + + + x = woff.num_tables; + entrySelector = 0; + while ( x ) + { + x >>= 1; + entrySelector += 1; + } + entrySelector--; + + searchRange = ( 1 << entrySelector ) * 16; + rangeShift = woff.num_tables * 16 - searchRange; + + WRITE_ULONG ( sfnt_header, woff.flavor ); + WRITE_USHORT( sfnt_header, woff.num_tables ); + WRITE_USHORT( sfnt_header, searchRange ); + WRITE_USHORT( sfnt_header, entrySelector ); + WRITE_USHORT( sfnt_header, rangeShift ); + } + + /* While the entries in the sfnt header must be sorted by the */ + /* tag value, the tables themselves are not. We thus have to */ + /* sort them by offset and check that they don't overlap. */ + + if ( FT_NEW_ARRAY( tables, woff.num_tables ) || + FT_NEW_ARRAY( indices, woff.num_tables ) ) + goto Exit; + + FT_TRACE2(( "\n" + " tag offset compLen origLen checksum\n" + " -------------------------------------------\n" )); + + if ( FT_FRAME_ENTER( 20L * woff.num_tables ) ) + goto Exit; + + for ( nn = 0; nn < woff.num_tables; nn++ ) + { + WOFF_Table table = tables + nn; + + table->Tag = FT_GET_TAG4(); + table->Offset = FT_GET_ULONG(); + table->CompLength = FT_GET_ULONG(); + table->OrigLength = FT_GET_ULONG(); + table->CheckSum = FT_GET_ULONG(); + + FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx %08lx\n", + (FT_Char)( table->Tag >> 24 ), + (FT_Char)( table->Tag >> 16 ), + (FT_Char)( table->Tag >> 8 ), + (FT_Char)( table->Tag ), + table->Offset, + table->CompLength, + table->OrigLength, + table->CheckSum )); + + if ( table->Tag <= old_tag ) + { + FT_FRAME_EXIT(); + + FT_ERROR(( "woff_font_open: table tags are not sorted\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + old_tag = table->Tag; + indices[nn] = table; + } + + FT_FRAME_EXIT(); + + /* Sort by offset. */ + + ft_qsort( indices, + woff.num_tables, + sizeof ( WOFF_Table ), + compare_offsets ); + + /* Check offsets and lengths. */ + + woff_offset = 44 + woff.num_tables * 20L; + sfnt_offset = 12 + woff.num_tables * 16L; + + for ( nn = 0; nn < woff.num_tables; nn++ ) + { + WOFF_Table table = indices[nn]; + + + if ( table->Offset != woff_offset || + table->CompLength > woff.length || + table->Offset > woff.length - table->CompLength || + table->OrigLength > woff.totalSfntSize || + sfnt_offset > woff.totalSfntSize - table->OrigLength || + table->CompLength > table->OrigLength ) + { + FT_ERROR(( "woff_font_open: invalid table offsets\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + table->OrigOffset = sfnt_offset; + + /* The offsets must be multiples of 4. */ + woff_offset += ( table->CompLength + 3 ) & ~3U; + sfnt_offset += ( table->OrigLength + 3 ) & ~3U; + } + + /* + * Final checks! + * + * We don't decode and check the metadata block. + * We don't check table checksums either. + * But other than those, I think we implement all + * `MUST' checks from the spec. + */ + + if ( woff.metaOffset ) + { + if ( woff.metaOffset != woff_offset || + woff.metaOffset + woff.metaLength > woff.length ) + { + FT_ERROR(( "woff_font_open:" + " invalid `metadata' offset or length\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* We have padding only ... */ + woff_offset += woff.metaLength; + } + + if ( woff.privOffset ) + { + /* ... if it isn't the last block. */ + woff_offset = ( woff_offset + 3 ) & ~3U; + + if ( woff.privOffset != woff_offset || + woff.privOffset + woff.privLength > woff.length ) + { + FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* No padding for the last block. */ + woff_offset += woff.privLength; + } + + if ( sfnt_offset != woff.totalSfntSize || + woff_offset != woff.length ) + { + FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + + /* Now use `totalSfntSize'. */ + if ( FT_REALLOC( sfnt, + 12 + woff.num_tables * 16UL, + woff.totalSfntSize ) ) + goto Exit; + + sfnt_header = sfnt + 12; + + /* Write the tables. */ + + for ( nn = 0; nn < woff.num_tables; nn++ ) + { + WOFF_Table table = tables + nn; + + + /* Write SFNT table entry. */ + WRITE_ULONG( sfnt_header, table->Tag ); + WRITE_ULONG( sfnt_header, table->CheckSum ); + WRITE_ULONG( sfnt_header, table->OrigOffset ); + WRITE_ULONG( sfnt_header, table->OrigLength ); + + /* Write table data. */ + if ( FT_STREAM_SEEK( table->Offset ) || + FT_FRAME_ENTER( table->CompLength ) ) + goto Exit; + + if ( table->CompLength == table->OrigLength ) + { + /* Uncompressed data; just copy. */ + ft_memcpy( sfnt + table->OrigOffset, + stream->cursor, + table->OrigLength ); + } + else + { +#ifdef FT_CONFIG_OPTION_USE_ZLIB + + /* Uncompress with zlib. */ + FT_ULong output_len = table->OrigLength; + + + error = FT_Gzip_Uncompress( memory, + sfnt + table->OrigOffset, &output_len, + stream->cursor, table->CompLength ); + if ( error ) + goto Exit; + if ( output_len != table->OrigLength ) + { + FT_ERROR(( "woff_font_open: compressed table length mismatch\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + +#else /* !FT_CONFIG_OPTION_USE_ZLIB */ + + error = FT_THROW( Unimplemented_Feature ); + goto Exit; + +#endif /* !FT_CONFIG_OPTION_USE_ZLIB */ + } + + FT_FRAME_EXIT(); + + /* We don't check whether the padding bytes in the WOFF file are */ + /* actually '\0'. For the output, however, we do set them properly. */ + sfnt_offset = table->OrigOffset + table->OrigLength; + while ( sfnt_offset & 3 ) + { + sfnt[sfnt_offset] = '\0'; + sfnt_offset++; + } + } + + /* Ok! Finally ready. Swap out stream and return. */ + FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize ); + sfnt_stream->memory = stream->memory; + sfnt_stream->close = sfnt_stream_close; + + FT_Stream_Free( + face->root.stream, + ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 ); + + face->root.stream = sfnt_stream; + + face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; + + Exit: + FT_FREE( tables ); + FT_FREE( indices ); + + if ( error ) + { + FT_FREE( sfnt ); + FT_Stream_Close( sfnt_stream ); + FT_FREE( sfnt_stream ); + } + + return error; + } + + +#undef WRITE_USHORT +#undef WRITE_ULONG + + +/* END */ diff --git a/src/sfnt/sfwoff.h b/src/sfnt/sfwoff.h new file mode 100644 index 000000000..15495c32a --- /dev/null +++ b/src/sfnt/sfwoff.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * sfwoff.h + * + * WOFFF format management (specification). + * + * Copyright (C) 1996-2019 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + + +#ifndef SFWOFF_H_ +#define SFWOFF_H_ + + +#include +#include FT_INTERNAL_SFNT_H +#include FT_INTERNAL_OBJECTS_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + woff_open_font( FT_Stream stream, + TT_Face face ); + + +FT_END_HEADER + +#endif /* SFWOFF_H_ */ + + +/* END */ -- cgit v1.2.1