summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/freetype/src/truetype/ttgxvar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/freetype/src/truetype/ttgxvar.c')
-rw-r--r--src/3rdparty/freetype/src/truetype/ttgxvar.c2690
1 files changed, 2345 insertions, 345 deletions
diff --git a/src/3rdparty/freetype/src/truetype/ttgxvar.c b/src/3rdparty/freetype/src/truetype/ttgxvar.c
index dd9e250c94..29ab2a4efd 100644
--- a/src/3rdparty/freetype/src/truetype/ttgxvar.c
+++ b/src/3rdparty/freetype/src/truetype/ttgxvar.c
@@ -4,7 +4,7 @@
/* */
/* TrueType GX Font Variation loader */
/* */
-/* Copyright 2004-2015 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
/*************************************************************************/
@@ -112,6 +113,8 @@
/* <Input> */
/* stream :: The data stream. */
/* */
+ /* size :: The size of the table holding the data. */
+ /* */
/* <Output> */
/* point_cnt :: The number of points read. A zero value means that */
/* all points in the glyph will be affected, without */
@@ -123,6 +126,7 @@
/* */
static FT_UShort*
ft_var_readpackedpoints( FT_Stream stream,
+ FT_ULong size,
FT_UInt *point_cnt )
{
FT_UShort *points = NULL;
@@ -149,48 +153,55 @@
n |= FT_GET_BYTE();
}
- if ( FT_NEW_ARRAY( points, n ) )
+ if ( n > size )
+ {
+ FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
+ return NULL;
+ }
+
+ /* in the nested loops below we increase `i' twice; */
+ /* it is faster to simply allocate one more slot */
+ /* than to add another test within the loop */
+ if ( FT_NEW_ARRAY( points, n + 1 ) )
return NULL;
*point_cnt = n;
- i = 0;
+ first = 0;
+ i = 0;
while ( i < n )
{
runcnt = FT_GET_BYTE();
if ( runcnt & GX_PT_POINTS_ARE_WORDS )
{
runcnt &= GX_PT_POINT_RUN_COUNT_MASK;
- first = FT_GET_USHORT();
+ first += FT_GET_USHORT();
points[i++] = first;
- if ( runcnt < 1 || i + runcnt > n )
- goto Exit;
-
/* first point not included in run count */
for ( j = 0; j < runcnt; j++ )
{
first += FT_GET_USHORT();
points[i++] = first;
+ if ( i >= n )
+ break;
}
}
else
{
- first = FT_GET_BYTE();
+ first += FT_GET_BYTE();
points[i++] = first;
- if ( runcnt < 1 || i + runcnt > n )
- goto Exit;
-
for ( j = 0; j < runcnt; j++ )
{
first += FT_GET_BYTE();
points[i++] = first;
+ if ( i >= n )
+ break;
}
}
}
- Exit:
return points;
}
@@ -212,6 +223,8 @@
/* <Input> */
/* stream :: The data stream. */
/* */
+ /* size :: The size of the table holding the data. */
+ /* */
/* delta_cnt :: The number of deltas to be read. */
/* */
/* <Return> */
@@ -222,6 +235,7 @@
/* */
static FT_Short*
ft_var_readpackeddeltas( FT_Stream stream,
+ FT_ULong size,
FT_UInt delta_cnt )
{
FT_Short *deltas = NULL;
@@ -233,6 +247,12 @@
FT_UNUSED( error );
+ if ( delta_cnt > size )
+ {
+ FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
+ return NULL;
+ }
+
if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
return NULL;
@@ -303,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 )
{
@@ -327,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;
}
@@ -341,7 +361,8 @@
FT_TRACE5(( " axis %d:\n", i ));
segment->pairCount = FT_GET_USHORT();
- if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
+ if ( (FT_ULong)segment->pairCount * 4 > table_len ||
+ FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
{
/* Failure. Free everything we have done so far. We must do */
/* it right now since loading the `avar' table is optional. */
@@ -360,7 +381,7 @@
segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4;
segment->correspondence[j].toCoord = FT_GET_SHORT() * 4;
- FT_TRACE5(( " mapping %.4f to %.4f\n",
+ FT_TRACE5(( " mapping %.5f to %.5f\n",
segment->correspondence[j].fromCoord / 65536.0,
segment->correspondence[j].toCoord / 65536.0 ));
}
@@ -373,6 +394,999 @@
}
+ /* some macros we need */
+#define FT_FIXED_ONE ( (FT_Fixed)0x10000 )
+
+#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 ) )
+
+
+ static FT_Error
+ 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;
+
+ FT_Error error;
+ FT_UShort format;
+ FT_ULong region_offset;
+ FT_UInt i, j, k;
+ FT_UInt shortDeltaCount;
+
+ GX_Blend blend = face->blend;
+ GX_ItemVarData varData;
+
+ FT_ULong* dataOffsetArray = NULL;
+
+
+ if ( FT_STREAM_SEEK( offset ) ||
+ FT_READ_USHORT( format ) )
+ goto Exit;
+
+ if ( format != 1 )
+ {
+ FT_TRACE2(( "ft_var_load_item_variation_store: bad store format %d\n",
+ format ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* read top level fields */
+ if ( FT_READ_ULONG( region_offset ) ||
+ FT_READ_USHORT( itemStore->dataCount ) )
+ goto Exit;
+
+ /* we need at least one entry in `itemStore->varData' */
+ if ( !itemStore->dataCount )
+ {
+ FT_TRACE2(( "ft_var_load_item_variation_store: missing varData\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;
+
+ for ( i = 0; i < itemStore->dataCount; i++ )
+ {
+ if ( FT_READ_ULONG( dataOffsetArray[i] ) )
+ goto Exit;
+ }
+
+ /* parse array of region records (region list) */
+ if ( FT_STREAM_SEEK( offset + region_offset ) )
+ goto Exit;
+
+ if ( FT_READ_USHORT( itemStore->axisCount ) ||
+ FT_READ_USHORT( itemStore->regionCount ) )
+ 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;
+ }
+
+ if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) )
+ goto Exit;
+
+ for ( i = 0; i < itemStore->regionCount; i++ )
+ {
+ GX_AxisCoords axisCoords;
+
+
+ if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList,
+ itemStore->axisCount ) )
+ goto Exit;
+
+ axisCoords = itemStore->varRegionList[i].axisList;
+
+ for ( j = 0; j < itemStore->axisCount; j++ )
+ {
+ FT_Short start, peak, end;
+
+
+ if ( FT_READ_SHORT( start ) ||
+ FT_READ_SHORT( peak ) ||
+ FT_READ_SHORT( end ) )
+ goto Exit;
+
+ axisCoords[j].startCoord = FT_fdot14ToFixed( start );
+ axisCoords[j].peakCoord = FT_fdot14ToFixed( peak );
+ axisCoords[j].endCoord = FT_fdot14ToFixed( end );
+ }
+ }
+
+ /* end of region list parse */
+
+ /* use dataOffsetArray now to parse varData items */
+ if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) )
+ goto Exit;
+
+ for ( i = 0; i < itemStore->dataCount; i++ )
+ {
+ varData = &itemStore->varData[i];
+
+ if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
+ goto Exit;
+
+ if ( FT_READ_USHORT( varData->itemCount ) ||
+ FT_READ_USHORT( shortDeltaCount ) ||
+ FT_READ_USHORT( varData->regionIdxCount ) )
+ goto Exit;
+
+ /* 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;
+ }
+
+ if ( varData->regionIdxCount > itemStore->regionCount )
+ {
+ FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n",
+ varData->regionIdxCount,
+ i ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* parse region indices */
+ if ( FT_NEW_ARRAY( varData->regionIndices,
+ varData->regionIdxCount ) )
+ goto Exit;
+
+ for ( j = 0; j < varData->regionIdxCount; j++ )
+ {
+ if ( FT_READ_USHORT( varData->regionIndices[j] ) )
+ goto Exit;
+
+ if ( varData->regionIndices[j] >= itemStore->regionCount )
+ {
+ 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;
+
+ /* 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++ )
+ {
+ /* read the short deltas */
+ FT_Short delta;
+
+
+ if ( FT_READ_SHORT( delta ) )
+ goto Exit;
+
+ varData->deltaSet[j] = delta;
+ }
+
+ for ( ; k < varData->regionIdxCount; k++, j++ )
+ {
+ /* read the (signed) byte deltas */
+ FT_Char delta;
+
+
+ if ( FT_READ_CHAR( delta ) )
+ goto Exit;
+
+ varData->deltaSet[j] = delta;
+ }
+ }
+ }
+
+ Exit:
+ FT_FREE( dataOffsetArray );
+
+ return error;
+ }
+
+
+ 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;
+
+ FT_Error error;
+
+ FT_UShort format;
+ FT_UInt entrySize;
+ FT_UInt innerBitCount;
+ FT_UInt innerIndexMask;
+ FT_UInt i, j;
+
+
+ if ( FT_STREAM_SEEK( offset ) ||
+ FT_READ_USHORT( format ) ||
+ FT_READ_USHORT( map->mapCount ) )
+ goto Exit;
+
+ 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_NEW_ARRAY( map->innerIndex, map->mapCount ) )
+ goto Exit;
+
+ if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) )
+ goto Exit;
+
+ for ( i = 0; i < map->mapCount; i++ )
+ {
+ FT_UInt mapData = 0;
+ FT_UInt outerIndex, innerIndex;
+
+
+ /* read map data one unsigned byte at a time, big endian */
+ for ( j = 0; j < entrySize; j++ )
+ {
+ FT_Byte data;
+
+
+ if ( FT_READ_BYTE( data ) )
+ goto Exit;
+
+ mapData = ( mapData << 8 ) | data;
+ }
+
+ outerIndex = mapData >> innerBitCount;
+
+ if ( outerIndex >= itemStore->dataCount )
+ {
+ FT_TRACE2(( "outerIndex[%d] == %d out of range\n",
+ i,
+ outerIndex ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ map->outerIndex[i] = outerIndex;
+
+ innerIndex = mapData & innerIndexMask;
+
+ if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
+ {
+ FT_TRACE2(( "innerIndex[%d] == %d out of range\n",
+ i,
+ innerIndex ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ map->innerIndex[i] = innerIndex;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_var_load_hvvar */
+ /* */
+ /* <Description> */
+ /* If `vertical' is zero, parse the `HVAR' table and set */
+ /* `blend->hvar_loaded' to TRUE. On success, `blend->hvar_checked' */
+ /* is set to TRUE. */
+ /* */
+ /* 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. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ ft_var_load_hvvar( TT_Face face,
+ FT_Bool vertical )
+ {
+ FT_Stream stream = FT_FACE_STREAM( face );
+ FT_Memory memory = stream->memory;
+
+ GX_Blend blend = face->blend;
+
+ 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 ( 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 )
+ {
+ FT_TRACE2(( "is missing\n" ));
+ goto Exit;
+ }
+
+ table_offset = FT_STREAM_POS();
+
+ /* skip minor version */
+ if ( FT_READ_USHORT( majorVersion ) ||
+ FT_STREAM_SKIP( 2 ) )
+ goto Exit;
+
+ if ( majorVersion != 1 )
+ {
+ FT_TRACE2(( "bad table version %d\n", majorVersion ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ if ( FT_READ_ULONG( store_offset ) ||
+ FT_READ_ULONG( widthMap_offset ) )
+ goto Exit;
+
+ if ( vertical )
+ {
+ if ( FT_NEW( blend->vvar_table ) )
+ goto Exit;
+ table = blend->vvar_table;
+ }
+ else
+ {
+ 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;
+ }
+
+ 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 = itemStore->varRegionList[regionIndex].axisList;
+
+
+ /* inner loop steps through axes in this region */
+ for ( j = 0; j < itemStore->axisCount; j++, axis++ )
+ {
+ FT_Fixed axisScalar;
+
+
+ /* compute the scalar contribution of this axis; */
+ /* ignore invalid ranges */
+ if ( axis->startCoord > axis->peakCoord ||
+ axis->peakCoord > axis->endCoord )
+ axisScalar = FT_FIXED_ONE;
+
+ else if ( axis->startCoord < 0 &&
+ axis->endCoord > 0 &&
+ axis->peakCoord != 0 )
+ axisScalar = FT_FIXED_ONE;
+
+ /* peak of 0 means ignore this axis */
+ else if ( axis->peakCoord == 0 )
+ axisScalar = FT_FIXED_ONE;
+
+ /* ignore this region if coords are out of range */
+ else if ( face->blend->normalizedcoords[j] < axis->startCoord ||
+ face->blend->normalizedcoords[j] > axis->endCoord )
+ axisScalar = 0;
+
+ /* calculate a proportional factor */
+ else
+ {
+ if ( face->blend->normalizedcoords[j] == axis->peakCoord )
+ axisScalar = FT_FIXED_ONE;
+ else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
+ axisScalar =
+ FT_DivFix( face->blend->normalizedcoords[j] - axis->startCoord,
+ axis->peakCoord - axis->startCoord );
+ else
+ axisScalar =
+ FT_DivFix( axis->endCoord - face->blend->normalizedcoords[j],
+ axis->endCoord - axis->peakCoord );
+ }
+
+ /* take product of all the axis scalars */
+ scalar = FT_MulFix( scalar, axisScalar );
+
+ } /* per-axis loop */
+
+ /* get the scaled delta for this region */
+ delta = FT_intToFixed( deltaSet[master] );
+ scaledDelta = FT_MulFix( scalar, delta );
+
+ /* accumulate the adjustments from each region */
+ netAdjustment = netAdjustment + scaledDelta;
+
+ } /* per-region loop */
+
+ 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,
+ delta,
+ delta == 1 ? "" : "s",
+ vertical ? "VVAR" : "HVAR" ));
+
+ *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;
@@ -434,10 +1448,10 @@
FT_TRACE2(( "GVAR " ));
- if ( ( error = face->goto_table( face,
- TTAG_gvar,
- stream,
- &table_len ) ) != 0 )
+ if ( FT_SET_ERROR( face->goto_table( face,
+ TTAG_gvar,
+ stream,
+ &table_len ) ) )
{
FT_TRACE2(( "is missing\n" ));
goto Exit;
@@ -447,10 +1461,6 @@
if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
goto Exit;
- blend->tuplecount = gvar_head.globalCoordCount;
- blend->gv_glyphcnt = gvar_head.glyphCount;
- offsetToData = gvar_start + gvar_head.offsetToData;
-
if ( gvar_head.version != 0x00010000L )
{
FT_TRACE1(( "bad table version\n" ));
@@ -458,8 +1468,6 @@
goto Exit;
}
- FT_TRACE2(( "loaded\n" ));
-
if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
{
FT_TRACE1(( "ft_var_load_gvar: number of axes in `gvar' and `cvar'\n"
@@ -468,8 +1476,36 @@
goto Exit;
}
- FT_TRACE5(( "gvar: there are %d shared coordinates:\n",
- blend->tuplecount ));
+ /* rough sanity check, ignoring offsets */
+ if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
+ table_len / 2 )
+ {
+ FT_TRACE1(( "ft_var_load_gvar:"
+ " invalid number of global coordinates\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ /* rough sanity check: offsets can be either 2 or 4 bytes */
+ if ( (FT_ULong)gvar_head.glyphCount *
+ ( ( gvar_head.flags & 1 ) ? 4 : 2 ) > table_len )
+ {
+ FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
+ }
+
+ FT_TRACE2(( "loaded\n" ));
+
+ blend->gvar_size = table_len;
+ blend->tuplecount = gvar_head.globalCoordCount;
+ blend->gv_glyphcnt = gvar_head.glyphCount;
+ offsetToData = gvar_start + gvar_head.offsetToData;
+
+ 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;
@@ -511,11 +1547,11 @@
for ( i = 0; i < blend->tuplecount; i++ )
{
FT_TRACE5(( " [ " ));
- for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; j++ )
+ for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
{
blend->tuplecoords[i * gvar_head.axisCount + j] =
FT_GET_SHORT() * 4; /* convert to FT_Fixed */
- FT_TRACE5(( "%.4f ",
+ FT_TRACE5(( "%.5f ",
blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
}
FT_TRACE5(( "]\n" ));
@@ -571,8 +1607,13 @@
for ( i = 0; i < blend->num_axis; i++ )
{
- FT_TRACE6(( " axis coordinate %d (%.4f):\n",
+ FT_TRACE6(( " axis coordinate %d (%.5f):\n",
i, blend->normalizedcoords[i] / 65536.0 ));
+ if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
+ FT_TRACE6(( " intermediate coordinates %d (%.5f, %.5f):\n",
+ i,
+ im_start_coords[i] / 65536.0,
+ im_end_coords[i] / 65536.0 ));
/* It's not clear why (for intermediate tuples) we don't need */
/* to check against start/end -- the documentation says we don't. */
@@ -585,68 +1626,254 @@
continue;
}
- else if ( blend->normalizedcoords[i] == 0 )
+ if ( blend->normalizedcoords[i] == 0 )
{
FT_TRACE6(( " axis coordinate is zero, stop\n" ));
apply = 0;
break;
}
- else if ( ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
- ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
+ if ( blend->normalizedcoords[i] == tuple_coords[i] )
{
- FT_TRACE6(( " tuple coordinate value %.4f is exceeded, stop\n",
+ FT_TRACE6(( " tuple coordinate value %.5f fits perfectly\n",
tuple_coords[i] / 65536.0 ));
- apply = 0;
- break;
+ /* `apply' does not change */
+ continue;
}
- else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
+ if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
{
- FT_TRACE6(( " tuple coordinate value %.4f fits\n",
- tuple_coords[i] / 65536.0 ));
/* not an intermediate tuple */
- apply = FT_MulFix( apply,
- blend->normalizedcoords[i] > 0
- ? blend->normalizedcoords[i]
- : -blend->normalizedcoords[i] );
- }
- else if ( blend->normalizedcoords[i] < im_start_coords[i] ||
- blend->normalizedcoords[i] > im_end_coords[i] )
+ if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
+ blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
+ {
+ FT_TRACE6(( " tuple coordinate value %.5f is exceeded, stop\n",
+ tuple_coords[i] / 65536.0 ));
+ apply = 0;
+ break;
+ }
+
+ FT_TRACE6(( " tuple coordinate value %.5f fits\n",
+ tuple_coords[i] / 65536.0 ));
+ apply = FT_MulDiv( apply,
+ blend->normalizedcoords[i],
+ tuple_coords[i] );
+ }
+ else
{
- FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] is exceeded,"
- " stop\n",
- im_start_coords[i] / 65536.0,
- im_end_coords[i] / 65536.0 ));
- apply = 0;
- break;
+ /* intermediate tuple */
+
+ if ( blend->normalizedcoords[i] < im_start_coords[i] ||
+ blend->normalizedcoords[i] > im_end_coords[i] )
+ {
+ FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] is exceeded,"
+ " stop\n",
+ im_start_coords[i] / 65536.0,
+ im_end_coords[i] / 65536.0 ));
+ apply = 0;
+ break;
+ }
+
+ else if ( blend->normalizedcoords[i] < tuple_coords[i] )
+ {
+ FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n",
+ im_start_coords[i] / 65536.0,
+ im_end_coords[i] / 65536.0 ));
+ apply = FT_MulDiv( apply,
+ blend->normalizedcoords[i] - im_start_coords[i],
+ tuple_coords[i] - im_start_coords[i] );
+ }
+
+ else
+ {
+ FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n",
+ im_start_coords[i] / 65536.0,
+ im_end_coords[i] / 65536.0 ));
+ apply = FT_MulDiv( apply,
+ im_end_coords[i] - blend->normalizedcoords[i],
+ im_end_coords[i] - tuple_coords[i] );
+ }
}
+ }
+
+ FT_TRACE6(( " apply factor is %.5f\n", apply / 65536.0 ));
- else if ( blend->normalizedcoords[i] < tuple_coords[i] )
+ return apply;
+ }
+
+
+ /* 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_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n",
- im_start_coords[i] / 65536.0,
- im_end_coords[i] / 65536.0 ));
- apply = FT_MulDiv( apply,
- blend->normalizedcoords[i] - im_start_coords[i],
- tuple_coords[i] - im_start_coords[i] );
+ 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++ )
{
- FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n",
- im_start_coords[i] / 65536.0,
- im_end_coords[i] / 65536.0 ));
- apply = FT_MulDiv( apply,
- im_end_coords[i] - blend->normalizedcoords[i],
- im_end_coords[i] - tuple_coords[i] );
+ 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;
+ }
+ }
}
}
+ }
- FT_TRACE6(( " apply factor is %.4f\n", apply / 65536.0 ));
- return apply;
+ /* 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;
+ }
}
@@ -663,7 +1890,6 @@
{
FT_Long version;
FT_UShort offsetToData;
- FT_UShort countSizePairs;
FT_UShort axisCount;
FT_UShort axisSize;
FT_UShort instanceCount;
@@ -691,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. */
@@ -708,18 +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 = 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[] =
{
@@ -728,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
};
@@ -758,21 +2000,28 @@
/* read the font data and set up the internal representation */
/* if not already done */
- if ( face->blend == NULL )
+ need_init = !face->blend;
+
+ if ( need_init )
{
FT_TRACE2(( "FVAR " ));
/* both `fvar' and `gvar' must be present */
- if ( ( error = face->goto_table( face, TTAG_gvar,
- stream, &table_len ) ) != 0 )
+ if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
+ stream, &table_len ) ) )
{
- FT_TRACE1(( "\n"
- "TT_Get_MM_Var: `gvar' table is missing\n" ));
- goto Exit;
+ /* CFF2 is an alternate to gvar here */
+ if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
+ stream, &table_len ) ) )
+ {
+ FT_TRACE1(( "\n"
+ "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
+ goto Exit;
+ }
}
- if ( ( error = face->goto_table( face, TTAG_fvar,
- stream, &table_len ) ) != 0 )
+ if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
+ stream, &table_len ) ) )
{
FT_TRACE1(( "is missing\n" ));
goto Exit;
@@ -780,45 +2029,67 @@
fvar_start = FT_STREAM_POS( );
+ /* the validity of the `fvar' header data was already checked */
+ /* in function `sfnt_init_face' */
if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
goto Exit;
- if ( fvar_head.version != (FT_Long)0x00010000L ||
-#if 0
- /* fonts like `JamRegular.ttf' have an incorrect value for */
- /* `countSizePairs'; since value 2 is hard-coded in `fvar' */
- /* version 1.0, we simply ignore it */
- fvar_head.countSizePairs != 2 ||
-#endif
- fvar_head.axisSize != 20 ||
- /* axisCount limit implied by 16-bit instanceSize */
- fvar_head.axisCount > 0x3FFE ||
- fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount ||
- /* instanceCount limit implied by limited range of name IDs */
- fvar_head.instanceCount > 0x7EFF ||
- fvar_head.offsetToData + fvar_head.axisCount * 20U +
- fvar_head.instanceCount * fvar_head.instanceSize > table_len )
- {
- FT_TRACE1(( "\n"
- "TT_Get_MM_Var: invalid `fvar' header\n" ));
- error = FT_THROW( Invalid_Table );
- goto Exit;
- }
+ usePsName = FT_BOOL( fvar_head.instanceSize ==
+ 6 + 4 * fvar_head.axisCount );
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;
@@ -828,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;
@@ -861,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;
@@ -880,36 +2160,194 @@
a->name[3] = (FT_String)( ( a->tag ) & 0xFF );
a->name[4] = '\0';
- FT_TRACE5(( " \"%s\": minimum=%.4f, default=%.4f, maximum=%.4f\n",
+ *axis_flags = axis_rec.flags;
+
+ if ( a->minimum > a->def ||
+ a->def > a->maximum )
+ {
+ a->minimum = a->def;
+ a->maximum = a->def;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ invalid = 1;
+#endif
+ }
+
+#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++ )
{
- if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
+ /* PostScript names add 2 bytes to the instance record size */
+ if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
+ 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 */
- if ( master != NULL )
+ if ( master )
{
FT_UInt n;
@@ -918,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;
@@ -959,40 +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 all_design_coords = FALSE;
+
FT_Memory memory = face->root.memory;
enum
@@ -1006,9 +2426,9 @@
face->doblend = FALSE;
- if ( face->blend == NULL )
+ if ( !face->blend )
{
- if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 )
+ if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
goto Exit;
}
@@ -1017,19 +2437,21 @@
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(( " %.4f\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 %.4f\n"
+ FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n"
" is out of range [-1;1]\n",
coords[i] / 65536.0 ));
error = FT_THROW( Invalid_Argument );
@@ -1039,11 +2461,20 @@
FT_TRACE5(( "\n" ));
- if ( blend->glyphoffsets == NULL )
- if ( ( error = ft_var_load_gvar( face ) ) != 0 )
+ if ( !face->is_cff2 && !blend->glyphoffsets )
+ if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
goto Exit;
- if ( blend->normalizedcoords == NULL )
+ 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 ) )
goto Exit;
@@ -1056,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++ )
@@ -1063,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 )
@@ -1087,9 +2548,15 @@
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 != NULL )
+ if ( face->cvt )
{
switch ( manageCvt )
{
@@ -1114,6 +2581,10 @@
}
}
+ /* enforce recomputation of the PostScript name; */
+ FT_FREE( face->postscript_name );
+ face->postscript_name = NULL;
+
Exit:
return error;
}
@@ -1122,6 +2593,128 @@
/*************************************************************************/
/* */
/* <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> */
+ /* Get the blend (normalized) coordinates for this instance of the */
+ /* font. */
+ /* */
+ /* <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, set the excess */
+ /* values to 0. */
+ /* */
+ /* coords :: An array of `num_coords', each between [-1,1]. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Get_MM_Blend( TT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error = FT_Err_Ok;
+ GX_Blend blend;
+ FT_UInt i, nc;
+
+
+ if ( !face->blend )
+ {
+ if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+ return error;
+ }
+
+ 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",
+ blend->num_axis, num_coords ));
+ nc = blend->num_axis;
+ }
+
+ if ( face->doblend )
+ {
+ for ( i = 0; i < nc; i++ )
+ coords[i] = blend->normalizedcoords[i];
+ }
+ else
+ {
+ for ( i = 0; i < nc; i++ )
+ coords[i] = 0;
+ }
+
+ for ( ; i < num_coords; i++ )
+ coords[i] = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
/* TT_Set_Var_Design */
/* */
/* <Description> */
@@ -1149,19 +2742,22 @@
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 == NULL )
+ if ( !face->blend )
{
- if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 )
+ if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
goto Exit;
}
@@ -1176,75 +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" ));
+ if ( !blend->coords )
+ {
+ if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
+ goto Exit;
+ }
- a = mmvar->axis;
- for ( i = 0; i < num_coords; i++, a++ )
+ c = blend->coords;
+ n = coords;
+ for ( i = 0; i < num_coords; i++, n++, c++ )
{
- FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 ));
- if ( coords[i] > a->maximum || coords[i] < a->minimum )
+ if ( *c != *n )
{
- FT_TRACE1(( "TT_Set_Var_Design: normalized design coordinate %.4f\n"
- " is out of range [%.4f;%.4f]\n",
- coords[i] / 65536.0,
- a->minimum / 65536.0,
- a->maximum / 65536.0 ));
- error = FT_THROW( Invalid_Argument );
- goto Exit;
+ *c = *n;
+ have_diff = 1;
}
-
- if ( coords[i] < a->def )
- normalized[i] = -FT_DivFix( coords[i] - a->def,
- a->minimum - a->def );
- else if ( a->maximum == a->def )
- normalized[i] = 0;
- else
- normalized[i] = FT_DivFix( coords[i] - a->def,
- a->maximum - a->def );
}
- 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 != NULL )
+ 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 )
{
- FT_TRACE5(( " %.4f\n", normalized[i] / 65536.0 ));
- if ( normalized[i] < av->correspondence[j].fromCoord )
- {
- 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 );
@@ -1253,6 +2855,165 @@
/*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Get_Var_Design */
+ /* */
+ /* <Description> */
+ /* Get the design coordinates of the currently selected interpolated */
+ /* font. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face. */
+ /* */
+ /* num_coords :: The number of design coordinates to retrieve. If it */
+ /* is larger than the number of axes, set the excess */
+ /* values to~0. */
+ /* */
+ /* <Output> */
+ /* coords :: The design coordinates array. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Get_Var_Design( TT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error = FT_Err_Ok;
+ GX_Blend blend;
+ FT_UInt i, nc;
+
+
+ if ( !face->blend )
+ {
+ if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+ return error;
+ }
+
+ 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",
+ blend->num_axis, num_coords ));
+ nc = blend->num_axis;
+ }
+
+ if ( face->doblend )
+ {
+ for ( i = 0; i < nc; i++ )
+ coords[i] = blend->coords[i];
+ }
+ else
+ {
+ for ( i = 0; i < nc; i++ )
+ coords[i] = 0;
+ }
+
+ for ( ; i < num_coords; i++ )
+ coords[i] = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <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_UInt num_instances;
+
+
+ if ( !face->blend )
+ {
+ if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+ goto Exit;
+ }
+
+ blend = face->blend;
+ mmvar = blend->mmvar;
+
+ 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 )
+ {
+ 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 );
+
+ face->root.face_index = ( instance_index << 16 ) |
+ ( face->root.face_index & 0xFFFFL );
+ face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GX VAR PARSING ROUTINES *****/
@@ -1298,14 +3059,16 @@
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;
FT_TRACE2(( "CVAR " ));
- if ( blend == NULL )
+ if ( !blend )
{
FT_TRACE2(( "\n"
"tt_face_vary_cvt: no blend specified\n" ));
@@ -1313,7 +3076,7 @@
goto Exit;
}
- if ( face->cvt == NULL )
+ if ( !face->cvt )
{
FT_TRACE2(( "\n"
"tt_face_vary_cvt: no `cvt ' table\n" ));
@@ -1353,13 +3116,39 @@
goto FExit;
tupleCount = FT_GET_USHORT();
- offsetToData = table_start + FT_GET_USHORT();
+ offsetToData = FT_GET_USHORT();
+
+ /* rough sanity test */
+ if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
+ table_len )
+ {
+ FT_TRACE2(( "tt_face_vary_cvt:"
+ " invalid CVT variation array header\n" ));
+
+ error = FT_THROW( Invalid_Table );
+ goto FExit;
+ }
- /* The documentation implies there are flags packed into the */
- /* tuplecount, but John Jenkins says that shared points don't apply */
- /* to `cvar', and no other flags are defined. */
+ offsetToData += table_start;
+
+ 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 ));
+ 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++ )
{
@@ -1373,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 */
+ FT_TRACE2(( "tt_face_vary_cvt:"
+ " invalid tuple index\n" ));
- if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
- for ( j = 0; j < 2 * blend->num_axis; j++ )
- (void)FT_GET_SHORT();
-
- 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 )
{
@@ -1407,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;
@@ -1421,11 +3206,27 @@
FT_Stream_SeekSet( stream, offsetToData );
- localpoints = ft_var_readpackedpoints( stream, &point_count );
- deltas = ft_var_readpackeddeltas( stream,
- point_count == 0 ? face->cvt_size
- : point_count );
- if ( localpoints == NULL || deltas == NULL )
+ 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 )
@@ -1473,10 +3274,15 @@
for ( j = 0; j < point_count; j++ )
{
- int pindex = localpoints[j];
- FT_Long orig_cvt = face->cvt[pindex];
+ int pindex;
+ FT_Long orig_cvt;
+ pindex = points[j];
+ if ( (FT_ULong)pindex >= face->cvt_size )
+ continue;
+
+ orig_cvt = face->cvt[pindex];
face->cvt[pindex] = (FT_Short)( orig_cvt +
FT_MulFix( deltas[j], apply ) );
@@ -1511,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 );
@@ -1599,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++ )
@@ -1644,12 +3439,11 @@
/* modeled after `Ins_IUP */
static void
- tt_handle_deltas( FT_Outline* outline,
- FT_Vector* in_points,
- FT_Bool* has_delta )
+ tt_interpolate_deltas( FT_Outline* outline,
+ FT_Vector* out_points,
+ FT_Vector* in_points,
+ FT_Bool* has_delta )
{
- FT_Vector* out_points;
-
FT_Int first_point;
FT_Int end_point;
@@ -1664,8 +3458,6 @@
if ( !outline->n_contours )
return;
- out_points = outline->points;
-
contour = 0;
point = 0;
@@ -1769,6 +3561,7 @@
GX_Blend blend = face->blend;
FT_Vector* points_org = NULL;
+ FT_Vector* points_out = NULL;
FT_Bool* has_delta = NULL;
FT_Error error;
@@ -1787,7 +3580,7 @@
FT_Short *deltas_x, *deltas_y;
- if ( !face->doblend || blend == NULL )
+ if ( !face->doblend || !blend )
return FT_THROW( Invalid_Argument );
if ( glyph_index >= blend->gv_glyphcnt ||
@@ -1800,6 +3593,7 @@
}
if ( FT_NEW_ARRAY( points_org, n_points ) ||
+ FT_NEW_ARRAY( points_out, n_points ) ||
FT_NEW_ARRAY( has_delta, n_points ) )
goto Fail1;
@@ -1811,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 ) ||
@@ -1819,7 +3612,20 @@
goto Fail2;
tupleCount = FT_GET_USHORT();
- offsetToData = glyph_start + FT_GET_USHORT();
+ offsetToData = FT_GET_USHORT();
+
+ /* rough sanity test */
+ if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
+ blend->gvar_size )
+ {
+ FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
+ " invalid glyph variation array header\n" ));
+
+ error = FT_THROW( Invalid_Table );
+ goto Fail2;
+ }
+
+ offsetToData += glyph_start;
if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
{
@@ -1827,13 +3633,21 @@
FT_Stream_SeekSet( stream, offsetToData );
- sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
+ sharedpoints = ft_var_readpackedpoints( stream,
+ blend->gvar_size,
+ &spoint_count );
offsetToData = FT_Stream_FTell( stream );
FT_Stream_SeekSet( stream, here );
}
- FT_TRACE5(( "gvar: there are %d tuples:\n", tupleCount ));
+ 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];
for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
{
@@ -1855,6 +3669,9 @@
}
else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
{
+ FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
+ " invalid tuple index\n" ));
+
error = FT_THROW( Invalid_Table );
goto Fail2;
}
@@ -1886,11 +3703,13 @@
here = FT_Stream_FTell( stream );
+ FT_Stream_SeekSet( stream, offsetToData );
+
if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
{
- FT_Stream_SeekSet( stream, offsetToData );
-
- localpoints = ft_var_readpackedpoints( stream, &point_count );
+ localpoints = ft_var_readpackedpoints( stream,
+ blend->gvar_size,
+ &point_count );
points = localpoints;
}
else
@@ -1900,13 +3719,15 @@
}
deltas_x = ft_var_readpackeddeltas( stream,
+ blend->gvar_size,
point_count == 0 ? n_points
: point_count );
deltas_y = ft_var_readpackeddeltas( stream,
+ blend->gvar_size,
point_count == 0 ? n_points
: point_count );
- if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
+ if ( !points || !deltas_y || !deltas_x )
; /* failure, ignore it */
else if ( points == ALL_POINTS )
@@ -1921,22 +3742,48 @@
/* this means that there are deltas for every point in the glyph */
for ( j = 0; j < n_points; j++ )
{
-#ifdef FT_DEBUG_LEVEL_TRACE
- FT_Vector point_org = outline->points[j];
-#endif
+ FT_Pos delta_x = FT_MulFix( deltas_x[j], apply );
+ FT_Pos delta_y = FT_MulFix( deltas_y[j], apply );
- outline->points[j].x += FT_MulFix( deltas_x[j], apply );
- outline->points[j].y += FT_MulFix( deltas_y[j], apply );
+ 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 ( ( point_org.x != outline->points[j].x ) ||
- ( point_org.y != outline->points[j].y ) )
+ if ( delta_x || delta_y )
{
FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
j,
- point_org.x,
- point_org.y,
+ outline->points[j].x - delta_x,
+ outline->points[j].y - delta_y,
outline->points[j].x,
outline->points[j].y ));
count++;
@@ -1961,13 +3808,13 @@
/* IUP bytecode instruction */
for ( j = 0; j < n_points; j++ )
{
- points_org[j] = outline->points[j];
has_delta[j] = FALSE;
+ points_out[j] = points_org[j];
}
for ( j = 0; j < point_count; j++ )
{
- FT_UShort idx = localpoints[j];
+ FT_UShort idx = points[j];
if ( idx >= n_points )
@@ -1975,34 +3822,71 @@
has_delta[idx] = TRUE;
- outline->points[idx].x += FT_MulFix( deltas_x[j], apply );
- outline->points[idx].y += FT_MulFix( deltas_y[j], apply );
+ points_out[idx].x += FT_MulFix( deltas_x[j], apply );
+ points_out[idx].y += FT_MulFix( deltas_y[j], apply );
}
/* no need to handle phantom points here, */
/* since solitary points can't be interpolated */
- tt_handle_deltas( outline,
- points_org,
- has_delta );
+ tt_interpolate_deltas( outline,
+ points_out,
+ points_org,
+ has_delta );
-#ifdef FT_DEBUG_LEVEL_TRACE
FT_TRACE7(( " point deltas:\n" ));
- for ( j = 0; j < n_points; j++)
+ for ( j = 0; j < n_points; j++ )
{
- if ( ( points_org[j].x != outline->points[j].x ) ||
- ( points_org[j].y != outline->points[j].y ) )
+ FT_Pos delta_x = points_out[j].x - points_org[j].x;
+ FT_Pos delta_y = points_out[j].y - points_org[j].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 )
{
FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
j,
- points_org[j].x,
- points_org[j].y,
+ outline->points[j].x - delta_x,
+ outline->points[j].y - delta_y,
outline->points[j].x,
outline->points[j].y ));
count++;
}
+#endif
}
+#ifdef FT_DEBUG_LEVEL_TRACE
if ( !count )
FT_TRACE7(( " none\n" ));
#endif
@@ -2021,6 +3905,8 @@
FT_TRACE5(( "\n" ));
Fail2:
+ if ( sharedpoints != ALL_POINTS )
+ FT_FREE( sharedpoints );
FT_FREE( tuple_coords );
FT_FREE( im_start_coords );
FT_FREE( im_end_coords );
@@ -2029,6 +3915,7 @@
Fail1:
FT_FREE( points_org );
+ FT_FREE( points_out );
FT_FREE( has_delta );
return error;
@@ -2038,37 +3925,150 @@
/*************************************************************************/
/* */
/* <Function> */
+ /* tt_get_var_blend */
+ /* */
+ /* <Description> */
+ /* An extended internal version of `TT_Get_MM_Blend' that returns */
+ /* pointers instead of copying data, without any initialization of */
+ /* the MM machinery in case it isn't loaded yet. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ 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;
+ if ( coords )
+ *coords = face->blend->coords;
+ if ( normalizedcoords )
+ *normalizedcoords = face->blend->normalizedcoords;
+ if ( mm_var )
+ *mm_var = face->blend->mmvar;
+ }
+ else
+ {
+ if ( num_coords )
+ *num_coords = 0;
+ if ( coords )
+ *coords = NULL;
+ if ( mm_var )
+ *mm_var = NULL;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ 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> */
/* tt_done_blend */
/* */
/* <Description> */
/* Free the blend internal data structure. */
/* */
FT_LOCAL_DEF( void )
- tt_done_blend( FT_Memory memory,
- GX_Blend blend )
+ tt_done_blend( TT_Face face )
{
- if ( blend != NULL )
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ GX_Blend blend = face->blend;
+
+
+ if ( blend )
{
- FT_UInt i;
+ FT_UInt i, num_axes;
+ /* 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 != NULL )
+ if ( blend->avar_segment )
{
- for ( i = 0; i < blend->num_axis; i++ )
+ for ( i = 0; i < num_axes; i++ )
FT_FREE( blend->avar_segment[i].correspondence );
FT_FREE( blend->avar_segment );
}
+ if ( blend->hvar_table )
+ {
+ 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 */