diff options
Diffstat (limited to 'modules/freetype2/src/truetype/ttgxvar.c')
-rw-r--r-- | modules/freetype2/src/truetype/ttgxvar.c | 2276 |
1 files changed, 1677 insertions, 599 deletions
diff --git a/modules/freetype2/src/truetype/ttgxvar.c b/modules/freetype2/src/truetype/ttgxvar.c index cf4f7b17a..29ab2a4ef 100644 --- a/modules/freetype2/src/truetype/ttgxvar.c +++ b/modules/freetype2/src/truetype/ttgxvar.c @@ -4,7 +4,7 @@ /* */ /* TrueType GX Font Variation loader */ /* */ -/* Copyright 2004-2016 by */ +/* Copyright 2004-2018 by */ /* David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -22,10 +22,6 @@ /* */ /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html */ /* */ - /* The documentation for `fvar' is inconsistent. At one point it says */ - /* that `countSizePairs' should be 3, at another point 2. It should */ - /* be 2. */ - /* */ /* The documentation for `gvar' is not intelligible; `cvar' refers you */ /* to `gvar' and is thus also incomprehensible. */ /* */ @@ -49,7 +45,9 @@ #include FT_INTERNAL_STREAM_H #include FT_INTERNAL_SFNT_H #include FT_TRUETYPE_TAGS_H +#include FT_TRUETYPE_IDS_H #include FT_MULTIPLE_MASTERS_H +#include FT_LIST_H #include "ttpload.h" #include "ttgxvar.h" @@ -62,8 +60,11 @@ #define FT_Stream_FTell( stream ) \ (FT_ULong)( (stream)->cursor - (stream)->base ) -#define FT_Stream_SeekSet( stream, off ) \ - ( (stream)->cursor = (stream)->base + (off) ) +#define FT_Stream_SeekSet( stream, off ) \ + (stream)->cursor = \ + ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \ + ? (stream)->base + (off) \ + : (stream)->limit /*************************************************************************/ @@ -322,7 +323,7 @@ FT_TRACE2(( "AVAR " )); - blend->avar_checked = TRUE; + blend->avar_loaded = TRUE; error = face->goto_table( face, TTAG_avar, stream, &table_len ); if ( error ) { @@ -346,7 +347,7 @@ if ( axisCount != (FT_Long)blend->mmvar->num_axis ) { - FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `cvar'\n" + FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `fvar'\n" " table are different\n" )); goto Exit; } @@ -394,361 +395,309 @@ /* some macros we need */ - #define FT_FIXED_ONE ( (FT_Fixed)0x10000 ) +#define FT_FIXED_ONE ( (FT_Fixed)0x10000 ) - #define FT_fdot14ToFixed( x ) \ - ( ( (FT_Fixed)( (FT_Int16)(x) ) ) << 2 ) - #define FT_intToFixed( i ) \ - ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) ) - #define FT_fixedToInt( x ) \ - ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) +#define FT_fdot14ToFixed( x ) \ + ( (FT_Fixed)( (FT_ULong)(x) << 2 ) ) +#define FT_intToFixed( i ) \ + ( (FT_Fixed)( (FT_ULong)(i) << 16 ) ) +#define FT_fixedToInt( x ) \ + ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) - /*************************************************************************/ - /* */ - /* <Function> */ - /* ft_var_load_hvar */ - /* */ - /* <Description> */ - /* Parse the `HVAR' table and set `blend->hvar_loaded' to TRUE. */ - /* */ - /* On success, `blend->hvar_checked' is set to TRUE. */ - /* */ - /* Some memory may remain allocated on error; it is always freed in */ - /* `tt_done_blend', however. */ - /* */ - /* <InOut> */ - /* face :: The font face. */ - /* */ - /* <Return> */ - /* FreeType error code. 0 means success. */ - /* */ static FT_Error - ft_var_load_hvar( TT_Face face ) + ft_var_load_item_variation_store( TT_Face face, + FT_ULong offset, + GX_ItemVarStore itemStore ) { FT_Stream stream = FT_FACE_STREAM( face ); FT_Memory memory = stream->memory; - GX_Blend blend = face->blend; - FT_Error error; - FT_UShort majorVersion; - FT_ULong table_len; - FT_ULong table_offset; - FT_ULong store_offset; + FT_UShort format; + FT_ULong region_offset; + FT_UInt i, j, k; + FT_UInt shortDeltaCount; - FT_ULong* dataOffsetArray = NULL; + GX_Blend blend = face->blend; + GX_ItemVarData varData; + FT_ULong* dataOffsetArray = NULL; - blend->hvar_loaded = TRUE; - FT_TRACE2(( "HVAR " )); + if ( FT_STREAM_SEEK( offset ) || + FT_READ_USHORT( format ) ) + goto Exit; - error = face->goto_table( face, TTAG_HVAR, stream, &table_len ); - if ( error ) + if ( format != 1 ) { - FT_TRACE2(( "is missing\n" )); + FT_TRACE2(( "ft_var_load_item_variation_store: bad store format %d\n", + format )); + error = FT_THROW( Invalid_Table ); goto Exit; } - table_offset = FT_STREAM_POS(); - - /* skip minor version */ - if ( FT_READ_USHORT( majorVersion ) || - FT_STREAM_SKIP( 2 ) ) + /* read top level fields */ + if ( FT_READ_ULONG( region_offset ) || + FT_READ_USHORT( itemStore->dataCount ) ) goto Exit; - if ( majorVersion != 1 ) + + /* we need at least one entry in `itemStore->varData' */ + if ( !itemStore->dataCount ) { - FT_TRACE2(( "bad table version %d\n", majorVersion )); + FT_TRACE2(( "ft_var_load_item_variation_store: missing varData\n" )); error = FT_THROW( Invalid_Table ); goto Exit; } - /* skip map offset */ - if ( FT_READ_ULONG( store_offset ) || - FT_STREAM_SKIP( 4 ) ) + /* make temporary copy of item variation data offsets; */ + /* we will parse region list first, then come back */ + if ( FT_NEW_ARRAY( dataOffsetArray, itemStore->dataCount ) ) goto Exit; - /* parse item variation store */ + for ( i = 0; i < itemStore->dataCount; i++ ) { - FT_UShort format; - FT_ULong region_offset; - FT_UInt i, j, k; - FT_UInt shortDeltaCount; - - GX_HVStore itemStore; - GX_HVarTable hvarTable; - GX_HVarData hvarData; - - - if ( FT_STREAM_SEEK( table_offset + store_offset ) || - FT_READ_USHORT( format ) ) - goto Exit; - if ( format != 1 ) - { - FT_TRACE2(( "bad store format %d\n", format )); - error = FT_THROW( Invalid_Table ); + if ( FT_READ_ULONG( dataOffsetArray[i] ) ) goto Exit; - } + } - if ( FT_NEW( blend->hvar_table ) ) /* allocate table at top level */ - goto Exit; + /* parse array of region records (region list) */ + if ( FT_STREAM_SEEK( offset + region_offset ) ) + goto Exit; - hvarTable = blend->hvar_table; - itemStore = &hvarTable->itemStore; + if ( FT_READ_USHORT( itemStore->axisCount ) || + FT_READ_USHORT( itemStore->regionCount ) ) + goto Exit; - /* read top level fields */ - if ( FT_READ_ULONG( region_offset ) || - FT_READ_USHORT( itemStore->dataCount ) ) - goto Exit; + if ( itemStore->axisCount != (FT_Long)blend->mmvar->num_axis ) + { + FT_TRACE2(( "ft_var_load_item_variation_store:" + " number of axes in item variation store\n" + " " + " and `fvar' table are different\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } - /* make temporary copy of item variation data offsets; */ - /* we will parse region list first, then come back */ - if ( FT_NEW_ARRAY( dataOffsetArray, itemStore->dataCount ) ) - goto Exit; + if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) ) + goto Exit; - for ( i = 0; i < itemStore->dataCount; i++ ) - { - if ( FT_READ_ULONG( dataOffsetArray[i] ) ) - goto Exit; - } + for ( i = 0; i < itemStore->regionCount; i++ ) + { + GX_AxisCoords axisCoords; - /* parse array of region records (region list) */ - if ( FT_STREAM_SEEK( table_offset + store_offset + region_offset ) ) - goto Exit; - if ( FT_READ_USHORT( itemStore->axisCount ) || - FT_READ_USHORT( itemStore->regionCount ) ) + if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, + itemStore->axisCount ) ) goto Exit; - if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) ) - goto Exit; + axisCoords = itemStore->varRegionList[i].axisList; - for ( i = 0; i < itemStore->regionCount; i++ ) + for ( j = 0; j < itemStore->axisCount; j++ ) { - GX_AxisCoords axisCoords; + FT_Short start, peak, end; - if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, - itemStore->axisCount ) ) + if ( FT_READ_SHORT( start ) || + FT_READ_SHORT( peak ) || + FT_READ_SHORT( end ) ) goto Exit; - axisCoords = itemStore->varRegionList[i].axisList; + axisCoords[j].startCoord = FT_fdot14ToFixed( start ); + axisCoords[j].peakCoord = FT_fdot14ToFixed( peak ); + axisCoords[j].endCoord = FT_fdot14ToFixed( end ); + } + } - for ( j = 0; j < itemStore->axisCount; j++ ) - { - FT_Short start, peak, end; + /* end of region list parse */ + /* use dataOffsetArray now to parse varData items */ + if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) ) + goto Exit; - if ( FT_READ_SHORT( start ) || - FT_READ_SHORT( peak ) || - FT_READ_SHORT( end ) ) - goto Exit; + for ( i = 0; i < itemStore->dataCount; i++ ) + { + varData = &itemStore->varData[i]; - axisCoords[j].startCoord = FT_fdot14ToFixed( start ); - axisCoords[j].peakCoord = FT_fdot14ToFixed( peak ); - axisCoords[j].endCoord = FT_fdot14ToFixed( end ); - } - } + if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) ) + goto Exit; - /* end of region list parse */ + if ( FT_READ_USHORT( varData->itemCount ) || + FT_READ_USHORT( shortDeltaCount ) || + FT_READ_USHORT( varData->regionIdxCount ) ) + goto Exit; - /* use dataOffsetArray now to parse varData items */ - if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) ) + /* check some data consistency */ + if ( shortDeltaCount > varData->regionIdxCount ) + { + FT_TRACE2(( "bad short count %d or region count %d\n", + shortDeltaCount, + varData->regionIdxCount )); + error = FT_THROW( Invalid_Table ); goto Exit; + } - for ( i = 0; i < itemStore->dataCount; i++ ) + if ( varData->regionIdxCount > itemStore->regionCount ) { - hvarData = &itemStore->varData[i]; + FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n", + varData->regionIdxCount, + i )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } - if ( FT_STREAM_SEEK( table_offset + - store_offset + - dataOffsetArray[i] ) ) - goto Exit; + /* parse region indices */ + if ( FT_NEW_ARRAY( varData->regionIndices, + varData->regionIdxCount ) ) + goto Exit; - if ( FT_READ_USHORT( hvarData->itemCount ) || - FT_READ_USHORT( shortDeltaCount ) || - FT_READ_USHORT( hvarData->regionIdxCount ) ) + for ( j = 0; j < varData->regionIdxCount; j++ ) + { + if ( FT_READ_USHORT( varData->regionIndices[j] ) ) goto Exit; - /* check some data consistency */ - if ( shortDeltaCount > hvarData->regionIdxCount ) + if ( varData->regionIndices[j] >= itemStore->regionCount ) { - FT_TRACE2(( "bad short count %d or region count %d\n", - shortDeltaCount, - hvarData->regionIdxCount )); + FT_TRACE2(( "bad region index %d\n", + varData->regionIndices[j] )); error = FT_THROW( Invalid_Table ); goto Exit; } + } + + /* Parse delta set. */ + /* */ + /* On input, deltas are (shortDeltaCount + regionIdxCount) bytes */ + /* each; on output, deltas are expanded to `regionIdxCount' shorts */ + /* each. */ + if ( FT_NEW_ARRAY( varData->deltaSet, + varData->regionIdxCount * varData->itemCount ) ) + goto Exit; - if ( hvarData->regionIdxCount > itemStore->regionCount ) + /* the delta set is stored as a 2-dimensional array of shorts; */ + /* sign-extend signed bytes to signed shorts */ + for ( j = 0; j < varData->itemCount * varData->regionIdxCount; ) + { + for ( k = 0; k < shortDeltaCount; k++, j++ ) { - FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n", - hvarData->regionIdxCount, - i )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } + /* read the short deltas */ + FT_Short delta; - /* parse region indices */ - if ( FT_NEW_ARRAY( hvarData->regionIndices, - hvarData->regionIdxCount ) ) - goto Exit; - for ( j = 0; j < hvarData->regionIdxCount; j++ ) - { - if ( FT_READ_USHORT( hvarData->regionIndices[j] ) ) + if ( FT_READ_SHORT( delta ) ) goto Exit; - if ( hvarData->regionIndices[j] >= itemStore->regionCount ) - { - FT_TRACE2(( "bad region index %d\n", - hvarData->regionIndices[j] )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } + varData->deltaSet[j] = delta; } - /* Parse delta set. */ - /* */ - /* On input, deltas are ( shortDeltaCount + regionIdxCount ) bytes */ - /* each; on output, deltas are expanded to `regionIdxCount' shorts */ - /* each. */ - if ( FT_NEW_ARRAY( hvarData->deltaSet, - hvarData->regionIdxCount * hvarData->itemCount ) ) - goto Exit; - - /* the delta set is stored as a 2-dimensional array of shorts; */ - /* sign-extend signed bytes to signed shorts */ - for ( j = 0; j < hvarData->itemCount * hvarData->regionIdxCount; ) + for ( ; k < varData->regionIdxCount; k++, j++ ) { - for ( k = 0; k < shortDeltaCount; k++, j++ ) - { - /* read the short deltas */ - FT_Short delta; + /* read the (signed) byte deltas */ + FT_Char delta; - if ( FT_READ_SHORT( delta ) ) - goto Exit; - - hvarData->deltaSet[j] = delta; - } - - for ( ; k < hvarData->regionIdxCount; k++, j++ ) - { - /* read the (signed) byte deltas */ - FT_Char delta; - - - if ( FT_READ_CHAR( delta ) ) - goto Exit; + if ( FT_READ_CHAR( delta ) ) + goto Exit; - hvarData->deltaSet[j] = delta; - } + varData->deltaSet[j] = delta; } } } - /* end parse item variation store */ + Exit: + FT_FREE( dataOffsetArray ); - /* parse width map */ - { - GX_WidthMap widthMap; + return error; + } - FT_UShort format; - FT_UInt entrySize; - FT_UInt innerBitCount; - FT_UInt innerIndexMask; - FT_UInt i, j; + static FT_Error + ft_var_load_delta_set_index_mapping( TT_Face face, + FT_ULong offset, + GX_DeltaSetIdxMap map, + GX_ItemVarStore itemStore ) + { + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; - widthMap = &blend->hvar_table->widthMap; + FT_Error error; - if ( FT_READ_USHORT( format ) || - FT_READ_USHORT( widthMap->mapCount ) ) - goto Exit; + FT_UShort format; + FT_UInt entrySize; + FT_UInt innerBitCount; + FT_UInt innerIndexMask; + FT_UInt i, j; - if ( format & 0xFFC0 ) - { - FT_TRACE2(( "bad map format %d\n", format )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - /* bytes per entry: 1, 2, 3, or 4 */ - entrySize = ( ( format & 0x0030 ) >> 4 ) + 1; - innerBitCount = ( format & 0x000F ) + 1; - innerIndexMask = ( 1 << innerBitCount ) - 1; + if ( FT_STREAM_SEEK( offset ) || + FT_READ_USHORT( format ) || + FT_READ_USHORT( map->mapCount ) ) + goto Exit; - if ( FT_NEW_ARRAY( widthMap->innerIndex, widthMap->mapCount ) ) - goto Exit; + if ( format & 0xFFC0 ) + { + FT_TRACE2(( "bad map format %d\n", format )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } - if ( FT_NEW_ARRAY( widthMap->outerIndex, widthMap->mapCount ) ) - goto Exit; + /* bytes per entry: 1, 2, 3, or 4 */ + entrySize = ( ( format & 0x0030 ) >> 4 ) + 1; + innerBitCount = ( format & 0x000F ) + 1; + innerIndexMask = ( 1 << innerBitCount ) - 1; - for ( i = 0; i < widthMap->mapCount; i++ ) - { - FT_UInt mapData = 0; - FT_UInt outerIndex, innerIndex; + if ( FT_NEW_ARRAY( map->innerIndex, map->mapCount ) ) + goto Exit; + if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) ) + goto Exit; - /* read map data one unsigned byte at a time, big endian */ - for ( j = 0; j < entrySize; j++ ) - { - FT_Byte data; + for ( i = 0; i < map->mapCount; i++ ) + { + FT_UInt mapData = 0; + FT_UInt outerIndex, innerIndex; - if ( FT_READ_BYTE( data ) ) - goto Exit; - - mapData = ( mapData << 8 ) | data; - } + /* read map data one unsigned byte at a time, big endian */ + for ( j = 0; j < entrySize; j++ ) + { + FT_Byte data; - outerIndex = mapData >> innerBitCount; - if ( outerIndex >= blend->hvar_table->itemStore.dataCount ) - { - FT_TRACE2(( "outerIndex[%d] == %d out of range\n", - i, - outerIndex )); - error = FT_THROW( Invalid_Table ); + if ( FT_READ_BYTE( data ) ) goto Exit; - } - widthMap->outerIndex[i] = outerIndex; + mapData = ( mapData << 8 ) | data; + } - innerIndex = mapData & innerIndexMask; + outerIndex = mapData >> innerBitCount; - if ( innerIndex >= - blend->hvar_table->itemStore.varData[outerIndex].itemCount ) - { - FT_TRACE2(( "innerIndex[%d] == %d out of range\n", - i, - innerIndex )); - error = FT_THROW( Invalid_Table ); - goto Exit; - } - - widthMap->innerIndex[i] = innerIndex; + if ( outerIndex >= itemStore->dataCount ) + { + FT_TRACE2(( "outerIndex[%d] == %d out of range\n", + i, + outerIndex )); + error = FT_THROW( Invalid_Table ); + goto Exit; } - } - /* end parse width map */ + map->outerIndex[i] = outerIndex; - FT_TRACE2(( "loaded\n" )); - error = FT_Err_Ok; + innerIndex = mapData & innerIndexMask; - Exit: - FT_FREE( dataOffsetArray ); - - if ( !error ) - { - blend->hvar_checked = TRUE; + if ( innerIndex >= itemStore->varData[outerIndex].itemCount ) + { + FT_TRACE2(( "innerIndex[%d] == %d out of range\n", + i, + innerIndex )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } - /* TODO: implement other HVAR stuff */ - face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE; + map->innerIndex[i] = innerIndex; } + Exit: return error; } @@ -756,86 +705,181 @@ /*************************************************************************/ /* */ /* <Function> */ - /* tt_hadvance_adjust */ + /* ft_var_load_hvvar */ /* */ /* <Description> */ - /* Apply HVAR advance width adjustment of a given glyph. */ + /* If `vertical' is zero, parse the `HVAR' table and set */ + /* `blend->hvar_loaded' to TRUE. On success, `blend->hvar_checked' */ + /* is set to TRUE. */ /* */ - /* <Input> */ - /* gindex :: The glyph index. */ + /* If `vertical' is not zero, parse the `VVAR' table and set */ + /* `blend->vvar_loaded' to TRUE. On success, `blend->vvar_checked' */ + /* is set to TRUE. */ + /* */ + /* Some memory may remain allocated on error; it is always freed in */ + /* `tt_done_blend', however. */ /* */ /* <InOut> */ - /* face :: The font face. */ + /* face :: The font face. */ /* */ - /* adelta :: Points to width value that gets modified. */ + /* <Return> */ + /* FreeType error code. 0 means success. */ /* */ - FT_LOCAL_DEF( FT_Error ) - tt_hadvance_adjust( TT_Face face, - FT_UInt gindex, - FT_Int *avalue ) + static FT_Error + ft_var_load_hvvar( TT_Face face, + FT_Bool vertical ) { - FT_Error error = FT_Err_Ok; + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; - GX_HVarData varData; + GX_Blend blend = face->blend; - FT_UInt innerIndex, outerIndex; - FT_UInt master, j; - FT_Fixed netAdjustment = 0; /* accumulated adjustment */ - FT_Fixed scaledDelta; - FT_Short* deltaSet; - FT_Fixed delta; + GX_HVVarTable table; + FT_Error error; + FT_UShort majorVersion; + FT_ULong table_len; + FT_ULong table_offset; + FT_ULong store_offset; + FT_ULong widthMap_offset; - if ( !face->doblend || !face->blend ) - goto Exit; - if ( !face->blend->hvar_loaded ) + if ( vertical ) + { + blend->vvar_loaded = TRUE; + + FT_TRACE2(( "VVAR " )); + + error = face->goto_table( face, TTAG_VVAR, stream, &table_len ); + } + else + { + blend->hvar_loaded = TRUE; + + FT_TRACE2(( "HVAR " )); + + error = face->goto_table( face, TTAG_HVAR, stream, &table_len ); + } + + if ( error ) { - /* initialize hvar table */ - face->blend->hvar_error = ft_var_load_hvar( face ); + FT_TRACE2(( "is missing\n" )); + goto Exit; } - if ( !face->blend->hvar_checked ) + table_offset = FT_STREAM_POS(); + + /* skip minor version */ + if ( FT_READ_USHORT( majorVersion ) || + FT_STREAM_SKIP( 2 ) ) + goto Exit; + + if ( majorVersion != 1 ) { - error = face->blend->hvar_error; + FT_TRACE2(( "bad table version %d\n", majorVersion )); + error = FT_THROW( Invalid_Table ); goto Exit; } - /* advance width adjustments are always present in an `HVAR' table, */ - /* so need to test for this capability */ + if ( FT_READ_ULONG( store_offset ) || + FT_READ_ULONG( widthMap_offset ) ) + goto Exit; - if ( gindex >= face->blend->hvar_table->widthMap.mapCount ) + if ( vertical ) + { + if ( FT_NEW( blend->vvar_table ) ) + goto Exit; + table = blend->vvar_table; + } + else { - FT_TRACE2(( "gindex %d out of range\n", gindex )); - error = FT_THROW( Invalid_Argument ); + if ( FT_NEW( blend->hvar_table ) ) + goto Exit; + table = blend->hvar_table; + } + + error = ft_var_load_item_variation_store( + face, + table_offset + store_offset, + &table->itemStore ); + if ( error ) goto Exit; + + if ( widthMap_offset ) + { + error = ft_var_load_delta_set_index_mapping( + face, + table_offset + widthMap_offset, + &table->widthMap, + &table->itemStore ); + if ( error ) + goto Exit; } - /* trust that HVAR parser has checked indices */ - outerIndex = face->blend->hvar_table->widthMap.outerIndex[gindex]; - innerIndex = face->blend->hvar_table->widthMap.innerIndex[gindex]; - varData = &face->blend->hvar_table->itemStore.varData[outerIndex]; - deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex]; + FT_TRACE2(( "loaded\n" )); + error = FT_Err_Ok; + + Exit: + if ( !error ) + { + if ( vertical ) + { + blend->vvar_checked = TRUE; + + /* FreeType doesn't provide functions to quickly retrieve */ + /* TSB, BSB, or VORG values; we thus don't have to implement */ + /* support for those three item variation stores. */ + + face->variation_support |= TT_FACE_FLAG_VAR_VADVANCE; + } + else + { + blend->hvar_checked = TRUE; + + /* FreeType doesn't provide functions to quickly retrieve */ + /* LSB or RSB values; we thus don't have to implement */ + /* support for those two item variation stores. */ + + face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE; + } + } + + return error; + } + + + static FT_Int + ft_var_get_item_delta( TT_Face face, + GX_ItemVarStore itemStore, + FT_UInt outerIndex, + FT_UInt innerIndex ) + { + GX_ItemVarData varData; + FT_Short* deltaSet; + + FT_UInt master, j; + FT_Fixed netAdjustment = 0; /* accumulated adjustment */ + FT_Fixed scaledDelta; + FT_Fixed delta; + /* See pseudo code from `Font Variations Overview' */ /* in the OpenType specification. */ + varData = &itemStore->varData[outerIndex]; + deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex]; + /* outer loop steps through master designs to be blended */ for ( master = 0; master < varData->regionIdxCount; master++ ) { FT_Fixed scalar = FT_FIXED_ONE; FT_UInt regionIndex = varData->regionIndices[master]; - GX_AxisCoords axis = face->blend - ->hvar_table - ->itemStore.varRegionList[regionIndex] - .axisList; + GX_AxisCoords axis = itemStore->varRegionList[regionIndex].axisList; /* inner loop steps through axes in this region */ - for ( j = 0; - j < face->blend->hvar_table->itemStore.axisCount; - j++, axis++ ) + for ( j = 0; j < itemStore->axisCount; j++, axis++ ) { FT_Fixed axisScalar; @@ -889,18 +933,460 @@ } /* per-region loop */ - /* apply the accumulated adjustment to derive the interpolated value */ - FT_TRACE5(( "horizontal width %d adjusted by %d units (HVAR)\n", + return FT_fixedToInt( netAdjustment ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_hvadvance_adjust */ + /* */ + /* <Description> */ + /* Apply `HVAR' advance width or `VVAR' advance height adjustment of */ + /* a given glyph. */ + /* */ + /* <Input> */ + /* gindex :: The glyph index. */ + /* */ + /* vertical :: If set, handle `VVAR' table. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + /* adelta :: Points to width or height value that gets modified. */ + /* */ + static FT_Error + tt_hvadvance_adjust( TT_Face face, + FT_UInt gindex, + FT_Int *avalue, + FT_Bool vertical ) + { + FT_Error error = FT_Err_Ok; + FT_UInt innerIndex, outerIndex; + FT_Int delta; + + GX_HVVarTable table; + + + if ( !face->doblend || !face->blend ) + goto Exit; + + if ( vertical ) + { + if ( !face->blend->vvar_loaded ) + { + /* initialize vvar table */ + face->blend->vvar_error = ft_var_load_hvvar( face, 1 ); + } + + if ( !face->blend->vvar_checked ) + { + error = face->blend->vvar_error; + goto Exit; + } + + table = face->blend->vvar_table; + } + else + { + if ( !face->blend->hvar_loaded ) + { + /* initialize hvar table */ + face->blend->hvar_error = ft_var_load_hvvar( face, 0 ); + } + + if ( !face->blend->hvar_checked ) + { + error = face->blend->hvar_error; + goto Exit; + } + + table = face->blend->hvar_table; + } + + /* advance width or height adjustments are always present in an */ + /* `HVAR' or `VVAR' table; no need to test for this capability */ + + if ( table->widthMap.innerIndex ) + { + FT_UInt idx = gindex; + + + if ( idx >= table->widthMap.mapCount ) + idx = table->widthMap.mapCount - 1; + + /* trust that HVAR parser has checked indices */ + outerIndex = table->widthMap.outerIndex[idx]; + innerIndex = table->widthMap.innerIndex[idx]; + } + else + { + GX_ItemVarData varData; + + + /* no widthMap data */ + outerIndex = 0; + innerIndex = gindex; + + varData = &table->itemStore.varData[outerIndex]; + if ( gindex >= varData->itemCount ) + { + FT_TRACE2(( "gindex %d out of range\n", gindex )); + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + } + + delta = ft_var_get_item_delta( face, + &table->itemStore, + outerIndex, + innerIndex ); + + FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n", + vertical ? "vertical height" : "horizontal width", *avalue, - FT_fixedToInt( netAdjustment ) )); + delta, + delta == 1 ? "" : "s", + vertical ? "VVAR" : "HVAR" )); - *avalue += FT_fixedToInt( netAdjustment ); + *avalue += delta; Exit: return error; } + FT_LOCAL_DEF( FT_Error ) + tt_hadvance_adjust( TT_Face face, + FT_UInt gindex, + FT_Int *avalue ) + { + return tt_hvadvance_adjust( face, gindex, avalue, 0 ); + } + + + FT_LOCAL_DEF( FT_Error ) + tt_vadvance_adjust( TT_Face face, + FT_UInt gindex, + FT_Int *avalue ) + { + return tt_hvadvance_adjust( face, gindex, avalue, 1 ); + } + + +#define GX_VALUE_SIZE 8 + + /* all values are FT_Short or FT_UShort entities; */ + /* we treat them consistently as FT_Short */ +#define GX_VALUE_CASE( tag, dflt ) \ + case MVAR_TAG_ ## tag : \ + p = (FT_Short*)&face->dflt; \ + break + +#define GX_GASP_CASE( idx ) \ + case MVAR_TAG_GASP_ ## idx : \ + if ( idx < face->gasp.numRanges - 1 ) \ + p = (FT_Short*)&face->gasp.gaspRanges[idx].maxPPEM; \ + else \ + p = NULL; \ + break + + + static FT_Short* + ft_var_get_value_pointer( TT_Face face, + FT_ULong mvar_tag ) + { + FT_Short* p; + + + switch ( mvar_tag ) + { + GX_GASP_CASE( 0 ); + GX_GASP_CASE( 1 ); + GX_GASP_CASE( 2 ); + GX_GASP_CASE( 3 ); + GX_GASP_CASE( 4 ); + GX_GASP_CASE( 5 ); + GX_GASP_CASE( 6 ); + GX_GASP_CASE( 7 ); + GX_GASP_CASE( 8 ); + GX_GASP_CASE( 9 ); + + GX_VALUE_CASE( CPHT, os2.sCapHeight ); + GX_VALUE_CASE( HASC, os2.sTypoAscender ); + GX_VALUE_CASE( HCLA, os2.usWinAscent ); + GX_VALUE_CASE( HCLD, os2.usWinDescent ); + GX_VALUE_CASE( HCOF, horizontal.caret_Offset ); + GX_VALUE_CASE( HCRN, horizontal.caret_Slope_Run ); + GX_VALUE_CASE( HCRS, horizontal.caret_Slope_Rise ); + GX_VALUE_CASE( HDSC, os2.sTypoDescender ); + GX_VALUE_CASE( HLGP, os2.sTypoLineGap ); + GX_VALUE_CASE( SBXO, os2.ySubscriptXOffset); + GX_VALUE_CASE( SBXS, os2.ySubscriptXSize ); + GX_VALUE_CASE( SBYO, os2.ySubscriptYOffset ); + GX_VALUE_CASE( SBYS, os2.ySubscriptYSize ); + GX_VALUE_CASE( SPXO, os2.ySuperscriptXOffset ); + GX_VALUE_CASE( SPXS, os2.ySuperscriptXSize ); + GX_VALUE_CASE( SPYO, os2.ySuperscriptYOffset ); + GX_VALUE_CASE( SPYS, os2.ySuperscriptYSize ); + GX_VALUE_CASE( STRO, os2.yStrikeoutPosition ); + GX_VALUE_CASE( STRS, os2.yStrikeoutSize ); + GX_VALUE_CASE( UNDO, postscript.underlinePosition ); + GX_VALUE_CASE( UNDS, postscript.underlineThickness ); + GX_VALUE_CASE( VASC, vertical.Ascender ); + GX_VALUE_CASE( VCOF, vertical.caret_Offset ); + GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run ); + GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise ); + GX_VALUE_CASE( VDSC, vertical.Descender ); + GX_VALUE_CASE( VLGP, vertical.Line_Gap ); + GX_VALUE_CASE( XHGT, os2.sxHeight ); + + default: + /* ignore unknown tag */ + p = NULL; + } + + return p; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_load_mvar */ + /* */ + /* <Description> */ + /* Parse the `MVAR' table. */ + /* */ + /* Some memory may remain allocated on error; it is always freed in */ + /* `tt_done_blend', however. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + static void + ft_var_load_mvar( TT_Face face ) + { + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; + + GX_Blend blend = face->blend; + GX_ItemVarStore itemStore; + GX_Value value, limit; + + FT_Error error; + FT_UShort majorVersion; + FT_ULong table_len; + FT_ULong table_offset; + FT_UShort store_offset; + FT_ULong records_offset; + + + FT_TRACE2(( "MVAR " )); + + error = face->goto_table( face, TTAG_MVAR, stream, &table_len ); + if ( error ) + { + FT_TRACE2(( "is missing\n" )); + return; + } + + table_offset = FT_STREAM_POS(); + + /* skip minor version */ + if ( FT_READ_USHORT( majorVersion ) || + FT_STREAM_SKIP( 2 ) ) + return; + + if ( majorVersion != 1 ) + { + FT_TRACE2(( "bad table version %d\n", majorVersion )); + return; + } + + if ( FT_NEW( blend->mvar_table ) ) + return; + + /* skip reserved entry and value record size */ + if ( FT_STREAM_SKIP( 4 ) || + FT_READ_USHORT( blend->mvar_table->valueCount ) || + FT_READ_USHORT( store_offset ) ) + return; + + records_offset = FT_STREAM_POS(); + + error = ft_var_load_item_variation_store( + face, + table_offset + store_offset, + &blend->mvar_table->itemStore ); + if ( error ) + return; + + if ( FT_NEW_ARRAY( blend->mvar_table->values, + blend->mvar_table->valueCount ) ) + return; + + if ( FT_STREAM_SEEK( records_offset ) || + FT_FRAME_ENTER( blend->mvar_table->valueCount * GX_VALUE_SIZE ) ) + return; + + value = blend->mvar_table->values; + limit = value + blend->mvar_table->valueCount; + itemStore = &blend->mvar_table->itemStore; + + for ( ; value < limit; value++ ) + { + value->tag = FT_GET_ULONG(); + value->outerIndex = FT_GET_USHORT(); + value->innerIndex = FT_GET_USHORT(); + + if ( value->outerIndex >= itemStore->dataCount || + value->innerIndex >= itemStore->varData[value->outerIndex] + .itemCount ) + { + error = FT_THROW( Invalid_Table ); + break; + } + } + + FT_FRAME_EXIT(); + + if ( error ) + return; + + FT_TRACE2(( "loaded\n" )); + + value = blend->mvar_table->values; + limit = value + blend->mvar_table->valueCount; + + /* save original values of the data MVAR is going to modify */ + for ( ; value < limit; value++ ) + { + FT_Short* p = ft_var_get_value_pointer( face, value->tag ); + + + if ( p ) + value->unmodified = *p; +#ifdef FT_DEBUG_LEVEL_TRACE + else + FT_TRACE1(( "ft_var_load_mvar: Ignoring unknown tag `%c%c%c%c'\n", + (FT_Char)( value->tag >> 24 ), + (FT_Char)( value->tag >> 16 ), + (FT_Char)( value->tag >> 8 ), + (FT_Char)( value->tag ) )); +#endif + } + + face->variation_support |= TT_FACE_FLAG_VAR_MVAR; + } + + + static FT_Error + tt_size_reset_iterator( FT_ListNode node, + void* user ) + { + TT_Size size = (TT_Size)node->data; + + FT_UNUSED( user ); + + + tt_size_reset( size, 1 ); + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_apply_mvar */ + /* */ + /* <Description> */ + /* Apply `MVAR' table adjustments. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + FT_LOCAL_DEF( void ) + tt_apply_mvar( TT_Face face ) + { + GX_Blend blend = face->blend; + GX_Value value, limit; + + + if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) ) + return; + + value = blend->mvar_table->values; + limit = value + blend->mvar_table->valueCount; + + for ( ; value < limit; value++ ) + { + FT_Short* p = ft_var_get_value_pointer( face, value->tag ); + FT_Int delta; + + + delta = ft_var_get_item_delta( face, + &blend->mvar_table->itemStore, + value->outerIndex, + value->innerIndex ); + + if ( p ) + { + FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n", + (FT_Char)( value->tag >> 24 ), + (FT_Char)( value->tag >> 16 ), + (FT_Char)( value->tag >> 8 ), + (FT_Char)( value->tag ), + value->unmodified, + value->unmodified == 1 ? "" : "s", + delta, + delta == 1 ? "" : "s" )); + + /* since we handle both signed and unsigned values as FT_Short, */ + /* ensure proper overflow arithmetic */ + *p = (FT_Short)( value->unmodified + (FT_Short)delta ); + } + } + + /* adjust all derived values */ + { + FT_Face root = &face->root; + + + 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->underline_position = face->postscript.underlinePosition - + face->postscript.underlineThickness / 2; + root->underline_thickness = face->postscript.underlineThickness; + + /* iterate over all FT_Size objects and call `tt_size_reset' */ + /* to propagate the metrics changes */ + FT_List_Iterate( &root->sizes_list, + tt_size_reset_iterator, + NULL ); + } + } + + typedef struct GX_GVar_Head_ { FT_Long version; @@ -1000,10 +1486,9 @@ goto Exit; } - /* rough sanity check: offsets can be either 2 or 4 bytes, */ - /* and a single variation needs at least 4 bytes per glyph */ + /* rough sanity check: offsets can be either 2 or 4 bytes */ if ( (FT_ULong)gvar_head.glyphCount * - ( ( gvar_head.flags & 1 ) ? 8 : 6 ) > table_len ) + ( ( gvar_head.flags & 1 ) ? 4 : 2 ) > table_len ) { FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" )); error = FT_THROW( Invalid_Table ); @@ -1017,8 +1502,10 @@ blend->gv_glyphcnt = gvar_head.glyphCount; offsetToData = gvar_start + gvar_head.offsetToData; - FT_TRACE5(( "gvar: there are %d shared coordinates:\n", - blend->tuplecount )); + FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n", + blend->tuplecount == 1 ? "is" : "are", + blend->tuplecount, + blend->tuplecount == 1 ? "" : "s" )); if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) ) goto Exit; @@ -1216,6 +1703,180 @@ } + /* convert from design coordinates to normalized coordinates */ + + static void + ft_var_to_normalized( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords, + FT_Fixed* normalized ) + { + GX_Blend blend; + FT_MM_Var* mmvar; + FT_UInt i, j; + FT_Var_Axis* a; + GX_AVarSegment av; + + + blend = face->blend; + mmvar = blend->mmvar; + + if ( num_coords > mmvar->num_axis ) + { + FT_TRACE2(( "ft_var_to_normalized:" + " only using first %d of %d coordinates\n", + mmvar->num_axis, num_coords )); + num_coords = mmvar->num_axis; + } + + /* Axis normalization is a two-stage process. First we normalize */ + /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ + /* Then, if there's an `avar' table, we renormalize this range. */ + + a = mmvar->axis; + for ( i = 0; i < num_coords; i++, a++ ) + { + FT_Fixed coord = coords[i]; + + + FT_TRACE5(( " %d: %.5f\n", i, coord / 65536.0 )); + if ( coord > a->maximum || coord < a->minimum ) + { + FT_TRACE1(( + "ft_var_to_normalized: design coordinate %.5f\n" + " is out of range [%.5f;%.5f]; clamping\n", + coord / 65536.0, + a->minimum / 65536.0, + a->maximum / 65536.0 )); + + if ( coord > a->maximum ) + coord = a->maximum; + else + coord = a->minimum; + } + + if ( coord < a->def ) + normalized[i] = -FT_DivFix( coord - a->def, + a->minimum - a->def ); + else if ( coord > a->def ) + normalized[i] = FT_DivFix( coord - a->def, + a->maximum - a->def ); + else + normalized[i] = 0; + } + + FT_TRACE5(( "\n" )); + + for ( ; i < mmvar->num_axis; i++ ) + normalized[i] = 0; + + if ( blend->avar_segment ) + { + FT_TRACE5(( "normalized design coordinates" + " before applying `avar' data:\n" )); + + av = blend->avar_segment; + for ( i = 0; i < mmvar->num_axis; i++, av++ ) + { + for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) + { + if ( normalized[i] < av->correspondence[j].fromCoord ) + { + FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 )); + + normalized[i] = + FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, + av->correspondence[j].toCoord - + av->correspondence[j - 1].toCoord, + av->correspondence[j].fromCoord - + av->correspondence[j - 1].fromCoord ) + + av->correspondence[j - 1].toCoord; + break; + } + } + } + } + } + + + /* convert from normalized coordinates to design coordinates */ + + static void + ft_var_to_design( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords, + FT_Fixed* design ) + { + GX_Blend blend; + FT_MM_Var* mmvar; + FT_Var_Axis* a; + + FT_UInt i, j, nc; + + + blend = face->blend; + + nc = num_coords; + if ( num_coords > blend->num_axis ) + { + FT_TRACE2(( "ft_var_to_design:" + " only using first %d of %d coordinates\n", + blend->num_axis, num_coords )); + nc = blend->num_axis; + } + + for ( i = 0; i < nc; i++ ) + design[i] = coords[i]; + + for ( ; i < num_coords; i++ ) + design[i] = 0; + + if ( blend->avar_segment ) + { + GX_AVarSegment av = blend->avar_segment; + + + FT_TRACE5(( "design coordinates" + " after removing `avar' distortion:\n" )); + + for ( i = 0; i < nc; i++, av++ ) + { + for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) + { + if ( design[i] < av->correspondence[j].toCoord ) + { + design[i] = + FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord, + av->correspondence[j].fromCoord - + av->correspondence[j - 1].fromCoord, + av->correspondence[j].toCoord - + av->correspondence[j - 1].toCoord ) + + av->correspondence[j - 1].fromCoord; + + FT_TRACE5(( " %.5f\n", design[i] / 65536.0 )); + break; + } + } + } + } + + mmvar = blend->mmvar; + a = mmvar->axis; + + for ( i = 0; i < nc; i++, a++ ) + { + if ( design[i] < 0 ) + design[i] = a->def + FT_MulFix( design[i], + a->def - a->minimum ); + else if ( design[i] > 0 ) + design[i] = a->def + FT_MulFix( design[i], + a->maximum - a->def ); + else + design[i] = a->def; + } + } + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -1229,7 +1890,6 @@ { FT_Long version; FT_UShort offsetToData; - FT_UShort countSizePairs; FT_UShort axisCount; FT_UShort axisSize; FT_UShort instanceCount; @@ -1257,7 +1917,8 @@ /* */ /* <Description> */ /* Check that the font's `fvar' table is valid, parse it, and return */ - /* those data. */ + /* those data. It also loads (and parses) the `MVAR' table, if */ + /* possible. */ /* */ /* <InOut> */ /* face :: The font face. */ @@ -1274,19 +1935,33 @@ TT_Get_MM_Var( TT_Face face, FT_MM_Var* *master ) { - FT_Stream stream = face->root.stream; - FT_Memory memory = face->root.memory; + FT_Stream stream = face->root.stream; + FT_Memory memory = face->root.memory; FT_ULong table_len; - FT_Error error = FT_Err_Ok; - FT_ULong fvar_start; - FT_Int i, j; + FT_Error error = FT_Err_Ok; + FT_ULong fvar_start = 0; + FT_UInt i, j; FT_MM_Var* mmvar = NULL; FT_Fixed* next_coords; + FT_Fixed* nsc; FT_String* next_name; FT_Var_Axis* a; + FT_Fixed* c; FT_Var_Named_Style* ns; GX_FVar_Head fvar_head; - FT_Bool usePsName; + FT_Bool usePsName = 0; + FT_UInt num_instances; + FT_UInt num_axes; + FT_UShort* axis_flags; + + FT_Offset mmvar_size; + FT_Offset axis_flags_size; + FT_Offset axis_size; + FT_Offset namedstyle_size; + FT_Offset next_coords_size; + FT_Offset next_name_size; + + FT_Bool need_init; static const FT_Frame_Field fvar_fields[] = { @@ -1295,13 +1970,13 @@ #define FT_STRUCTURE GX_FVar_Head FT_FRAME_START( 16 ), - FT_FRAME_LONG ( version ), - FT_FRAME_USHORT( offsetToData ), - FT_FRAME_USHORT( countSizePairs ), - FT_FRAME_USHORT( axisCount ), - FT_FRAME_USHORT( axisSize ), - FT_FRAME_USHORT( instanceCount ), - FT_FRAME_USHORT( instanceSize ), + FT_FRAME_LONG ( version ), + FT_FRAME_USHORT ( offsetToData ), + FT_FRAME_SKIP_SHORT, + FT_FRAME_USHORT ( axisCount ), + FT_FRAME_USHORT ( axisSize ), + FT_FRAME_USHORT ( instanceCount ), + FT_FRAME_USHORT ( instanceSize ), FT_FRAME_END }; @@ -1325,7 +2000,9 @@ /* read the font data and set up the internal representation */ /* if not already done */ - if ( !face->blend ) + need_init = !face->blend; + + if ( need_init ) { FT_TRACE2(( "FVAR " )); @@ -1362,18 +2039,57 @@ FT_TRACE2(( "loaded\n" )); - FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount )); + FT_TRACE5(( "%d variation ax%s\n", + fvar_head.axisCount, + fvar_head.axisCount == 1 ? "is" : "es" )); if ( FT_NEW( face->blend ) ) goto Exit; - /* cannot overflow 32-bit arithmetic because of limits above */ - face->blend->mmvar_len = - sizeof ( FT_MM_Var ) + - fvar_head.axisCount * sizeof ( FT_Var_Axis ) + - fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) + - fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) + - 5 * fvar_head.axisCount; + num_axes = fvar_head.axisCount; + face->blend->num_axis = num_axes; + } + else + num_axes = face->blend->num_axis; + + /* `num_instances' holds the number of all named instances, */ + /* including the default instance which might be missing */ + /* in fvar's table of named instances */ + num_instances = (FT_UInt)face->root.style_flags >> 16; + + /* prepare storage area for MM data; this cannot overflow */ + /* 32-bit arithmetic because of the size limits used in the */ + /* `fvar' table validity check in `sfnt_init_face' */ + + /* the various `*_size' variables, which we also use as */ + /* offsets into the `mmlen' array, must be multiples of the */ + /* pointer size (except the last one); without such an */ + /* alignment there might be runtime errors due to */ + /* misaligned addresses */ +#undef ALIGN_SIZE +#define ALIGN_SIZE( n ) \ + ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) ) + + mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) ); + axis_flags_size = ALIGN_SIZE( num_axes * + sizeof ( FT_UShort ) ); + axis_size = ALIGN_SIZE( num_axes * + sizeof ( FT_Var_Axis ) ); + namedstyle_size = ALIGN_SIZE( num_instances * + sizeof ( FT_Var_Named_Style ) ); + next_coords_size = ALIGN_SIZE( num_instances * + num_axes * + sizeof ( FT_Fixed ) ); + next_name_size = num_axes * 5; + + if ( need_init ) + { + face->blend->mmvar_len = mmvar_size + + axis_flags_size + + axis_size + + namedstyle_size + + next_coords_size + + next_name_size; if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) goto Exit; @@ -1383,28 +2099,33 @@ /* the data gets filled in later on */ mmvar->num_axis = - fvar_head.axisCount; + num_axes; mmvar->num_designs = ~0U; /* meaningless in this context; each glyph */ /* may have a different number of designs */ /* (or tuples, as called by Apple) */ mmvar->num_namedstyles = - fvar_head.instanceCount; + num_instances; + + /* alas, no public field in `FT_Var_Axis' for axis flags */ + axis_flags = + (FT_UShort*)( (char*)mmvar + mmvar_size ); mmvar->axis = - (FT_Var_Axis*)&( mmvar[1] ); + (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); mmvar->namedstyle = - (FT_Var_Named_Style*)&( mmvar->axis[fvar_head.axisCount] ); + (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size ); - next_coords = - (FT_Fixed*)&( mmvar->namedstyle[fvar_head.instanceCount] ); - for ( i = 0; i < fvar_head.instanceCount; i++ ) + next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + + namedstyle_size ); + for ( i = 0; i < num_instances; i++ ) { mmvar->namedstyle[i].coords = next_coords; - next_coords += fvar_head.axisCount; + next_coords += num_axes; } - next_name = (FT_String*)next_coords; - for ( i = 0; i < fvar_head.axisCount; i++ ) + next_name = (FT_String*)( (char*)mmvar->namedstyle + + namedstyle_size + next_coords_size ); + for ( i = 0; i < num_axes; i++ ) { mmvar->axis[i].name = next_name; next_name += 5; @@ -1416,10 +2137,14 @@ goto Exit; a = mmvar->axis; - for ( i = 0; i < fvar_head.axisCount; i++ ) + for ( i = 0; i < num_axes; i++ ) { GX_FVar_Axis axis_rec; +#ifdef FT_DEBUG_LEVEL_TRACE + int invalid = 0; +#endif + if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) goto Exit; @@ -1435,47 +2160,189 @@ a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); a->name[4] = '\0'; + *axis_flags = axis_rec.flags; + if ( a->minimum > a->def || a->def > a->maximum ) { - FT_TRACE2(( "TT_Get_MM_Var:" - " invalid \"%s\" axis record; disabling\n", - a->name )); - a->minimum = a->def; a->maximum = a->def; + +#ifdef FT_DEBUG_LEVEL_TRACE + invalid = 1; +#endif } - FT_TRACE5(( " \"%s\": minimum=%.5f, default=%.5f, maximum=%.5f\n", +#ifdef FT_DEBUG_LEVEL_TRACE + if ( i == 0 ) + FT_TRACE5(( " idx tag " + /* " XXX `XXXX'" */ + " minimum default maximum flags\n" )); + /* " XXXX.XXXXX XXXX.XXXXX XXXX.XXXXX 0xXXXX" */ + + FT_TRACE5(( " %3d `%s'" + " %10.5f %10.5f %10.5f 0x%04X%s\n", + i, a->name, a->minimum / 65536.0, a->def / 65536.0, - a->maximum / 65536.0 )); + a->maximum / 65536.0, + *axis_flags, + invalid ? " (invalid, disabled)" : "" )); +#endif a++; + axis_flags++; } FT_TRACE5(( "\n" )); - ns = mmvar->namedstyle; + /* named instance coordinates are stored as design coordinates; */ + /* we have to convert them to normalized coordinates also */ + if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords, + num_axes * num_instances ) ) + goto Exit; + + if ( fvar_head.instanceCount && !face->blend->avar_loaded ) + { + FT_ULong offset = FT_STREAM_POS(); + + + ft_var_load_avar( face ); + + if ( FT_STREAM_SEEK( offset ) ) + goto Exit; + } + + FT_TRACE5(( "%d instance%s\n", + fvar_head.instanceCount, + fvar_head.instanceCount == 1 ? "" : "s" )); + + ns = mmvar->namedstyle; + nsc = face->blend->normalized_stylecoords; for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) { /* PostScript names add 2 bytes to the instance record size */ if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) + - 4L * fvar_head.axisCount ) ) + 4L * num_axes ) ) goto Exit; ns->strid = FT_GET_USHORT(); (void) /* flags = */ FT_GET_USHORT(); - for ( j = 0; j < fvar_head.axisCount; j++ ) - ns->coords[j] = FT_GET_LONG(); + c = ns->coords; + for ( j = 0; j < num_axes; j++, c++ ) + *c = FT_GET_LONG(); + /* valid psid values are 6, [256;32767], and 0xFFFF */ if ( usePsName ) ns->psid = FT_GET_USHORT(); + else + ns->psid = 0xFFFF; + +#ifdef FT_DEBUG_LEVEL_TRACE + { + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + FT_String* strname = NULL; + FT_String* psname = NULL; + + FT_ULong pos; + + + pos = FT_STREAM_POS(); + + if ( ns->strid != 0xFFFF ) + { + (void)sfnt->get_name( face, + (FT_UShort)ns->strid, + &strname ); + if ( strname && !ft_strcmp( strname, ".notdef" ) ) + strname = NULL; + } + + if ( ns->psid != 0xFFFF ) + { + (void)sfnt->get_name( face, + (FT_UShort)ns->psid, + &psname ); + if ( psname && !ft_strcmp( psname, ".notdef" ) ) + psname = NULL; + } + + (void)FT_STREAM_SEEK( pos ); + + FT_TRACE5(( " instance %d (%s%s%s, %s%s%s)\n", + i, + strname ? "name: `" : "", + strname ? strname : "unnamed", + strname ? "'" : "", + psname ? "PS name: `" : "", + psname ? psname : "no PS name", + psname ? "'" : "" )); + + FT_FREE( strname ); + FT_FREE( psname ); + } +#endif /* FT_DEBUG_LEVEL_TRACE */ + + ft_var_to_normalized( face, num_axes, ns->coords, nsc ); + nsc += num_axes; FT_FRAME_EXIT(); } + + if ( num_instances != fvar_head.instanceCount ) + { + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + FT_Int found, dummy1, dummy2; + FT_UInt strid = ~0U; + + + /* the default instance is missing in array the */ + /* of named instances; try to synthesize an entry */ + found = sfnt->get_name_id( face, + TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, + &dummy1, + &dummy2 ); + if ( found ) + strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY; + else + { + found = sfnt->get_name_id( face, + TT_NAME_ID_FONT_SUBFAMILY, + &dummy1, + &dummy2 ); + if ( found ) + strid = TT_NAME_ID_FONT_SUBFAMILY; + } + + if ( found ) + { + found = sfnt->get_name_id( face, + TT_NAME_ID_PS_NAME, + &dummy1, + &dummy2 ); + if ( found ) + { + FT_TRACE5(( "TT_Get_MM_Var:" + " Adding default instance to named instances\n" )); + + ns = &mmvar->namedstyle[fvar_head.instanceCount]; + + ns->strid = strid; + ns->psid = TT_NAME_ID_PS_NAME; + + a = mmvar->axis; + c = ns->coords; + for ( j = 0; j < num_axes; j++, a++, c++ ) + *c = a->def; + } + } + } + + ft_var_load_mvar( face ); } /* fill the output array if requested */ @@ -1489,22 +2356,25 @@ goto Exit; FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); + axis_flags = + (FT_UShort*)( (char*)mmvar + mmvar_size ); mmvar->axis = - (FT_Var_Axis*)&( mmvar[1] ); + (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); mmvar->namedstyle = - (FT_Var_Named_Style*)&( mmvar->axis[mmvar->num_axis] ); - next_coords = - (FT_Fixed*)&( mmvar->namedstyle[mmvar->num_namedstyles] ); + (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size ); + next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + + namedstyle_size ); for ( n = 0; n < mmvar->num_namedstyles; n++ ) { mmvar->namedstyle[n].coords = next_coords; - next_coords += mmvar->num_axis; + next_coords += num_axes; } a = mmvar->axis; - next_name = (FT_String*)next_coords; - for ( n = 0; n < mmvar->num_axis; n++ ) + next_name = (FT_String*)( (char*)mmvar->namedstyle + + namedstyle_size + next_coords_size ); + for ( n = 0; n < num_axes; n++ ) { a->name = next_name; @@ -1530,41 +2400,19 @@ } - /*************************************************************************/ - /* */ - /* <Function> */ - /* TT_Set_MM_Blend */ - /* */ - /* <Description> */ - /* Set the blend (normalized) coordinates for this instance of the */ - /* font. Check that the `gvar' table is reasonable and does some */ - /* initial preparation. */ - /* */ - /* <InOut> */ - /* face :: The font. */ - /* Initialize the blend structure with `gvar' data. */ - /* */ - /* <Input> */ - /* num_coords :: The number of available coordinates. If it is */ - /* larger than the number of axes, ignore the excess */ - /* values. If it is smaller than the number of axes, */ - /* use the default value (0) for the remaining axes. */ - /* */ - /* coords :: An array of `num_coords', each between [-1,1]. */ - /* */ - /* <Return> */ - /* FreeType error code. 0 means success. */ - /* */ - FT_LOCAL_DEF( FT_Error ) - TT_Set_MM_Blend( TT_Face face, + static FT_Error + tt_set_mm_blend( TT_Face face, FT_UInt num_coords, - FT_Fixed* coords ) + FT_Fixed* coords, + FT_Bool set_design_coords ) { FT_Error error = FT_Err_Ok; GX_Blend blend; FT_MM_Var* mmvar; FT_UInt i; - FT_Bool is_default_instance = 1; + + FT_Bool all_design_coords = FALSE; + FT_Memory memory = face->root.memory; enum @@ -1589,16 +2437,18 @@ if ( num_coords > mmvar->num_axis ) { - FT_TRACE2(( "TT_Set_MM_Blend: only using first %d of %d coordinates\n", + FT_TRACE2(( "TT_Set_MM_Blend:" + " only using first %d of %d coordinates\n", mmvar->num_axis, num_coords )); num_coords = mmvar->num_axis; } - FT_TRACE5(( "normalized design coordinates:\n" )); + FT_TRACE5(( "TT_Set_MM_Blend:\n" + " normalized design coordinates:\n" )); for ( i = 0; i < num_coords; i++ ) { - FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); + FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) { FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n" @@ -1607,17 +2457,23 @@ error = FT_THROW( Invalid_Argument ); goto Exit; } - - if ( coords[i] != 0 ) - is_default_instance = 0; } FT_TRACE5(( "\n" )); - if ( !face->isCFF2 && !blend->glyphoffsets ) + if ( !face->is_cff2 && !blend->glyphoffsets ) if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) ) goto Exit; + if ( !blend->coords ) + { + if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) + goto Exit; + + /* the first time we have to compute all design coordinates */ + all_design_coords = TRUE; + } + if ( !blend->normalizedcoords ) { if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) @@ -1631,6 +2487,12 @@ } else { + FT_Bool have_diff = 0; + FT_UInt j; + FT_Fixed* c; + FT_Fixed* n; + + manageCvt = mcvt_retain; for ( i = 0; i < num_coords; i++ ) @@ -1638,10 +2500,34 @@ if ( blend->normalizedcoords[i] != coords[i] ) { manageCvt = mcvt_load; + have_diff = 1; break; } } + if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) + { + FT_UInt idx = (FT_UInt)face->root.face_index >> 16; + + + c = blend->normalizedcoords + i; + n = blend->normalized_stylecoords + idx * mmvar->num_axis + i; + for ( j = i; j < mmvar->num_axis; j++, n++, c++ ) + if ( *c != *n ) + have_diff = 1; + } + else + { + c = blend->normalizedcoords + i; + for ( j = i; j < mmvar->num_axis; j++, c++ ) + if ( *c != 0 ) + have_diff = 1; + } + + /* return value -1 indicates `no change' */ + if ( !have_diff ) + return -1; + for ( ; i < mmvar->num_axis; i++ ) { if ( blend->normalizedcoords[i] != 0 ) @@ -1662,6 +2548,12 @@ coords, num_coords * sizeof ( FT_Fixed ) ); + if ( set_design_coords ) + ft_var_to_design( face, + all_design_coords ? blend->num_axis : num_coords, + blend->normalizedcoords, + blend->coords ); + face->doblend = TRUE; if ( face->cvt ) @@ -1689,7 +2581,9 @@ } } - face->is_default_instance = is_default_instance; + /* enforce recomputation of the PostScript name; */ + FT_FREE( face->postscript_name ); + face->postscript_name = NULL; Exit: return error; @@ -1699,6 +2593,52 @@ /*************************************************************************/ /* */ /* <Function> */ + /* TT_Set_MM_Blend */ + /* */ + /* <Description> */ + /* Set the blend (normalized) coordinates for this instance of the */ + /* font. Check that the `gvar' table is reasonable and does some */ + /* initial preparation. */ + /* */ + /* <InOut> */ + /* face :: The font. */ + /* Initialize the blend structure with `gvar' data. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available coordinates. If it is */ + /* larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use the default value (0) for the remaining axes. */ + /* */ + /* coords :: An array of `num_coords', each between [-1,1]. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Set_MM_Blend( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error; + + + error = tt_set_mm_blend( face, num_coords, coords, 1 ); + if ( error ) + return error; + + if ( num_coords ) + face->root.face_flags |= FT_FACE_FLAG_VARIATION; + else + face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; + + return FT_Err_Ok; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ /* TT_Get_MM_Blend */ /* */ /* <Description> */ @@ -1737,10 +2677,19 @@ blend = face->blend; + if ( !blend->coords ) + { + /* select default instance coordinates */ + /* if no instance is selected yet */ + if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) + return error; + } + nc = num_coords; if ( num_coords > blend->num_axis ) { - FT_TRACE2(( "TT_Get_MM_Blend: only using first %d of %d coordinates\n", + FT_TRACE2(( "TT_Get_MM_Blend:" + " only using first %d of %d coordinates\n", blend->num_axis, num_coords )); nc = blend->num_axis; } @@ -1793,14 +2742,17 @@ FT_UInt num_coords, FT_Fixed* coords ) { - FT_Error error = FT_Err_Ok; - FT_Fixed* normalized = NULL; - GX_Blend blend; - FT_MM_Var* mmvar; - FT_UInt i, j; - FT_Var_Axis* a; - GX_AVarSegment av; - FT_Memory memory = face->root.memory; + FT_Error error = FT_Err_Ok; + GX_Blend blend; + FT_MM_Var* mmvar; + FT_UInt i; + FT_Memory memory = face->root.memory; + + FT_Fixed* c; + FT_Fixed* n; + FT_Fixed* normalized = NULL; + + FT_Bool have_diff = 0; if ( !face->blend ) @@ -1820,83 +2772,81 @@ num_coords = mmvar->num_axis; } - /* Axis normalization is a two-stage process. First we normalize */ - /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ - /* Then, if there's an `avar' table, we renormalize this range. */ - - if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) - goto Exit; - - FT_TRACE5(( "design coordinates:\n" )); - - a = mmvar->axis; - for ( i = 0; i < num_coords; i++, a++ ) + if ( !blend->coords ) { - FT_Fixed coord = coords[i]; - + if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) + goto Exit; + } - FT_TRACE5(( " %.5f\n", coord / 65536.0 )); - if ( coord > a->maximum || coord < a->minimum ) + c = blend->coords; + n = coords; + for ( i = 0; i < num_coords; i++, n++, c++ ) + { + if ( *c != *n ) { - FT_TRACE1(( - "TT_Set_Var_Design: design coordinate %.5f\n" - " is out of range [%.5f;%.5f]; clamping\n", - coord / 65536.0, - a->minimum / 65536.0, - a->maximum / 65536.0 )); - - if ( coord > a->maximum) - coord = a->maximum; - else - coord = a->minimum; + *c = *n; + have_diff = 1; } - - if ( coord < a->def ) - normalized[i] = -FT_DivFix( coords[i] - a->def, - a->minimum - a->def ); - else if ( coord > a->def ) - normalized[i] = FT_DivFix( coords[i] - a->def, - a->maximum - a->def ); - else - normalized[i] = 0; } - FT_TRACE5(( "\n" )); + if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) + { + FT_UInt instance_index; + FT_Var_Named_Style* named_style; - for ( ; i < mmvar->num_axis; i++ ) - normalized[i] = 0; - if ( !blend->avar_checked ) - ft_var_load_avar( face ); + instance_index = (FT_UInt)face->root.face_index >> 16; + named_style = mmvar->namedstyle + instance_index - 1; - if ( blend->avar_segment ) + n = named_style->coords + num_coords; + for ( ; i < mmvar->num_axis; i++, n++, c++ ) + { + if ( *c != *n ) + { + *c = *n; + have_diff = 1; + } + } + } + else { - FT_TRACE5(( "normalized design coordinates" - " before applying `avar' data:\n" )); + FT_Var_Axis* a; - av = blend->avar_segment; - for ( i = 0; i < mmvar->num_axis; i++, av++ ) + + a = mmvar->axis + num_coords; + for ( ; i < mmvar->num_axis; i++, a++, c++ ) { - for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) + if ( *c != a->def ) { - if ( normalized[i] < av->correspondence[j].fromCoord ) - { - FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 )); - - normalized[i] = - FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, - av->correspondence[j].toCoord - - av->correspondence[j - 1].toCoord, - av->correspondence[j].fromCoord - - av->correspondence[j - 1].fromCoord ) + - av->correspondence[j - 1].toCoord; - break; - } + *c = a->def; + have_diff = 1; } } } - error = TT_Set_MM_Blend( face, mmvar->num_axis, normalized ); + /* return value -1 indicates `no change'; */ + /* we can exit early if `normalizedcoords' is already computed */ + if ( blend->normalizedcoords && !have_diff ) + return -1; + + if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) + goto Exit; + + if ( !face->blend->avar_loaded ) + ft_var_load_avar( face ); + + FT_TRACE5(( "TT_Set_Var_Design:\n" + " normalized design coordinates:\n" )); + ft_var_to_normalized( face, num_coords, blend->coords, normalized ); + + error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 ); + if ( error ) + goto Exit; + + if ( num_coords ) + face->root.face_flags |= FT_FACE_FLAG_VARIATION; + else + face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; Exit: FT_FREE( normalized ); @@ -1932,12 +2882,8 @@ FT_Fixed* coords ) { FT_Error error = FT_Err_Ok; - - GX_Blend blend; - FT_MM_Var* mmvar; - FT_Var_Axis* a; - - FT_UInt i, j, nc; + GX_Blend blend; + FT_UInt i, nc; if ( !face->blend ) @@ -1948,10 +2894,19 @@ blend = face->blend; + if ( !blend->coords ) + { + /* select default instance coordinates */ + /* if no instance is selected yet */ + if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) + return error; + } + nc = num_coords; if ( num_coords > blend->num_axis ) { - FT_TRACE2(( "TT_Get_Var_Design: only using first %d of %d coordinates\n", + FT_TRACE2(( "TT_Get_Var_Design:" + " only using first %d of %d coordinates\n", blend->num_axis, num_coords )); nc = blend->num_axis; } @@ -1959,7 +2914,7 @@ if ( face->doblend ) { for ( i = 0; i < nc; i++ ) - coords[i] = blend->normalizedcoords[i]; + coords[i] = blend->coords[i]; } else { @@ -1970,54 +2925,91 @@ for ( ; i < num_coords; i++ ) coords[i] = 0; - if ( !blend->avar_checked ) - ft_var_load_avar( face ); + return FT_Err_Ok; + } - if ( blend->avar_segment ) - { - GX_AVarSegment av = blend->avar_segment; + /*************************************************************************/ + /* */ + /* <Function> */ + /* TT_Set_Named_Instance */ + /* */ + /* <Description> */ + /* Set the given named instance, also resetting any further */ + /* variation. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* instance_index :: The instance index, starting with value 1. */ + /* Value 0 indicates to not use an instance. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + TT_Set_Named_Instance( TT_Face face, + FT_UInt instance_index ) + { + FT_Error error = FT_ERR( Invalid_Argument ); + GX_Blend blend; + FT_MM_Var* mmvar; - FT_TRACE5(( "design coordinates" - " after removing `avar' distortion:\n" )); + FT_UInt num_instances; - for ( i = 0; i < nc; i++, av++ ) - { - for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) - { - if ( coords[i] < av->correspondence[j].toCoord ) - { - coords[i] = - FT_MulDiv( coords[i] - av->correspondence[j - 1].toCoord, - av->correspondence[j].fromCoord - - av->correspondence[j - 1].fromCoord, - av->correspondence[j].toCoord - - av->correspondence[j - 1].toCoord ) + - av->correspondence[j - 1].fromCoord; - FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); - break; - } - } - } + if ( !face->blend ) + { + if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) + goto Exit; } + blend = face->blend; mmvar = blend->mmvar; - a = mmvar->axis; - for ( i = 0; i < nc; i++, a++ ) + num_instances = (FT_UInt)face->root.style_flags >> 16; + + /* `instance_index' starts with value 1, thus `>' */ + if ( instance_index > num_instances ) + goto Exit; + + if ( instance_index > 0 && mmvar->namedstyle ) { - if ( coords[i] < 0 ) - coords[i] = a->def + FT_MulFix( coords[i], - a->def - a->minimum ); - else if ( coords[i] > 0 ) - coords[i] = a->def + FT_MulFix( coords[i], - a->maximum - a->def ); - else - coords[i] = a->def; + FT_Memory memory = face->root.memory; + SFNT_Service sfnt = (SFNT_Service)face->sfnt; + + FT_Var_Named_Style* named_style; + FT_String* style_name; + + + named_style = mmvar->namedstyle + instance_index - 1; + + error = sfnt->get_name( face, + (FT_UShort)named_style->strid, + &style_name ); + if ( error ) + goto Exit; + + /* set (or replace) style name */ + FT_FREE( face->root.style_name ); + face->root.style_name = style_name; + + /* finally, select the named instance */ + error = TT_Set_Var_Design( face, + mmvar->num_axis, + named_style->coords ); + if ( error ) + goto Exit; } + else + error = TT_Set_Var_Design( face, 0, NULL ); - return FT_Err_Ok; + face->root.face_index = ( instance_index << 16 ) | + ( face->root.face_index & 0xFFFFL ); + face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; + + Exit: + return error; } @@ -2067,8 +3059,10 @@ FT_Fixed* im_start_coords = NULL; FT_Fixed* im_end_coords = NULL; GX_Blend blend = face->blend; - FT_UInt point_count; - FT_UShort* localpoints; + FT_UInt point_count, spoint_count = 0; + FT_UShort* sharedpoints = NULL; + FT_UShort* localpoints = NULL; + FT_UShort* points; FT_Short* deltas; @@ -2137,11 +3131,24 @@ offsetToData += table_start; - /* The documentation implies there are flags packed into */ - /* `tupleCount', but John Jenkins says that shared points don't apply */ - /* to `cvar', and no other flags are defined. */ + if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) + { + here = FT_Stream_FTell( stream ); + + FT_Stream_SeekSet( stream, offsetToData ); + + sharedpoints = ft_var_readpackedpoints( stream, + table_len, + &spoint_count ); + offsetToData = FT_Stream_FTell( stream ); + + FT_Stream_SeekSet( stream, here ); + } - FT_TRACE5(( "cvar: there are %d tuples:\n", tupleCount & 0xFFF )); + FT_TRACE5(( "cvar: there %s %d tuple%s:\n", + ( tupleCount & 0xFFF ) == 1 ? "is" : "are", + tupleCount & 0xFFF, + ( tupleCount & 0xFFF ) == 1 ? "" : "s" )); for ( i = 0; i < ( tupleCount & 0xFFF ); i++ ) { @@ -2155,26 +3162,25 @@ tupleDataSize = FT_GET_USHORT(); tupleIndex = FT_GET_USHORT(); - /* There is no provision here for a global tuple coordinate section, */ - /* so John says. There are no tuple indices, just embedded tuples. */ - if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) { for ( j = 0; j < blend->num_axis; j++ ) tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ /* short frac to fixed */ } - else + else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) { - /* skip this tuple; it makes no sense */ - - if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) - for ( j = 0; j < 2 * blend->num_axis; j++ ) - (void)FT_GET_SHORT(); + FT_TRACE2(( "tt_face_vary_cvt:" + " invalid tuple index\n" )); - offsetToData += tupleDataSize; - continue; + error = FT_THROW( Invalid_Table ); + goto Exit; } + else + FT_MEM_COPY( + tuple_coords, + &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], + blend->num_axis * sizeof ( FT_Fixed ) ); if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) { @@ -2189,11 +3195,8 @@ tuple_coords, im_start_coords, im_end_coords ); - if ( /* tuple isn't active for our blend */ - apply == 0 || - /* global points not allowed, */ - /* if they aren't local, makes no sense */ - !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) ) + + if ( apply == 0 ) /* tuple isn't active for our blend */ { offsetToData += tupleDataSize; continue; @@ -2203,14 +3206,27 @@ FT_Stream_SeekSet( stream, offsetToData ); - localpoints = ft_var_readpackedpoints( stream, - table_len, - &point_count ); - deltas = ft_var_readpackeddeltas( stream, - table_len, - point_count == 0 ? face->cvt_size - : point_count ); - if ( !localpoints || !deltas ) + if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) + { + localpoints = ft_var_readpackedpoints( stream, + table_len, + &point_count ); + points = localpoints; + } + else + { + points = sharedpoints; + point_count = spoint_count; + } + + deltas = ft_var_readpackeddeltas( stream, + table_len, + point_count == 0 ? face->cvt_size + : point_count ); + + if ( !points || + !deltas || + ( localpoints == ALL_POINTS && point_count != face->cvt_size ) ) ; /* failure, ignore it */ else if ( localpoints == ALL_POINTS ) @@ -2262,7 +3278,7 @@ FT_Long orig_cvt; - pindex = localpoints[j]; + pindex = points[j]; if ( (FT_ULong)pindex >= face->cvt_size ) continue; @@ -2301,6 +3317,8 @@ FT_FRAME_EXIT(); Exit: + if ( sharedpoints != ALL_POINTS ) + FT_FREE( sharedpoints ); FT_FREE( tuple_coords ); FT_FREE( im_start_coords ); FT_FREE( im_end_coords ); @@ -2389,25 +3407,12 @@ d1 = out1 - in1; d2 = out2 - in2; - if ( out1 == out2 || in1 == in2 ) + /* If the reference points have the same coordinate but different */ + /* delta, inferred delta is zero. Otherwise interpolate. */ + if ( in1 != in2 || out1 == out2 ) { - for ( p = p1; p <= p2; p++ ) - { - out = in_points[p].x; - - if ( out <= in1 ) - out += d1; - else if ( out >= in2 ) - out += d2; - else - out = out1; - - out_points[p].x = out; - } - } - else - { - FT_Fixed scale = FT_DivFix( out2 - out1, in2 - in1 ); + FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 ) + : 0; for ( p = p1; p <= p2; p++ ) @@ -2600,7 +3605,6 @@ glyph_start = FT_Stream_FTell( stream ); /* each set of glyph variation data is formatted similarly to `cvar' */ - /* (except we get shared points and global tuples) */ if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || @@ -2637,8 +3641,10 @@ FT_Stream_SeekSet( stream, here ); } - FT_TRACE5(( "gvar: there are %d tuples:\n", - tupleCount & GX_TC_TUPLE_COUNT_MASK )); + FT_TRACE5(( "gvar: there %s %d tuple%s:\n", + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", + tupleCount & GX_TC_TUPLE_COUNT_MASK, + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); for ( j = 0; j < n_points; j++ ) points_org[j] = outline->points[j]; @@ -2740,7 +3746,7 @@ FT_Pos delta_y = FT_MulFix( deltas_y[j], apply ); - if ( j < n_points - 3 ) + if ( j < n_points - 4 ) { outline->points[j].x += delta_x; outline->points[j].y += delta_y; @@ -2750,25 +3756,25 @@ /* To avoid double adjustment of advance width or height, */ /* adjust phantom points only if there is no HVAR or VVAR */ /* support, respectively. */ - if ( j == ( n_points - 3 ) && - !( face->variation_support & - TT_FACE_FLAG_VAR_HADVANCE ) ) + if ( j == ( n_points - 4 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_LSB ) ) + outline->points[j].x += delta_x; + + else if ( j == ( n_points - 3 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_HADVANCE ) ) outline->points[j].x += delta_x; else if ( j == ( n_points - 2 ) && !( face->variation_support & - TT_FACE_FLAG_VAR_LSB ) ) - outline->points[j].x += delta_x; + TT_FACE_FLAG_VAR_TSB ) ) + outline->points[j].y += delta_y; else if ( j == ( n_points - 1 ) && !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) outline->points[j].y += delta_y; - - else if ( j == ( n_points - 0 ) && - !( face->variation_support & - TT_FACE_FLAG_VAR_TSB ) ) - outline->points[j].y += delta_y; } #ifdef FT_DEBUG_LEVEL_TRACE @@ -2835,8 +3841,36 @@ FT_Pos delta_y = points_out[j].y - points_org[j].y; - outline->points[j].x += delta_x; - outline->points[j].y += delta_y; + if ( j < n_points - 4 ) + { + outline->points[j].x += delta_x; + outline->points[j].y += delta_y; + } + else + { + /* To avoid double adjustment of advance width or height, */ + /* adjust phantom points only if there is no HVAR or VVAR */ + /* support, respectively. */ + if ( j == ( n_points - 4 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_LSB ) ) + outline->points[j].x += delta_x; + + else if ( j == ( n_points - 3 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_HADVANCE ) ) + outline->points[j].x += delta_x; + + else if ( j == ( n_points - 2 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_TSB ) ) + outline->points[j].y += delta_y; + + else if ( j == ( n_points - 1 ) && + !( face->variation_support & + TT_FACE_FLAG_VAR_VADVANCE ) ) + outline->points[j].y += delta_y; + } #ifdef FT_DEBUG_LEVEL_TRACE if ( delta_x || delta_y ) @@ -2902,16 +3936,19 @@ tt_get_var_blend( TT_Face face, FT_UInt *num_coords, FT_Fixed* *coords, + FT_Fixed* *normalizedcoords, FT_MM_Var* *mm_var ) { if ( face->blend ) { if ( num_coords ) - *num_coords = face->blend->num_axis; + *num_coords = face->blend->num_axis; if ( coords ) - *coords = face->blend->normalizedcoords; + *coords = face->blend->coords; + if ( normalizedcoords ) + *normalizedcoords = face->blend->normalizedcoords; if ( mm_var ) - *mm_var = face->blend->mmvar; + *mm_var = face->blend->mmvar; } else { @@ -2927,6 +3964,35 @@ } + static void + ft_var_done_item_variation_store( TT_Face face, + GX_ItemVarStore itemStore ) + { + FT_Memory memory = FT_FACE_MEMORY( face ); + FT_UInt i; + + + if ( itemStore->varData ) + { + for ( i = 0; i < itemStore->dataCount; i++ ) + { + FT_FREE( itemStore->varData[i].regionIndices ); + FT_FREE( itemStore->varData[i].deltaSet ); + } + + FT_FREE( itemStore->varData ); + } + + if ( itemStore->varRegionList ) + { + for ( i = 0; i < itemStore->regionCount; i++ ) + FT_FREE( itemStore->varRegionList[i].axisList ); + + FT_FREE( itemStore->varRegionList ); + } + } + + /*************************************************************************/ /* */ /* <Function> */ @@ -2950,7 +4016,9 @@ /* blend->num_axis might not be set up yet */ num_axes = blend->mmvar->num_axis; + FT_FREE( blend->coords ); FT_FREE( blend->normalizedcoords ); + FT_FREE( blend->normalized_stylecoords ); FT_FREE( blend->mmvar ); if ( blend->avar_segment ) @@ -2962,35 +4030,45 @@ if ( blend->hvar_table ) { - if ( blend->hvar_table->itemStore.varData ) - { - for ( i = 0; i < blend->hvar_table->itemStore.dataCount; i++ ) - { - FT_FREE( blend->hvar_table->itemStore.varData[i].regionIndices ); - FT_FREE( blend->hvar_table->itemStore.varData[i].deltaSet ); - } - FT_FREE( blend->hvar_table->itemStore.varData ); - } - - if ( blend->hvar_table->itemStore.varRegionList ) - { - for ( i = 0; i < blend->hvar_table->itemStore.regionCount; i++ ) - FT_FREE( blend->hvar_table->itemStore.varRegionList[i].axisList ); - FT_FREE( blend->hvar_table->itemStore.varRegionList ); - } + ft_var_done_item_variation_store( face, + &blend->hvar_table->itemStore ); FT_FREE( blend->hvar_table->widthMap.innerIndex ); FT_FREE( blend->hvar_table->widthMap.outerIndex ); FT_FREE( blend->hvar_table ); } + if ( blend->vvar_table ) + { + ft_var_done_item_variation_store( face, + &blend->vvar_table->itemStore ); + + FT_FREE( blend->vvar_table->widthMap.innerIndex ); + FT_FREE( blend->vvar_table->widthMap.outerIndex ); + FT_FREE( blend->vvar_table ); + } + + if ( blend->mvar_table ) + { + ft_var_done_item_variation_store( face, + &blend->mvar_table->itemStore ); + + FT_FREE( blend->mvar_table->values ); + FT_FREE( blend->mvar_table ); + } + FT_FREE( blend->tuplecoords ); FT_FREE( blend->glyphoffsets ); FT_FREE( blend ); } } -#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ +#else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ + + /* ANSI C doesn't like empty source files */ + typedef int _tt_gxvar_dummy; + +#endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ /* END */ |