@ -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 )