/***************************************************************************/ | |
/* */ | |
/* sfobjs.c */ | |
/* */ | |
/* SFNT object management (base). */ | |
/* */ | |
/* Copyright 1996-2015 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 "sfobjs.h" | |
#include "ttload.h" | |
#include "ttcmap.h" | |
#include "ttkern.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 | |
#include "sferrors.h" | |
#ifdef TT_CONFIG_OPTION_BDF | |
#include "ttbdf.h" | |
#endif | |
/*************************************************************************/ | |
/* */ | |
/* 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 trace_sfobjs | |
/* convert a UTF-16 name entry to ASCII */ | |
static FT_String* | |
tt_name_entry_ascii_from_utf16( TT_NameEntry entry, | |
FT_Memory memory ) | |
{ | |
FT_String* string = NULL; | |
FT_UInt len, code, n; | |
FT_Byte* read = (FT_Byte*)entry->string; | |
FT_Error error; | |
len = (FT_UInt)entry->stringLength / 2; | |
if ( FT_NEW_ARRAY( string, len + 1 ) ) | |
return NULL; | |
for ( n = 0; n < len; n++ ) | |
{ | |
code = FT_NEXT_USHORT( read ); | |
if ( code == 0 ) | |
break; | |
if ( code < 32 || code > 127 ) | |
code = '?'; | |
string[n] = (char)code; | |
} | |
string[n] = 0; | |
return string; | |
} | |
/* convert an Apple Roman or symbol name entry to ASCII */ | |
static FT_String* | |
tt_name_entry_ascii_from_other( TT_NameEntry entry, | |
FT_Memory memory ) | |
{ | |
FT_String* string = NULL; | |
FT_UInt len, code, n; | |
FT_Byte* read = (FT_Byte*)entry->string; | |
FT_Error error; | |
len = (FT_UInt)entry->stringLength; | |
if ( FT_NEW_ARRAY( string, len + 1 ) ) | |
return NULL; | |
for ( n = 0; n < len; n++ ) | |
{ | |
code = *read++; | |
if ( code == 0 ) | |
break; | |
if ( code < 32 || code > 127 ) | |
code = '?'; | |
string[n] = (char)code; | |
} | |
string[n] = 0; | |
return string; | |
} | |
typedef FT_String* (*TT_NameEntry_ConvertFunc)( TT_NameEntry entry, | |
FT_Memory memory ); | |
/* documentation is in sfnt.h */ | |
FT_LOCAL_DEF( FT_Error ) | |
tt_face_get_name( TT_Face face, | |
FT_UShort nameid, | |
FT_String** name ) | |
{ | |
FT_Memory memory = face->root.memory; | |
FT_Error error = FT_Err_Ok; | |
FT_String* result = NULL; | |
FT_UShort n; | |
TT_NameEntryRec* rec; | |
FT_Int found_apple = -1; | |
FT_Int found_apple_roman = -1; | |
FT_Int found_apple_english = -1; | |
FT_Int found_win = -1; | |
FT_Int found_unicode = -1; | |
FT_Bool is_english = 0; | |
TT_NameEntry_ConvertFunc convert; | |
FT_ASSERT( name ); | |
rec = face->name_table.names; | |
for ( n = 0; n < face->num_names; n++, rec++ ) | |
{ | |
/* According to the OpenType 1.3 specification, only Microsoft or */ | |
/* Apple platform IDs might be used in the `name' table. The */ | |
/* `Unicode' platform is reserved for the `cmap' table, and the */ | |
/* `ISO' one is deprecated. */ | |
/* */ | |
/* However, the Apple TrueType specification doesn't say the same */ | |
/* thing and goes to suggest that all Unicode `name' table entries */ | |
/* should be coded in UTF-16 (in big-endian format I suppose). */ | |
/* */ | |
if ( rec->nameID == nameid && rec->stringLength > 0 ) | |
{ | |
switch ( rec->platformID ) | |
{ | |
case TT_PLATFORM_APPLE_UNICODE: | |
case TT_PLATFORM_ISO: | |
/* there is `languageID' to check there. We should use this */ | |
/* field only as a last solution when nothing else is */ | |
/* available. */ | |
/* */ | |
found_unicode = n; | |
break; | |
case TT_PLATFORM_MACINTOSH: | |
/* This is a bit special because some fonts will use either */ | |
/* an English language id, or a Roman encoding id, to indicate */ | |
/* the English version of its font name. */ | |
/* */ | |
if ( rec->languageID == TT_MAC_LANGID_ENGLISH ) | |
found_apple_english = n; | |
else if ( rec->encodingID == TT_MAC_ID_ROMAN ) | |
found_apple_roman = n; | |
break; | |
case TT_PLATFORM_MICROSOFT: | |
/* we only take a non-English name when there is nothing */ | |
/* else available in the font */ | |
/* */ | |
if ( found_win == -1 || ( rec->languageID & 0x3FF ) == 0x009 ) | |
{ | |
switch ( rec->encodingID ) | |
{ | |
case TT_MS_ID_SYMBOL_CS: | |
case TT_MS_ID_UNICODE_CS: | |
case TT_MS_ID_UCS_4: | |
is_english = FT_BOOL( ( rec->languageID & 0x3FF ) == 0x009 ); | |
found_win = n; | |
break; | |
default: | |
; | |
} | |
} | |
break; | |
default: | |
; | |
} | |
} | |
} | |
found_apple = found_apple_roman; | |
if ( found_apple_english >= 0 ) | |
found_apple = found_apple_english; | |
/* some fonts contain invalid Unicode or Macintosh formatted entries; */ | |
/* we will thus favor names encoded in Windows formats if available */ | |
/* (provided it is an English name) */ | |
/* */ | |
convert = NULL; | |
if ( found_win >= 0 && !( found_apple >= 0 && !is_english ) ) | |
{ | |
rec = face->name_table.names + found_win; | |
switch ( rec->encodingID ) | |
{ | |
/* all Unicode strings are encoded using UTF-16BE */ | |
case TT_MS_ID_UNICODE_CS: | |
case TT_MS_ID_SYMBOL_CS: | |
convert = tt_name_entry_ascii_from_utf16; | |
break; | |
case TT_MS_ID_UCS_4: | |
/* Apparently, if this value is found in a name table entry, it is */ | |
/* documented as `full Unicode repertoire'. Experience with the */ | |
/* MsGothic font shipped with Windows Vista shows that this really */ | |
/* means UTF-16 encoded names (UCS-4 values are only used within */ | |
/* charmaps). */ | |
convert = tt_name_entry_ascii_from_utf16; | |
break; | |
default: | |
; | |
} | |
} | |
else if ( found_apple >= 0 ) | |
{ | |
rec = face->name_table.names + found_apple; | |
convert = tt_name_entry_ascii_from_other; | |
} | |
else if ( found_unicode >= 0 ) | |
{ | |
rec = face->name_table.names + found_unicode; | |
convert = tt_name_entry_ascii_from_utf16; | |
} | |
if ( rec && convert ) | |
{ | |
if ( rec->string == NULL ) | |
{ | |
FT_Stream stream = face->name_table.stream; | |
if ( FT_QNEW_ARRAY ( rec->string, rec->stringLength ) || | |
FT_STREAM_SEEK( rec->stringOffset ) || | |
FT_STREAM_READ( rec->string, rec->stringLength ) ) | |
{ | |
FT_FREE( rec->string ); | |
rec->stringLength = 0; | |
result = NULL; | |
goto Exit; | |
} | |
} | |
result = convert( rec, memory ); | |
} | |
Exit: | |
*name = result; | |
return error; | |
} | |
static FT_Encoding | |
sfnt_find_encoding( int platform_id, | |
int encoding_id ) | |
{ | |
typedef struct TEncoding_ | |
{ | |
int platform_id; | |
int encoding_id; | |
FT_Encoding encoding; | |
} TEncoding; | |
static | |
const TEncoding tt_encodings[] = | |
{ | |
{ TT_PLATFORM_ISO, -1, FT_ENCODING_UNICODE }, | |
{ TT_PLATFORM_APPLE_UNICODE, -1, FT_ENCODING_UNICODE }, | |
{ TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, FT_ENCODING_APPLE_ROMAN }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, FT_ENCODING_MS_SYMBOL }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_UCS_4, FT_ENCODING_UNICODE }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, FT_ENCODING_UNICODE }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_SJIS, FT_ENCODING_SJIS }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_GB2312, FT_ENCODING_GB2312 }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_BIG_5, FT_ENCODING_BIG5 }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_WANSUNG, FT_ENCODING_WANSUNG }, | |
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_JOHAB, FT_ENCODING_JOHAB } | |
}; | |
const TEncoding *cur, *limit; | |
cur = tt_encodings; | |
limit = cur + sizeof ( tt_encodings ) / sizeof ( tt_encodings[0] ); | |
for ( ; cur < limit; cur++ ) | |
{ | |
if ( cur->platform_id == platform_id ) | |
{ | |
if ( cur->encoding_id == encoding_id || | |
cur->encoding_id == -1 ) | |
return cur->encoding; | |
} | |
} | |
return FT_ENCODING_NONE; | |
} | |
#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 | |
sfnt_open_font( FT_Stream stream, | |
TT_Face face ) | |
{ | |
FT_Memory memory = stream->memory; | |
FT_Error error; | |
FT_ULong tag, offset; | |
static const FT_Frame_Field ttc_header_fields[] = | |
{ | |
#undef FT_STRUCTURE | |
#define FT_STRUCTURE TTC_HeaderRec | |
FT_FRAME_START( 8 ), | |
FT_FRAME_LONG( version ), | |
FT_FRAME_LONG( count ), /* this is ULong in the specs */ | |
FT_FRAME_END | |
}; | |
face->ttc_header.tag = 0; | |
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 && | |
tag != TTAG_true && | |
tag != TTAG_typ1 && | |
tag != 0x00020000UL ) | |
{ | |
FT_TRACE2(( " not a font using the SFNT container format\n" )); | |
return FT_THROW( Unknown_File_Format ); | |
} | |
face->ttc_header.tag = TTAG_ttcf; | |
if ( tag == TTAG_ttcf ) | |
{ | |
FT_Int n; | |
FT_TRACE3(( "sfnt_open_font: file is a collection\n" )); | |
if ( FT_STREAM_READ_FIELDS( ttc_header_fields, &face->ttc_header ) ) | |
return error; | |
if ( face->ttc_header.count == 0 ) | |
return FT_THROW( Invalid_Table ); | |
/* a rough size estimate: let's conservatively assume that there */ | |
/* is just a single table info in each subfont header (12 + 16*1 = */ | |
/* 28 bytes), thus we have (at least) `12 + 4*count' bytes for the */ | |
/* size of the TTC header plus `28*count' bytes for all subfont */ | |
/* headers */ | |
if ( (FT_ULong)face->ttc_header.count > stream->size / ( 28 + 4 ) ) | |
return FT_THROW( Array_Too_Large ); | |
/* now read the offsets of each font in the file */ | |
if ( FT_NEW_ARRAY( face->ttc_header.offsets, face->ttc_header.count ) ) | |
return error; | |
if ( FT_FRAME_ENTER( face->ttc_header.count * 4L ) ) | |
return error; | |
for ( n = 0; n < face->ttc_header.count; n++ ) | |
face->ttc_header.offsets[n] = FT_GET_ULONG(); | |
FT_FRAME_EXIT(); | |
} | |
else | |
{ | |
FT_TRACE3(( "sfnt_open_font: synthesize TTC\n" )); | |
face->ttc_header.version = 1 << 16; | |
face->ttc_header.count = 1; | |
if ( FT_NEW( face->ttc_header.offsets ) ) | |
return error; | |
face->ttc_header.offsets[0] = offset; | |
} | |
return error; | |
} | |
FT_LOCAL_DEF( FT_Error ) | |
sfnt_init_face( FT_Stream stream, | |
TT_Face face, | |
FT_Int face_instance_index, | |
FT_Int num_params, | |
FT_Parameter* params ) | |
{ | |
FT_Error error; | |
FT_Library library = face->root.driver->root.library; | |
SFNT_Service sfnt; | |
FT_Int face_index; | |
/* for now, parameters are unused */ | |
FT_UNUSED( num_params ); | |
FT_UNUSED( params ); | |
sfnt = (SFNT_Service)face->sfnt; | |
if ( !sfnt ) | |
{ | |
sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" ); | |
if ( !sfnt ) | |
{ | |
FT_ERROR(( "sfnt_init_face: cannot access `sfnt' module\n" )); | |
return FT_THROW( Missing_Module ); | |
} | |
face->sfnt = sfnt; | |
face->goto_table = sfnt->goto_table; | |
} | |
FT_FACE_FIND_GLOBAL_SERVICE( face, face->psnames, POSTSCRIPT_CMAPS ); | |
FT_TRACE2(( "SFNT driver\n" )); | |
error = sfnt_open_font( stream, face ); | |
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_instance_index )); | |
face_index = FT_ABS( face_instance_index ) & 0xFFFF; | |
if ( face_index >= face->ttc_header.count ) | |
{ | |
if ( face_instance_index >= 0 ) | |
return FT_THROW( Invalid_Argument ); | |
else | |
face_index = 0; | |
} | |
if ( FT_STREAM_SEEK( face->ttc_header.offsets[face_index] ) ) | |
return error; | |
/* check whether we have a valid TrueType file */ | |
error = sfnt->load_font_dir( face, stream ); | |
if ( error ) | |
return error; | |
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT | |
{ | |
FT_ULong fvar_len; | |
FT_ULong version; | |
FT_ULong offset; | |
FT_UShort num_axes; | |
FT_UShort axis_size; | |
FT_UShort num_instances; | |
FT_UShort instance_size; | |
FT_Int instance_index; | |
instance_index = FT_ABS( face_instance_index ) >> 16; | |
/* test whether current face is a GX font with named instances */ | |
if ( face->goto_table( face, TTAG_fvar, stream, &fvar_len ) || | |
fvar_len < 20 || | |
FT_READ_ULONG( version ) || | |
FT_READ_USHORT( offset ) || | |
FT_STREAM_SKIP( 2 ) || | |
FT_READ_USHORT( num_axes ) || | |
FT_READ_USHORT( axis_size ) || | |
FT_READ_USHORT( num_instances ) || | |
FT_READ_USHORT( instance_size ) ) | |
{ | |
version = 0; | |
offset = 0; | |
num_axes = 0; | |
axis_size = 0; | |
num_instances = 0; | |
instance_size = 0; | |
} | |
/* check that the data is bound by the table length; */ | |
/* based on similar code in function `TT_Get_MM_Var' */ | |
if ( version != 0x00010000UL || | |
axis_size != 20 || | |
num_axes > 0x3FFE || | |
instance_size != 4 + 4 * num_axes || | |
num_instances > 0x7EFF || | |
offset + | |
axis_size * num_axes + | |
instance_size * num_instances > fvar_len ) | |
num_instances = 0; | |
/* we support at most 2^15 - 1 instances */ | |
if ( num_instances >= ( 1U << 15 ) - 1 ) | |
{ | |
if ( face_instance_index >= 0 ) | |
return FT_THROW( Invalid_Argument ); | |
else | |
num_instances = 0; | |
} | |
/* instance indices in `face_instance_index' start with index 1, */ | |
/* thus `>' and not `>=' */ | |
if ( instance_index > num_instances ) | |
{ | |
if ( face_instance_index >= 0 ) | |
return FT_THROW( Invalid_Argument ); | |
else | |
num_instances = 0; | |
} | |
face->root.style_flags = (FT_Long)num_instances << 16; | |
} | |
#endif | |
face->root.num_faces = face->ttc_header.count; | |
face->root.face_index = face_index; | |
return error; | |
} | |
#define LOAD_( x ) \ | |
do \ | |
{ \ | |
FT_TRACE2(( "`" #x "' " )); \ | |
FT_TRACE3(( "-->\n" )); \ | |
\ | |
error = sfnt->load_ ## x( face, stream ); \ | |
\ | |
FT_TRACE2(( "%s\n", ( !error ) \ | |
? "loaded" \ | |
: FT_ERR_EQ( error, Table_Missing ) \ | |
? "missing" \ | |
: "failed to load" )); \ | |
FT_TRACE3(( "\n" )); \ | |
} while ( 0 ) | |
#define LOADM_( x, vertical ) \ | |
do \ | |
{ \ | |
FT_TRACE2(( "`%s" #x "' ", \ | |
vertical ? "vertical " : "" )); \ | |
FT_TRACE3(( "-->\n" )); \ | |
\ | |
error = sfnt->load_ ## x( face, stream, vertical ); \ | |
\ | |
FT_TRACE2(( "%s\n", ( !error ) \ | |
? "loaded" \ | |
: FT_ERR_EQ( error, Table_Missing ) \ | |
? "missing" \ | |
: "failed to load" )); \ | |
FT_TRACE3(( "\n" )); \ | |
} while ( 0 ) | |
#define GET_NAME( id, field ) \ | |
do \ | |
{ \ | |
error = tt_face_get_name( face, TT_NAME_ID_ ## id, field ); \ | |
if ( error ) \ | |
goto Exit; \ | |
} while ( 0 ) | |
FT_LOCAL_DEF( FT_Error ) | |
sfnt_load_face( FT_Stream stream, | |
TT_Face face, | |
FT_Int face_instance_index, | |
FT_Int num_params, | |
FT_Parameter* params ) | |
{ | |
FT_Error error; | |
#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES | |
FT_Error psnames_error; | |
#endif | |
FT_Bool has_outline; | |
FT_Bool is_apple_sbit; | |
FT_Bool is_apple_sbix; | |
FT_Bool ignore_preferred_family = FALSE; | |
FT_Bool ignore_preferred_subfamily = FALSE; | |
SFNT_Service sfnt = (SFNT_Service)face->sfnt; | |
FT_UNUSED( face_instance_index ); | |
/* Check parameters */ | |
{ | |
FT_Int i; | |
for ( i = 0; i < num_params; i++ ) | |
{ | |
if ( params[i].tag == FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY ) | |
ignore_preferred_family = TRUE; | |
else if ( params[i].tag == FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY ) | |
ignore_preferred_subfamily = TRUE; | |
} | |
} | |
/* Load tables */ | |
/* We now support two SFNT-based bitmapped font formats. They */ | |
/* are recognized easily as they do not include a `glyf' */ | |
/* table. */ | |
/* */ | |
/* The first format comes from Apple, and uses a table named */ | |
/* `bhed' instead of `head' to store the font header (using */ | |
/* the same format). It also doesn't include horizontal and */ | |
/* vertical metrics tables (i.e. `hhea' and `vhea' tables are */ | |
/* missing). */ | |
/* */ | |
/* The other format comes from Microsoft, and is used with */ | |
/* WinCE/PocketPC. It looks like a standard TTF, except that */ | |
/* it doesn't contain outlines. */ | |
/* */ | |
FT_TRACE2(( "sfnt_load_face: %08p\n\n", face )); | |
/* do we have outlines in there? */ | |
#ifdef FT_CONFIG_OPTION_INCREMENTAL | |
has_outline = FT_BOOL( face->root.internal->incremental_interface != 0 || | |
tt_face_lookup_table( face, TTAG_glyf ) != 0 || | |
tt_face_lookup_table( face, TTAG_CFF ) != 0 ); | |
#else | |
has_outline = FT_BOOL( tt_face_lookup_table( face, TTAG_glyf ) != 0 || | |
tt_face_lookup_table( face, TTAG_CFF ) != 0 ); | |
#endif | |
is_apple_sbit = 0; | |
is_apple_sbix = !face->goto_table( face, TTAG_sbix, stream, 0 ); | |
/* Apple 'sbix' color bitmaps are rendered scaled and then the 'glyf' | |
* outline rendered on top. We don't support that yet, so just ignore | |
* the 'glyf' outline and advertise it as a bitmap-only font. */ | |
if ( is_apple_sbix ) | |
has_outline = FALSE; | |
/* if this font doesn't contain outlines, we try to load */ | |
/* a `bhed' table */ | |
if ( !has_outline && sfnt->load_bhed ) | |
{ | |
LOAD_( bhed ); | |
is_apple_sbit = FT_BOOL( !error ); | |
} | |
/* load the font header (`head' table) if this isn't an Apple */ | |
/* sbit font file */ | |
if ( !is_apple_sbit || is_apple_sbix ) | |
{ | |
LOAD_( head ); | |
if ( error ) | |
goto Exit; | |
} | |
if ( face->header.Units_Per_EM == 0 ) | |
{ | |
error = FT_THROW( Invalid_Table ); | |
goto Exit; | |
} | |
/* the following tables are often not present in embedded TrueType */ | |
/* fonts within PDF documents, so don't check for them. */ | |
LOAD_( maxp ); | |
LOAD_( cmap ); | |
/* the following tables are optional in PCL fonts -- */ | |
/* don't check for errors */ | |
LOAD_( name ); | |
LOAD_( post ); | |
#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES | |
psnames_error = error; | |
#endif | |
/* do not load the metrics headers and tables if this is an Apple */ | |
/* sbit font file */ | |
if ( !is_apple_sbit ) | |
{ | |
/* load the `hhea' and `hmtx' tables */ | |
LOADM_( hhea, 0 ); | |
if ( !error ) | |
{ | |
LOADM_( hmtx, 0 ); | |
if ( FT_ERR_EQ( error, Table_Missing ) ) | |
{ | |
error = FT_THROW( Hmtx_Table_Missing ); | |
#ifdef FT_CONFIG_OPTION_INCREMENTAL | |
/* If this is an incrementally loaded font and there are */ | |
/* overriding metrics, tolerate a missing `hmtx' table. */ | |
if ( face->root.internal->incremental_interface && | |
face->root.internal->incremental_interface->funcs-> | |
get_glyph_metrics ) | |
{ | |
face->horizontal.number_Of_HMetrics = 0; | |
error = FT_Err_Ok; | |
} | |
#endif | |
} | |
} | |
else if ( FT_ERR_EQ( error, Table_Missing ) ) | |
{ | |
/* No `hhea' table necessary for SFNT Mac fonts. */ | |
if ( face->format_tag == TTAG_true ) | |
{ | |
FT_TRACE2(( "This is an SFNT Mac font.\n" )); | |
has_outline = 0; | |
error = FT_Err_Ok; | |
} | |
else | |
{ | |
error = FT_THROW( Horiz_Header_Missing ); | |
#ifdef FT_CONFIG_OPTION_INCREMENTAL | |
/* If this is an incrementally loaded font and there are */ | |
/* overriding metrics, tolerate a missing `hhea' table. */ | |
if ( face->root.internal->incremental_interface && | |
face->root.internal->incremental_interface->funcs-> | |
get_glyph_metrics ) | |
{ | |
face->horizontal.number_Of_HMetrics = 0; | |
error = FT_Err_Ok; | |
} | |
#endif | |
} | |
} | |
if ( error ) | |
goto Exit; | |
/* try to load the `vhea' and `vmtx' tables */ | |
LOADM_( hhea, 1 ); | |
if ( !error ) | |
{ | |
LOADM_( hmtx, 1 ); | |
if ( !error ) | |
face->vertical_info = 1; | |
} | |
if ( error && FT_ERR_NEQ( error, Table_Missing ) ) | |
goto Exit; | |
LOAD_( os2 ); | |
if ( error ) | |
{ | |
/* we treat the table as missing if there are any errors */ | |
face->os2.version = 0xFFFFU; | |
} | |
} | |
/* the optional tables */ | |
/* embedded bitmap support */ | |
if ( sfnt->load_eblc ) | |
{ | |
LOAD_( eblc ); | |
if ( error ) | |
{ | |
/* a font which contains neither bitmaps nor outlines is */ | |
/* still valid (although rather useless in most cases); */ | |
/* however, you can find such stripped fonts in PDFs */ | |
if ( FT_ERR_EQ( error, Table_Missing ) ) | |
error = FT_Err_Ok; | |
else | |
goto Exit; | |
} | |
} | |
LOAD_( pclt ); | |
if ( error ) | |
{ | |
if ( FT_ERR_NEQ( error, Table_Missing ) ) | |
goto Exit; | |
face->pclt.Version = 0; | |
} | |
/* consider the kerning and gasp tables as optional */ | |
LOAD_( gasp ); | |
LOAD_( kern ); | |
face->root.num_glyphs = face->max_profile.numGlyphs; | |
/* Bit 8 of the `fsSelection' field in the `OS/2' table denotes */ | |
/* a WWS-only font face. `WWS' stands for `weight', width', and */ | |
/* `slope', a term used by Microsoft's Windows Presentation */ | |
/* Foundation (WPF). This flag has been introduced in version */ | |
/* 1.5 of the OpenType specification (May 2008). */ | |
face->root.family_name = NULL; | |
face->root.style_name = NULL; | |
if ( face->os2.version != 0xFFFFU && face->os2.fsSelection & 256 ) | |
{ | |
if ( !ignore_preferred_family ) | |
GET_NAME( PREFERRED_FAMILY, &face->root.family_name ); | |
if ( !face->root.family_name ) | |
GET_NAME( FONT_FAMILY, &face->root.family_name ); | |
if ( !ignore_preferred_subfamily ) | |
GET_NAME( PREFERRED_SUBFAMILY, &face->root.style_name ); | |
if ( !face->root.style_name ) | |
GET_NAME( FONT_SUBFAMILY, &face->root.style_name ); | |
} | |
else | |
{ | |
GET_NAME( WWS_FAMILY, &face->root.family_name ); | |
if ( !face->root.family_name && !ignore_preferred_family ) | |
GET_NAME( PREFERRED_FAMILY, &face->root.family_name ); | |
if ( !face->root.family_name ) | |
GET_NAME( FONT_FAMILY, &face->root.family_name ); | |
GET_NAME( WWS_SUBFAMILY, &face->root.style_name ); | |
if ( !face->root.style_name && !ignore_preferred_subfamily ) | |
GET_NAME( PREFERRED_SUBFAMILY, &face->root.style_name ); | |
if ( !face->root.style_name ) | |
GET_NAME( FONT_SUBFAMILY, &face->root.style_name ); | |
} | |
/* now set up root fields */ | |
{ | |
FT_Face root = &face->root; | |
FT_Long flags = root->face_flags; | |
/*********************************************************************/ | |
/* */ | |
/* Compute face flags. */ | |
/* */ | |
if ( face->sbit_table_type == TT_SBIT_TABLE_TYPE_CBLC || | |
face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX ) | |
flags |= FT_FACE_FLAG_COLOR; /* color glyphs */ | |
if ( has_outline == TRUE ) | |
flags |= FT_FACE_FLAG_SCALABLE; /* scalable outlines */ | |
/* The sfnt driver only supports bitmap fonts natively, thus we */ | |
/* don't set FT_FACE_FLAG_HINTER. */ | |
flags |= FT_FACE_FLAG_SFNT | /* SFNT file format */ | |
FT_FACE_FLAG_HORIZONTAL; /* horizontal data */ | |
#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES | |
if ( !psnames_error && | |
face->postscript.FormatType != 0x00030000L ) | |
flags |= FT_FACE_FLAG_GLYPH_NAMES; | |
#endif | |
/* fixed width font? */ | |
if ( face->postscript.isFixedPitch ) | |
flags |= FT_FACE_FLAG_FIXED_WIDTH; | |
/* vertical information? */ | |
if ( face->vertical_info ) | |
flags |= FT_FACE_FLAG_VERTICAL; | |
/* kerning available ? */ | |
if ( TT_FACE_HAS_KERNING( face ) ) | |
flags |= FT_FACE_FLAG_KERNING; | |
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT | |
/* Don't bother to load the tables unless somebody asks for them. */ | |
/* No need to do work which will (probably) not be used. */ | |
if ( tt_face_lookup_table( face, TTAG_glyf ) != 0 && | |
tt_face_lookup_table( face, TTAG_fvar ) != 0 && | |
tt_face_lookup_table( face, TTAG_gvar ) != 0 ) | |
flags |= FT_FACE_FLAG_MULTIPLE_MASTERS; | |
#endif | |
root->face_flags = flags; | |
/*********************************************************************/ | |
/* */ | |
/* Compute style flags. */ | |
/* */ | |
flags = 0; | |
if ( has_outline == TRUE && face->os2.version != 0xFFFFU ) | |
{ | |
/* We have an OS/2 table; use the `fsSelection' field. Bit 9 */ | |
/* indicates an oblique font face. This flag has been */ | |
/* introduced in version 1.5 of the OpenType specification. */ | |
if ( face->os2.fsSelection & 512 ) /* bit 9 */ | |
flags |= FT_STYLE_FLAG_ITALIC; | |
else if ( face->os2.fsSelection & 1 ) /* bit 0 */ | |
flags |= FT_STYLE_FLAG_ITALIC; | |
if ( face->os2.fsSelection & 32 ) /* bit 5 */ | |
flags |= FT_STYLE_FLAG_BOLD; | |
} | |
else | |
{ | |
/* this is an old Mac font, use the header field */ | |
if ( face->header.Mac_Style & 1 ) | |
flags |= FT_STYLE_FLAG_BOLD; | |
if ( face->header.Mac_Style & 2 ) | |
flags |= FT_STYLE_FLAG_ITALIC; | |
} | |
root->style_flags |= flags; | |
/*********************************************************************/ | |
/* */ | |
/* Polish the charmaps. */ | |
/* */ | |
/* Try to set the charmap encoding according to the platform & */ | |
/* encoding ID of each charmap. */ | |
/* */ | |
tt_face_build_cmaps( face ); /* ignore errors */ | |
/* set the encoding fields */ | |
{ | |
FT_Int m; | |
for ( m = 0; m < root->num_charmaps; m++ ) | |
{ | |
FT_CharMap charmap = root->charmaps[m]; | |
charmap->encoding = sfnt_find_encoding( charmap->platform_id, | |
charmap->encoding_id ); | |
#if 0 | |
if ( root->charmap == NULL && | |
charmap->encoding == FT_ENCODING_UNICODE ) | |
{ | |
/* set 'root->charmap' to the first Unicode encoding we find */ | |
root->charmap = charmap; | |
} | |
#endif | |
} | |
} | |
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS | |
/* | |
* Now allocate the root array of FT_Bitmap_Size records and | |
* populate them. Unfortunately, it isn't possible to indicate bit | |
* depths in the FT_Bitmap_Size record. This is a design error. | |
*/ | |
{ | |
FT_UInt i, count; | |
count = face->sbit_num_strikes; | |
if ( count > 0 ) | |
{ | |
FT_Memory memory = face->root.stream->memory; | |
FT_UShort em_size = face->header.Units_Per_EM; | |
FT_Short avgwidth = face->os2.xAvgCharWidth; | |
FT_Size_Metrics metrics; | |
if ( em_size == 0 || face->os2.version == 0xFFFFU ) | |
{ | |
avgwidth = 1; | |
em_size = 1; | |
} | |
if ( FT_NEW_ARRAY( root->available_sizes, count ) ) | |
goto Exit; | |
for ( i = 0; i < count; i++ ) | |
{ | |
FT_Bitmap_Size* bsize = root->available_sizes + i; | |
error = sfnt->load_strike_metrics( face, i, &metrics ); | |
if ( error ) | |
goto Exit; | |
bsize->height = (FT_Short)( metrics.height >> 6 ); | |
bsize->width = (FT_Short)( | |
( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size ); | |
bsize->x_ppem = metrics.x_ppem << 6; | |
bsize->y_ppem = metrics.y_ppem << 6; | |
/* assume 72dpi */ | |
bsize->size = metrics.y_ppem << 6; | |
} | |
root->face_flags |= FT_FACE_FLAG_FIXED_SIZES; | |
root->num_fixed_sizes = (FT_Int)count; | |
} | |
} | |
#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ | |
/* a font with no bitmaps and no outlines is scalable; */ | |
/* it has only empty glyphs then */ | |
if ( !FT_HAS_FIXED_SIZES( root ) && !FT_IS_SCALABLE( root ) ) | |
root->face_flags |= FT_FACE_FLAG_SCALABLE; | |
/*********************************************************************/ | |
/* */ | |
/* Set up metrics. */ | |
/* */ | |
if ( FT_IS_SCALABLE( root ) ) | |
{ | |
/* XXX What about if outline header is missing */ | |
/* (e.g. sfnt wrapped bitmap)? */ | |
root->bbox.xMin = face->header.xMin; | |
root->bbox.yMin = face->header.yMin; | |
root->bbox.xMax = face->header.xMax; | |
root->bbox.yMax = face->header.yMax; | |
root->units_per_EM = face->header.Units_Per_EM; | |
/* XXX: Computing the ascender/descender/height is very different */ | |
/* from what the specification tells you. Apparently, we */ | |
/* must be careful because */ | |
/* */ | |
/* - not all fonts have an OS/2 table; in this case, we take */ | |
/* the values in the horizontal header. However, these */ | |
/* values very often are not reliable. */ | |
/* */ | |
/* - otherwise, the correct typographic values are in the */ | |
/* sTypoAscender, sTypoDescender & sTypoLineGap fields. */ | |
/* */ | |
/* However, certain fonts have these fields set to 0. */ | |
/* Rather, they have usWinAscent & usWinDescent correctly */ | |
/* set (but with different values). */ | |
/* */ | |
/* As an example, Arial Narrow is implemented through four */ | |
/* files ARIALN.TTF, ARIALNI.TTF, ARIALNB.TTF & ARIALNBI.TTF */ | |
/* */ | |
/* Strangely, all fonts have the same values in their */ | |
/* sTypoXXX fields, except ARIALNB which sets them to 0. */ | |
/* */ | |
/* On the other hand, they all have different */ | |
/* usWinAscent/Descent values -- as a conclusion, the OS/2 */ | |
/* table cannot be used to compute the text height reliably! */ | |
/* */ | |
/* The ascender and descender are taken from the `hhea' table. */ | |
/* If zero, they are taken from the `OS/2' table. */ | |
root->ascender = face->horizontal.Ascender; | |
root->descender = face->horizontal.Descender; | |
root->height = root->ascender - root->descender + | |
face->horizontal.Line_Gap; | |
if ( !( root->ascender || root->descender ) ) | |
{ | |
if ( face->os2.version != 0xFFFFU ) | |
{ | |
if ( face->os2.sTypoAscender || face->os2.sTypoDescender ) | |
{ | |
root->ascender = face->os2.sTypoAscender; | |
root->descender = face->os2.sTypoDescender; | |
root->height = root->ascender - root->descender + | |
face->os2.sTypoLineGap; | |
} | |
else | |
{ | |
root->ascender = (FT_Short)face->os2.usWinAscent; | |
root->descender = -(FT_Short)face->os2.usWinDescent; | |
root->height = root->ascender - root->descender; | |
} | |
} | |
} | |
root->max_advance_width = | |
(FT_Short)face->horizontal.advance_Width_Max; | |
root->max_advance_height = | |
(FT_Short)( face->vertical_info ? face->vertical.advance_Height_Max | |
: root->height ); | |
/* See http://www.microsoft.com/OpenType/OTSpec/post.htm -- */ | |
/* Adjust underline position from top edge to centre of */ | |
/* stroke to convert TrueType meaning to FreeType meaning. */ | |
root->underline_position = face->postscript.underlinePosition - | |
face->postscript.underlineThickness / 2; | |
root->underline_thickness = face->postscript.underlineThickness; | |
} | |
} | |
Exit: | |
FT_TRACE2(( "sfnt_load_face: done\n" )); | |
return error; | |
} | |
#undef LOAD_ | |
#undef LOADM_ | |
#undef GET_NAME | |
FT_LOCAL_DEF( void ) | |
sfnt_done_face( TT_Face face ) | |
{ | |
FT_Memory memory; | |
SFNT_Service sfnt; | |
if ( !face ) | |
return; | |
memory = face->root.memory; | |
sfnt = (SFNT_Service)face->sfnt; | |
if ( sfnt ) | |
{ | |
/* destroy the postscript names table if it is loaded */ | |
if ( sfnt->free_psnames ) | |
sfnt->free_psnames( face ); | |
/* destroy the embedded bitmaps table if it is loaded */ | |
if ( sfnt->free_eblc ) | |
sfnt->free_eblc( face ); | |
} | |
#ifdef TT_CONFIG_OPTION_BDF | |
/* freeing the embedded BDF properties */ | |
tt_face_free_bdf_props( face ); | |
#endif | |
/* freeing the kerning table */ | |
tt_face_done_kern( face ); | |
/* freeing the collection table */ | |
FT_FREE( face->ttc_header.offsets ); | |
face->ttc_header.count = 0; | |
/* freeing table directory */ | |
FT_FREE( face->dir_tables ); | |
face->num_tables = 0; | |
{ | |
FT_Stream stream = FT_FACE_STREAM( face ); | |
/* simply release the 'cmap' table frame */ | |
FT_FRAME_RELEASE( face->cmap_table ); | |
face->cmap_size = 0; | |
} | |
/* freeing the horizontal metrics */ | |
{ | |
FT_Stream stream = FT_FACE_STREAM( face ); | |
FT_FRAME_RELEASE( face->horz_metrics ); | |
FT_FRAME_RELEASE( face->vert_metrics ); | |
face->horz_metrics_size = 0; | |
face->vert_metrics_size = 0; | |
} | |
/* freeing the vertical ones, if any */ | |
if ( face->vertical_info ) | |
{ | |
FT_FREE( face->vertical.long_metrics ); | |
FT_FREE( face->vertical.short_metrics ); | |
face->vertical_info = 0; | |
} | |
/* freeing the gasp table */ | |
FT_FREE( face->gasp.gaspRanges ); | |
face->gasp.numRanges = 0; | |
/* freeing the name table */ | |
if ( sfnt ) | |
sfnt->free_name( face ); | |
/* freeing family and style name */ | |
FT_FREE( face->root.family_name ); | |
FT_FREE( face->root.style_name ); | |
/* freeing sbit size table */ | |
FT_FREE( face->root.available_sizes ); | |
face->root.num_fixed_sizes = 0; | |
FT_FREE( face->postscript_name ); | |
face->sfnt = NULL; | |
} | |
/* END */ |