From ae4eb996ab1ed43a52fac19461de3bdcaf1a1742 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 23 Jul 2022 13:49:27 -0600 Subject: [PATCH] [truetype] Add support for `avar` table 2.0 format. See https://github.com/harfbuzz/boring-expansion-spec/blob/main/avar2.md for the specification. Currently, this is implemented only in most recent OS versions on Apple platforms and in the HarfBuzz library, but it is expected to be added to the OpenType standard soon. * src/truetype/ttgxvar.h (GX_AVarTableRec): New structure. (GX_BlendRec): Use it to replace `avar_segment` with `avar_table`. * src/truetype/ttgxvar.c (ft_var_load_avar): Load new table version. (ft_var_to_normalized, tt_done_blend): Extend for new format. (ft_var_load_hvvar, ft_var_to_design): Updated. --- src/truetype/ttgxvar.c | 199 ++++++++++++++++++++++++++++++++--------- src/truetype/ttgxvar.h | 21 ++++- 2 files changed, 178 insertions(+), 42 deletions(-) diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c index 14d272f58..663fb2e48 100644 --- a/src/truetype/ttgxvar.c +++ b/src/truetype/ttgxvar.c @@ -353,15 +353,21 @@ static void ft_var_load_avar( TT_Face face ) { - FT_Stream stream = FT_FACE_STREAM( face ); - FT_Memory memory = stream->memory; + FT_Error error; + FT_Stream stream = FT_FACE_STREAM( face ); + FT_Memory memory = stream->memory; + FT_Int i, j; + GX_Blend blend = face->blend; GX_AVarSegment segment; - FT_Error error; - FT_Long version; - FT_Long axisCount; - FT_Int i, j; - FT_ULong table_len; + GX_AVarTable table; + + FT_Long version; + FT_Long axisCount; + FT_ULong table_offset; + FT_ULong table_len; + FT_ULong store_offset; + FT_ULong axisMap_offset; FT_TRACE2(( "AVAR " )); @@ -374,13 +380,15 @@ return; } + table_offset = FT_STREAM_POS(); + if ( FT_FRAME_ENTER( table_len ) ) return; version = FT_GET_LONG(); axisCount = FT_GET_LONG(); - if ( version != 0x00010000L ) + if ( version != 0x00010000L && version != 0x00020000L ) { FT_TRACE2(( "bad table version\n" )); goto Exit; @@ -396,10 +404,14 @@ goto Exit; } - if ( FT_QNEW_ARRAY( blend->avar_segment, axisCount ) ) + if ( FT_NEW( blend->avar_table ) ) + goto Exit; + table = blend->avar_table; + + if ( FT_QNEW_ARRAY( table->avar_segment, axisCount ) ) goto Exit; - segment = &blend->avar_segment[0]; + segment = &table->avar_segment[0]; for ( i = 0; i < axisCount; i++, segment++ ) { FT_TRACE5(( " axis %d:\n", i )); @@ -412,9 +424,9 @@ /* it right now since loading the `avar' table is optional. */ for ( j = i - 1; j >= 0; j-- ) - FT_FREE( blend->avar_segment[j].correspondence ); + FT_FREE( table->avar_segment[j].correspondence ); - FT_FREE( blend->avar_segment ); + FT_FREE( table->avar_segment ); goto Exit; } @@ -433,6 +445,35 @@ FT_TRACE5(( "\n" )); } + if ( version < 0x00020000L ) + goto Exit; + + axisMap_offset = FT_GET_ULONG(); + store_offset = FT_GET_ULONG(); + + if ( store_offset ) + { + error = tt_var_load_item_variation_store( + face, + table_offset + store_offset, + &table->itemStore ); + if ( error ) + goto Exit; + } + + if ( axisMap_offset ) + { + error = tt_var_load_delta_set_index_mapping( + face, + table_offset + axisMap_offset, + &table->axisMap, + &table->itemStore, + table_len ); + if ( error ) + goto Exit; + } + + Exit: FT_FRAME_EXIT(); } @@ -888,12 +929,15 @@ table = blend->hvar_table; } - error = tt_var_load_item_variation_store( - face, - table_offset + store_offset, - &table->itemStore ); - if ( error ) - goto Exit; + if ( store_offset ) + { + error = tt_var_load_item_variation_store( + face, + table_offset + store_offset, + &table->itemStore ); + if ( error ) + goto Exit; + } if ( widthMap_offset ) { @@ -1924,12 +1968,18 @@ FT_Fixed* coords, FT_Fixed* normalized ) { + FT_Error error = FT_Err_Ok; + FT_Memory memory = face->root.memory; + FT_UInt i, j; + GX_Blend blend; FT_MM_Var* mmvar; - FT_UInt i, j; FT_Var_Axis* a; GX_AVarSegment av; + FT_Fixed* new_normalized; + FT_Fixed* old_normalized; + blend = face->blend; mmvar = blend->mmvar; @@ -1980,31 +2030,92 @@ for ( ; i < mmvar->num_axis; i++ ) normalized[i] = 0; - if ( blend->avar_segment ) + if ( blend->avar_table ) { + GX_AVarTable table = blend->avar_table; + + FT_TRACE5(( "normalized design coordinates" " before applying `avar' data:\n" )); - av = blend->avar_segment; - for ( i = 0; i < mmvar->num_axis; i++, av++ ) + if ( table->avar_segment ) { - for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) - { - if ( normalized[i] < av->correspondence[j].fromCoord ) - { - FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 )); + av = table->avar_segment; - 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; + for ( i = 0; i < mmvar->num_axis; i++, av++ ) + { + for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) + { + if ( normalized[i] < av->correspondence[j].fromCoord ) + { + FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 )); + + normalized[i] = + FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, + av->correspondence[j].toCoord - + av->correspondence[j - 1].toCoord, + av->correspondence[j].fromCoord - + av->correspondence[j - 1].fromCoord ) + + av->correspondence[j - 1].toCoord; + break; + } } } } + + if ( table->itemStore.varData ) + { + if ( FT_QNEW_ARRAY( new_normalized, mmvar->num_axis ) ) + return; + + /* Install our half-normalized coordinates for the next */ + /* Item Variation Store to work with. */ + old_normalized = face->blend->normalizedcoords; + face->blend->normalizedcoords = normalized; + + for ( i = 0; i < mmvar->num_axis; i++ ) + { + FT_Fixed v = normalized[i]; + FT_UInt innerIndex = i; + FT_UInt outerIndex = 0; + FT_Int delta; + + + if ( table->axisMap.innerIndex ) + { + FT_UInt idx = i; + + + if ( idx >= table->axisMap.mapCount ) + idx = table->axisMap.mapCount - 1; + + outerIndex = table->axisMap.outerIndex[idx]; + innerIndex = table->axisMap.innerIndex[idx]; + } + + delta = tt_var_get_item_delta( face, + &table->itemStore, + outerIndex, + innerIndex ); + + v += delta << 2; + + /* Clamp value range. */ + v = v >= 0x10000L ? 0x10000 : v; + v = v <= -0x10000L ? -0x10000 : v; + + new_normalized[i] = v; + } + + for ( i = 0; i < mmvar->num_axis; i++ ) + { + normalized[i] = new_normalized[i]; + } + + face->blend->normalizedcoords = old_normalized; + + FT_FREE( new_normalized ); + } } } @@ -2041,9 +2152,9 @@ for ( ; i < num_coords; i++ ) design[i] = 0; - if ( blend->avar_segment ) + if ( blend->avar_table && blend->avar_table->avar_segment ) { - GX_AVarSegment av = blend->avar_segment; + GX_AVarSegment av = blend->avar_table->avar_segment; FT_TRACE5(( "design coordinates" @@ -4390,11 +4501,19 @@ FT_FREE( blend->normalized_stylecoords ); FT_FREE( blend->mmvar ); - if ( blend->avar_segment ) + if ( blend->avar_table ) { for ( i = 0; i < num_axes; i++ ) - FT_FREE( blend->avar_segment[i].correspondence ); - FT_FREE( blend->avar_segment ); + FT_FREE( blend->avar_table->avar_segment[i].correspondence ); + FT_FREE( blend->avar_table->avar_segment ); + + tt_var_done_item_variation_store( face, + &blend->avar_table->itemStore ); + + tt_var_done_delta_set_index_map( face, + &blend->avar_table->axisMap ); + + FT_FREE( blend->avar_table ); } if ( blend->hvar_table ) diff --git a/src/truetype/ttgxvar.h b/src/truetype/ttgxvar.h index 513c40b8a..3a7829d26 100644 --- a/src/truetype/ttgxvar.h +++ b/src/truetype/ttgxvar.h @@ -62,6 +62,23 @@ FT_BEGIN_HEADER } GX_AVarSegmentRec, *GX_AVarSegment; + /************************************************************************** + * + * @Struct: + * GX_AVarTableRec + * + * @Description: + * Data from the `avar' table. + */ + typedef struct GX_AVarTableRec_ + { + GX_AVarSegment avar_segment; /* avar_segment[num_axis] */ + GX_ItemVarStoreRec itemStore; /* Item Variation Store */ + GX_DeltaSetIdxMapRec axisMap; /* Axis Mapping */ + + } GX_AVarTableRec, *GX_AVarTable; + + /************************************************************************** * * @Struct: @@ -194,7 +211,7 @@ FT_BEGIN_HEADER * A Boolean; if set, FreeType tried to load (and parse) the `avar' * table. * - * avar_segment :: + * avar_table :: * Data from the `avar' table. * * hvar_loaded :: @@ -259,7 +276,7 @@ FT_BEGIN_HEADER /* normalized_stylecoords[num_namedstyles][num_axis] */ FT_Bool avar_loaded; - GX_AVarSegment avar_segment; /* avar_segment[num_axis] */ + GX_AVarTable avar_table; FT_Bool hvar_loaded; FT_Bool hvar_checked;