Implement support for WOFF containers.
We simply synthesize a SFNT from the WOFF, create a memory stream for the new data, and load the SFNT as usual. Does NOT add any API to access WOFF metadata or private blocks. * include/freetype/internal/tttypes.h (WOFF_HeaderRec, WOFF_TableRec): New structures. * include/freetype/tttags.h (TTAG_wOFF): New macro. * src/base/ftobjs.c (FT_Open_Face): Set `stream' after calling `open_face'. * src/sfnt/sfobjs.c [FT_CONFIG_OPTION_SYSTEM_ZLIB]: Include `FT_GZIP_H'. (WRITE_BYTE, WRITE_USHORT, WRITE_ULONG): New temporary macros for writing to a stream. (sfnt_stream_close, compare_offsets, woff_open_font): New functions. (sfnt_open_font): Handle `TTAG_wOFF'. (sfnt_init_face): Set `stream' after calling `sfnt_open_font'. * src/truetype/ttobjs.c (tt_face_init): Set `stream' after calling `sfnt->init_face'. * src/base/ftobjs.c (open_face): Use a pointer to FT_Stream as an argument so that a changed stream survives. Update callers.
This commit is contained in:
parent
dc240524ff
commit
d689d1cf78
33
ChangeLog
33
ChangeLog
|
@ -1,3 +1,36 @@
|
|||
2013-08-28 Werner Lemberg <wl@gnu.org>
|
||||
Behdad Esfahbod <behdad@google.com>
|
||||
|
||||
Implement support for WOFF containers.
|
||||
|
||||
We simply synthesize a SFNT from the WOFF, create a memory stream
|
||||
for the new data, and load the SFNT as usual.
|
||||
|
||||
Does NOT add any API to access WOFF metadata or private blocks.
|
||||
|
||||
* include/freetype/internal/tttypes.h (WOFF_HeaderRec,
|
||||
WOFF_TableRec): New structures.
|
||||
|
||||
* include/freetype/tttags.h (TTAG_wOFF): New macro.
|
||||
|
||||
* src/base/ftobjs.c (FT_Open_Face): Set `stream' after calling
|
||||
`open_face'.
|
||||
|
||||
* src/sfnt/sfobjs.c [FT_CONFIG_OPTION_SYSTEM_ZLIB]: Include
|
||||
`FT_GZIP_H'.
|
||||
(WRITE_BYTE, WRITE_USHORT, WRITE_ULONG): New temporary macros for
|
||||
writing to a stream.
|
||||
(sfnt_stream_close, compare_offsets, woff_open_font): New functions.
|
||||
(sfnt_open_font): Handle `TTAG_wOFF'.
|
||||
(sfnt_init_face): Set `stream' after calling `sfnt_open_font'.
|
||||
|
||||
* src/truetype/ttobjs.c (tt_face_init): Set `stream' after calling
|
||||
`sfnt->init_face'.
|
||||
|
||||
* src/base/ftobjs.c (open_face): Use a pointer to FT_Stream as an
|
||||
argument so that a changed stream survives.
|
||||
Update callers.
|
||||
|
||||
2013-08-28 Werner Lemberg <wl@gnu.org>
|
||||
|
||||
[gzip] New function `FT_Gzip_Uncompress'.
|
||||
|
|
|
@ -137,6 +137,71 @@ FT_BEGIN_HEADER
|
|||
} TT_TableRec, *TT_Table;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* WOFF_HeaderRec */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* WOFF file format header. */
|
||||
/* */
|
||||
/* <Fields> */
|
||||
/* See */
|
||||
/* */
|
||||
/* http://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 :: Unompressed table length (in bytes). */
|
||||
/* */
|
||||
/* CheckSum :: The table checksum. This value can be ignored. */
|
||||
/* */
|
||||
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> */
|
||||
|
|
|
@ -100,6 +100,7 @@ FT_BEGIN_HEADER
|
|||
#define TTAG_VDMX FT_MAKE_TAG( 'V', 'D', 'M', 'X' )
|
||||
#define TTAG_vhea FT_MAKE_TAG( 'v', 'h', 'e', 'a' )
|
||||
#define TTAG_vmtx FT_MAKE_TAG( 'v', 'm', 't', 'x' )
|
||||
#define TTAG_wOFF FT_MAKE_TAG( 'w', 'O', 'F', 'F' )
|
||||
|
||||
|
||||
FT_END_HEADER
|
||||
|
|
|
@ -1133,7 +1133,7 @@
|
|||
/* */
|
||||
static FT_Error
|
||||
open_face( FT_Driver driver,
|
||||
FT_Stream stream,
|
||||
FT_Stream *astream,
|
||||
FT_Bool external_stream,
|
||||
FT_Long face_index,
|
||||
FT_Int num_params,
|
||||
|
@ -1142,10 +1142,11 @@
|
|||
{
|
||||
FT_Memory memory;
|
||||
FT_Driver_Class clazz;
|
||||
FT_Face face = 0;
|
||||
FT_Error error, error2;
|
||||
FT_Face face = NULL;
|
||||
FT_Face_Internal internal = NULL;
|
||||
|
||||
FT_Error error, error2;
|
||||
|
||||
|
||||
clazz = driver->clazz;
|
||||
memory = driver->root.memory;
|
||||
|
@ -1156,13 +1157,12 @@
|
|||
|
||||
face->driver = driver;
|
||||
face->memory = memory;
|
||||
face->stream = stream;
|
||||
face->stream = *astream;
|
||||
|
||||
/* set the FT_FACE_FLAG_EXTERNAL_STREAM bit for FT_Done_Face */
|
||||
if ( external_stream )
|
||||
face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM;
|
||||
|
||||
|
||||
if ( FT_NEW( internal ) )
|
||||
goto Fail;
|
||||
|
||||
|
@ -1183,11 +1183,12 @@
|
|||
#endif
|
||||
|
||||
if ( clazz->init_face )
|
||||
error = clazz->init_face( stream,
|
||||
error = clazz->init_face( *astream,
|
||||
face,
|
||||
(FT_Int)face_index,
|
||||
num_params,
|
||||
params );
|
||||
*astream = face->stream; /* Stream may have been changed. */
|
||||
if ( error )
|
||||
goto Fail;
|
||||
|
||||
|
@ -2075,7 +2076,7 @@
|
|||
params = args->params;
|
||||
}
|
||||
|
||||
error = open_face( driver, stream, external_stream, face_index,
|
||||
error = open_face( driver, &stream, external_stream, face_index,
|
||||
num_params, params, &face );
|
||||
if ( !error )
|
||||
goto Success;
|
||||
|
@ -2111,7 +2112,7 @@
|
|||
params = args->params;
|
||||
}
|
||||
|
||||
error = open_face( driver, stream, external_stream, face_index,
|
||||
error = open_face( driver, &stream, external_stream, face_index,
|
||||
num_params, params, &face );
|
||||
if ( !error )
|
||||
goto Success;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include FT_TRUETYPE_TAGS_H
|
||||
#include FT_SERVICE_POSTSCRIPT_CMAPS_H
|
||||
#include FT_SFNT_NAMES_H
|
||||
#include FT_GZIP_H
|
||||
#include "sferrors.h"
|
||||
|
||||
#ifdef TT_CONFIG_OPTION_BDF
|
||||
|
@ -347,6 +348,380 @@
|
|||
}
|
||||
|
||||
|
||||
#define WRITE_BYTE( p, v ) \
|
||||
do \
|
||||
{ \
|
||||
*(p)++ = (v) >> 0; \
|
||||
\
|
||||
} while ( 0 )
|
||||
|
||||
#define WRITE_USHORT( p, v ) \
|
||||
do \
|
||||
{ \
|
||||
*(p)++ = (v) >> 8; \
|
||||
*(p)++ = (v) >> 0; \
|
||||
\
|
||||
} while ( 0 )
|
||||
|
||||
#define WRITE_ULONG( p, v ) \
|
||||
do \
|
||||
{ \
|
||||
*(p)++ = (v) >> 24; \
|
||||
*(p)++ = (v) >> 16; \
|
||||
*(p)++ = (v) >> 8; \
|
||||
*(p)++ = (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 = 0;
|
||||
stream->close = 0;
|
||||
}
|
||||
|
||||
|
||||
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 ) )
|
||||
return FT_THROW( Invalid_Table );
|
||||
|
||||
if ( FT_ALLOC( sfnt, woff.totalSfntSize ) ||
|
||||
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" ));
|
||||
|
||||
for ( nn = 0; nn < woff.num_tables; nn++ )
|
||||
{
|
||||
WOFF_Table table = tables + nn;
|
||||
|
||||
|
||||
if ( FT_STREAM_SEEK( 44 + nn * 20 ) ||
|
||||
FT_FRAME_ENTER( 20L ) )
|
||||
goto Exit;
|
||||
|
||||
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_FRAME_EXIT();
|
||||
|
||||
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 )
|
||||
{
|
||||
error = FT_THROW( Invalid_Table );
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
old_tag = table->Tag;
|
||||
indices[nn] = table;
|
||||
}
|
||||
|
||||
/* 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->Offset + table->CompLength > woff.length ||
|
||||
sfnt_offset + table->OrigLength > woff.totalSfntSize ||
|
||||
table->CompLength > table->OrigLength )
|
||||
{
|
||||
error = FT_THROW( Invalid_Table );
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
table->OrigOffset = sfnt_offset;
|
||||
|
||||
/* The offsets must be multiples of 4. */
|
||||
woff_offset += ( table->CompLength + 3 ) & ~3;
|
||||
sfnt_offset += ( table->OrigLength + 3 ) & ~3;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 )
|
||||
{
|
||||
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 ) & ~3;
|
||||
|
||||
if ( woff.privOffset != woff_offset ||
|
||||
woff.privOffset + woff.privLength > woff.length )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
error = FT_THROW( Invalid_Table );
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
/* 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
|
||||
{
|
||||
/* 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 )
|
||||
{
|
||||
error = FT_THROW( Invalid_Table );
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
stream = 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_BYTE
|
||||
#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
|
||||
|
@ -373,11 +748,28 @@
|
|||
face->ttc_header.version = 0;
|
||||
face->ttc_header.count = 0;
|
||||
|
||||
retry:
|
||||
offset = FT_STREAM_POS();
|
||||
|
||||
if ( FT_READ_ULONG( tag ) )
|
||||
return error;
|
||||
|
||||
if ( tag == TTAG_wOFF )
|
||||
{
|
||||
FT_TRACE2(( "sfnt_open_font: file is a WOFF; synthesizing SFNT\n" ));
|
||||
|
||||
if ( FT_STREAM_SEEK( offset ) )
|
||||
return error;
|
||||
|
||||
error = woff_open_font( stream, face );
|
||||
if ( error )
|
||||
return error;
|
||||
|
||||
/* Swap out stream and retry! */
|
||||
stream = face->root.stream;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if ( tag != 0x00010000UL &&
|
||||
tag != TTAG_ttcf &&
|
||||
tag != TTAG_OTTO &&
|
||||
|
@ -480,6 +872,9 @@
|
|||
if ( error )
|
||||
return error;
|
||||
|
||||
/* Stream may have changed in sfnt_open_font. */
|
||||
stream = face->root.stream;
|
||||
|
||||
FT_TRACE2(( "sfnt_init_face: %08p, %ld\n", face, face_index ));
|
||||
|
||||
if ( face_index < 0 )
|
||||
|
|
|
@ -532,6 +532,10 @@
|
|||
|
||||
/* check that we have a valid TrueType file */
|
||||
error = sfnt->init_face( stream, face, face_index, num_params, params );
|
||||
|
||||
/* Stream may have changed. */
|
||||
stream = face->root.stream;
|
||||
|
||||
if ( error )
|
||||
goto Exit;
|
||||
|
||||
|
|
Loading…
Reference in New Issue