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.cmakelists-updates
parent
f0de218ced
commit
7b275a5af1
11 changed files with 617 additions and 473 deletions
@ -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 <ft2build.h> |
||||
#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 */ |
@ -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 <ft2build.h> |
||||
#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 */ |
@ -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 <ft2build.h> |
||||
#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 */ |
Loading…
Reference in new issue