diff options
Diffstat (limited to 'src/truetype/ttgxvar.c')
-rw-r--r-- | src/truetype/ttgxvar.c | 537 |
1 files changed, 524 insertions, 13 deletions
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c index 080e2ddce..504022ad7 100644 --- a/src/truetype/ttgxvar.c +++ b/src/truetype/ttgxvar.c @@ -392,6 +392,418 @@ FT_FRAME_EXIT(); } + /*************************************************************************/ + /* */ + /* <Function> */ + /* ft_var_load_hvar */ + /* */ + /* <Description> */ + /* Parse the `HVAR' table if present. It need not be, so we return */ + /* nothing. */ + /* On success, blend->hvar_checked is TRUE */ + /* Some memory may remain allocated on error (hvar_checked FALSE) */ + /* Memory is always freed in tt_done_blend. */ + /* */ + /* <InOut> */ + /* face :: The font face. */ + /* */ + /* */ + + /* some macros we need */ + #define FT_fdot14ToFixed( x ) \ + (((FT_Fixed)((FT_Int16)(x))) << 2 ) + + #define FT_FIXED_ONE ((FT_Fixed)0x10000) + #define FT_intToFixed( i ) \ + ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) ) + #define FT_fixedToInt( x ) \ + ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) + + static void + ft_var_load_hvar( TT_Face face ) + { + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; + GX_Blend blend = face->blend; + FT_Error error; + FT_UShort majorVersion; + FT_UShort minorVersion; + FT_ULong table_len; + FT_ULong table_offset; + FT_ULong store_offset; + FT_ULong map_offset; + + FT_TRACE2(( "HVAR " )); + + /* if we allocated the table, assume we've already tried to parse it */ + if ( face->blend->hvar_table ) + return; + + error = face->goto_table( face, TTAG_HVAR, stream, &table_len ); + if ( error ) + { + FT_TRACE2(( "is missing\n" )); + return; + } + + table_offset = FT_STREAM_POS(); + + if ( FT_READ_USHORT( majorVersion ) || + FT_READ_USHORT( minorVersion ) ) + goto Exit; + if ( majorVersion != 1 ) + { + FT_TRACE2(( "bad table version %d\n", majorVersion )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + if ( FT_READ_ULONG( store_offset ) || + FT_READ_ULONG( map_offset ) ) + goto Exit; + + /* parse item variation store */ + { + FT_UShort format; + FT_ULong data_offset_array_offset; + FT_ULong data_offset; + FT_ULong region_offset; + GX_HVStore itemStore; + FT_UInt i, j, k; + FT_UInt shortDeltaCount; + 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_File_Format ); + goto Exit; + } + + if ( FT_NEW( blend->hvar_table ) ) /* allocate table at top level */ + goto Exit; + + hvarTable = blend->hvar_table; + itemStore = &hvarTable->itemStore; + + if ( FT_READ_ULONG( region_offset ) || + FT_READ_USHORT( itemStore->dataCount ) ) + goto Exit; + + /* save position of item variation data offsets */ + /* we'll parse region list first, then come back */ + data_offset_array_offset = FT_STREAM_POS(); + + /* 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 ) ) + 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 */ + + /* parse array of item variation data subtables */ + if ( FT_STREAM_SEEK( data_offset_array_offset ) ) + goto Exit; + + if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) ) + goto Exit; + + hvarData = itemStore->varData; + for ( i=0; i<itemStore->dataCount; i++ ) + { + if ( FT_READ_ULONG( data_offset ) ) + goto Exit; + + if ( FT_STREAM_SEEK( table_offset + store_offset + data_offset ) || + FT_READ_USHORT( hvarData->itemCount ) || + FT_READ_USHORT( shortDeltaCount ) || + FT_READ_USHORT( hvarData->regionCount ) ) + goto Exit; + + /* check some data consistency */ + if ( shortDeltaCount > hvarData->regionCount ) + { + FT_TRACE2(( "bad short count %d or region count %d\n", + shortDeltaCount, hvarData->regionCount )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + if ( hvarData->regionCount > itemStore->regionCount ) + { + FT_TRACE2(( "inconsistent regionCount %d in varData[ %d ]\n", + hvarData->regionCount, i )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* parse region indices */ + if ( FT_NEW_ARRAY( hvarData->regionIndices, hvarData->regionCount ) ) + goto Exit; + + for ( j=0; j<hvarData->regionCount; j++ ) + { + if ( FT_READ_USHORT( hvarData->regionIndices[j] ) ) + goto Exit; + if ( hvarData->regionIndices[j] >= hvarData->regionCount ) + { + FT_TRACE2(( "bad region index %d\n", hvarData->regionIndices[j] )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + } + + /* parse delta set */ + /* on input, deltas are ( shortDeltaCount + regionCount ) bytes each */ + /* on output, deltas are expanded to regionCount shorts each */ + if ( FT_NEW_ARRAY( hvarData->deltaSet, hvarData->regionCount * 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->regionCount; ) + { + for ( k=0; k<shortDeltaCount; k++,j++ ) + { + /* read the short deltas */ + FT_Short delta; + if ( FT_READ_SHORT( delta ) ) + goto Exit; + hvarData->deltaSet[j] = delta; + } + for ( ; k<hvarData->regionCount; k++,j++ ) + { + /* read the (signed) byte deltas */ + FT_Char delta; + if ( FT_READ_CHAR( delta ) ) + goto Exit; + hvarData->deltaSet[j] = delta; + } + } + } + } + /* end parse item variation store */ + + { + /* parse width map */ + GX_WidthMap widthMap; + FT_UShort format; + FT_UInt entrySize; + FT_UInt innerBitCount; + FT_UInt innerIndexMask; + FT_UInt outerBitCount; + FT_UInt i, j; + + widthMap = &blend->hvar_table->widthMap; + if ( FT_READ_USHORT( format ) || + FT_READ_USHORT( widthMap->mapCount ) ) + goto Exit; + + if ( format & 0xFFC0 ) + { + FT_TRACE2(( "bad map format %d\n", format )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + entrySize = ( ( format & 0x0030 ) >> 4 ) + 1; /* bytes per entry, 1,2,3 or 4 */ + innerBitCount = ( format & 0x000F ) + 1; + innerIndexMask = ( 1 << innerBitCount ) - 1; + outerBitCount = 8 * entrySize - innerBitCount; + + if ( FT_NEW_ARRAY( widthMap->innerIndex, widthMap->mapCount ) ) + goto Exit; + + if ( FT_NEW_ARRAY( widthMap->outerIndex, widthMap->mapCount ) ) + goto Exit; + + for ( i=0; i<widthMap->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 >= blend->hvar_table->itemStore.dataCount ) + { + FT_TRACE2(( "outerIndex[ %d ] == %d out of range\n", + i, outerIndex )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + widthMap->outerIndex[i] = outerIndex; + + innerIndex = mapData & innerIndexMask; + + if ( innerIndex >= blend->hvar_table->itemStore.varData[ outerIndex ].itemCount ) + { + FT_TRACE2(( "innerIndex[ %d ] == %d out of range\n", + i, innerIndex )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + widthMap->innerIndex[i] = innerIndex; + } + + } /* end parse width map */ + + FT_TRACE2(( "loaded\n" )); + error = FT_Err_Ok; + + Exit: + if ( error == FT_Err_Ok ) + blend->hvar_checked = TRUE; + } + + /*************************************************************************/ + /* */ + /* <Function> */ + /* tt_adjust_advance */ + /* */ + /* <Description> */ + /* Apply HVAR adjustment to advance width of gindex */ + /* */ + /* <In> */ + /* gindex :: The glyph */ + /* <InOut> */ + /* face :: The font face. */ + /* aadvance :: points to width value */ + /* */ + FT_EXPORT( void ) + tt_adjust_advance( TT_Face face, + FT_UInt gindex, + FT_UShort *aadvance ) + { + FT_UInt innerIndex, outerIndex; + GX_HVarData varData; + FT_UInt master, j; + FT_Fixed netAdjustment = 0; /* accumulated adjustment */ + FT_Fixed scaledDelta; + FT_Short* deltaSet; + FT_Fixed delta; + + if ( !face->blend ) + return; + + if ( !face->blend->hvar_checked ) + { + /* initialize hvar table */ + ft_var_load_hvar( face ); + } + if ( !face->blend->hvar_checked ) + return; /* HVAR parse failed */ + + if ( gindex >= face->blend->hvar_table->widthMap.mapCount ) + { + FT_TRACE2(( "gindex %d out of range\n", gindex )); + 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[ face->blend->hvar_table->itemStore.regionCount * innerIndex ]; + + /* see pseudo code from Font Variations Overview */ + /* outer loop steps through master designs to be blended */ + for ( master=0; master<varData->regionCount; master++ ) + { + FT_UInt regionIndex = varData->regionIndices[ master ]; + GX_AxisCoords axis = face->blend->hvar_table->itemStore.varRegionList[ regionIndex ].axisList; + FT_Fixed scalar = FT_FIXED_ONE; + + /* inner loop steps through axes in this region */ + for ( j=0; j<face->blend->hvar_table->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 */ + FT_TRACE4(( ", %f ", (double)scalar / 65536 )); + + /* 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 */ + FT_TRACE4(( "]\n" )); + + /* apply the accumulated adjustment to the default to derive the interpolated value */ + /* TODO check rounding: *aadvance is short */ + *aadvance += FT_fixedToInt( netAdjustment ); + +Exit: + return; + } + typedef struct GX_GVar_Head_ { @@ -762,7 +1174,7 @@ /* <Return> */ /* FreeType error code. 0 means success. */ /* */ - FT_LOCAL_DEF( FT_Error ) + FT_EXPORT( FT_Error ) TT_Get_MM_Var( TT_Face face, FT_MM_Var* *master ) { @@ -778,6 +1190,7 @@ FT_Var_Axis* a; FT_Var_Named_Style* ns; GX_FVar_Head fvar_head; + FT_Bool usePsName; static const FT_Frame_Field fvar_fields[] = { @@ -824,9 +1237,14 @@ if ( ( error = face->goto_table( face, TTAG_gvar, stream, &table_len ) ) != 0 ) { - FT_TRACE1(( "\n" - "TT_Get_MM_Var: `gvar' table is missing\n" )); - goto Exit; + /* CFF2 is an alternate to gvar here */ + if ( ( error = face->goto_table( face, TTAG_CFF2, + stream, &table_len ) ) != 0 ) + { + FT_TRACE1(( "\n" + "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" )); + goto Exit; + } } if ( ( error = face->goto_table( face, TTAG_fvar, @@ -851,8 +1269,6 @@ 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 ) @@ -862,7 +1278,21 @@ error = FT_THROW( Invalid_Table ); goto Exit; } - + if ( fvar_head.instanceSize == 4 + 4 * fvar_head.axisCount ) + { + usePsName = FALSE; + } + else if ( fvar_head.instanceSize == 6 + 4 * fvar_head.axisCount ) + { + usePsName = TRUE; + } + else + { + FT_TRACE1(( "\n" + "TT_Get_MM_Var: invalid `fvar' header\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } FT_TRACE2(( "loaded\n" )); FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount )); @@ -952,8 +1382,17 @@ ns = mmvar->namedstyle; for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) { - if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) ) - goto Exit; + /* PostScript names add 2 bytes to the instance record size */ + if ( usePsName ) + { + if ( FT_FRAME_ENTER( 6L + 4L * fvar_head.axisCount ) ) + goto Exit; + } + else + { + if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) ) + goto Exit; + } ns->strid = FT_GET_USHORT(); (void) /* flags = */ FT_GET_USHORT(); @@ -961,6 +1400,9 @@ for ( j = 0; j < fvar_head.axisCount; j++ ) ns->coords[j] = FT_GET_LONG(); + if ( usePsName ) + ns->psid = FT_GET_USHORT(); + FT_FRAME_EXIT(); } } @@ -1042,7 +1484,7 @@ /* <Return> */ /* FreeType error code. 0 means success. */ /* */ - FT_LOCAL_DEF( FT_Error ) + FT_EXPORT( FT_Error ) TT_Set_MM_Blend( TT_Face face, FT_UInt num_coords, FT_Fixed* coords ) @@ -1097,7 +1539,7 @@ FT_TRACE5(( "\n" )); - if ( blend->glyphoffsets == NULL ) + if ( !face->isCFF2 && blend->glyphoffsets == NULL ) if ( ( error = ft_var_load_gvar( face ) ) != 0 ) goto Exit; @@ -1202,7 +1644,7 @@ /* <Return> */ /* FreeType error code. 0 means success. */ /* */ - FT_LOCAL_DEF( FT_Error ) + FT_EXPORT( FT_Error ) TT_Set_Var_Design( TT_Face face, FT_UInt num_coords, FT_Fixed* coords ) @@ -1310,6 +1752,50 @@ } + FT_EXPORT( FT_Error ) + TT_Get_Var_Blend( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error = FT_Err_Ok; + GX_Blend blend; + FT_UInt i; + + face->doblend = FALSE; + + if ( face->blend == NULL ) + { + if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) + return error; + } + + blend = face->blend; + + 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 )); + num_coords = blend->num_axis; + } + + for (i = 0; i < num_coords; ++i) + { + coords[i] = blend->normalizedcoords[i]; + } + + return FT_Err_Ok; + } + + FT_EXPORT( FT_Error ) + TT_Get_Var_Design( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + /* TODO: Implement this function. */ + return FT_THROW( Unimplemented_Feature ); + } + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -2153,7 +2639,7 @@ /* <Description> */ /* Free the blend internal data structure. */ /* */ - FT_LOCAL_DEF( void ) + FT_EXPORT( void ) tt_done_blend( FT_Memory memory, GX_Blend blend ) { @@ -2172,6 +2658,31 @@ FT_FREE( blend->avar_segment ); } + if ( blend->hvar_table != NULL ) + { + FT_UInt i; + 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_FREE( blend->hvar_table->widthMap.innerIndex ); + FT_FREE( blend->hvar_table->widthMap.outerIndex ); + FT_FREE( blend->hvar_table ); + } + FT_FREE( blend->tuplecoords ); FT_FREE( blend->glyphoffsets ); FT_FREE( blend ); |