1
0
mirror of https://github.com/freetype/freetype.git synced 2026-10-23 11:08:54 +03:00

[varc] Support static component axes.

The VARC draft permits static fonts without a 'fvar' table to retain 'gvar'
or 'CFF2' variation data for component-internal coordinates.  Initialize a
private blend from the relevant variation store without exposing those axes
through the public MM API.

Also add TrueType and CFF2 regression fonts.

* src/truetype/ttvarc.c (tt_face_init_varc_axes): New function.
  (tt_face_load_varc_glyph): Handle static VARC fonts.
* src/tyuetype/ttvarc.h: Updated.

* src/cff/cffdrivr.c (cff_varc_init_axes): New function.
  (cff_varc_load_glyph): Use it.
  (cff_service_varc): Updated.

* src/truetype/ttgxvar.c (ft_var_load_gvar, TT_Get_MM_Var): Handle static
  VARC fonts.
  (tt_var_init_gvar, tt_var_init_blend): New functions.
  (tt_done_blend): Updated.
* src/truetype/ttgxvar.h: Updated.
* src/truetype/ttdriver.c (tt_service_varc): Updated.

* include/freetype/internal/services/svvarc.h (FT_VARC_Init_Axes_Func): New
  typedef.
  (FT_ServiceVARC): Updated.

* tests/data/varc-static-gvar.ttf, tests/data/varc-static-cff2.otf: New test
  fonts.
* tests/varc/main.c: Updated.
This commit is contained in:
Behdad Esfahbod
2026-09-14 22:08:01 -06:00
committed by Werner Lemberg
parent c7b5673f3d
commit a69e39ad9e
10 changed files with 227 additions and 31 deletions

View File

@@ -29,6 +29,10 @@ FT_BEGIN_HEADER
#define FT_SERVICE_ID_VARC "varc"
typedef FT_Error
(*FT_VARC_Init_Axes_Func)( FT_Face face,
FT_UInt axis_count );
typedef FT_Error
(*FT_VARC_Load_Func)( FT_Face face,
FT_Stream stream );
@@ -49,6 +53,7 @@ FT_BEGIN_HEADER
FT_DEFINE_SERVICE( VARC )
{
FT_VARC_Init_Axes_Func init_axes;
FT_VARC_Load_Func load;
FT_VARC_Done_Func done;
FT_VARC_Has_Glyph_Func has_glyph;
@@ -57,12 +62,14 @@ FT_BEGIN_HEADER
#define FT_DEFINE_SERVICE_VARCREC( class_, \
init_axes_, \
load_, \
done_, \
has_glyph_, \
load_glyph_ ) \
static const FT_Service_VARCRec class_ = \
{ \
init_axes_, \
load_, \
done_, \
has_glyph_, \

View File

@@ -1099,6 +1099,20 @@
* that it forwards variation-font operations.
*/
FT_CALLBACK_DEF( FT_Error )
cff_varc_init_axes( FT_Face face,
FT_UInt axis_count )
{
CFF_Face cffface = (CFF_Face)face;
FT_Service_VARC varc = (FT_Service_VARC)cffface->tt_varc;
return varc && varc->init_axes
? varc->init_axes( face, axis_count )
: FT_THROW( Missing_Module );
}
FT_CALLBACK_DEF( FT_Error )
cff_varc_load( FT_Face face,
FT_Stream stream )
@@ -1144,17 +1158,33 @@
{
CFF_Face cffface = (CFF_Face)face;
FT_Service_VARC varc = (FT_Service_VARC)cffface->tt_varc;
CFF_Font cff = (CFF_Font)cffface->extra.data;
FT_Error error;
return varc ? varc->load_glyph( face, glyph_slot,
glyph_index, load_flags )
: FT_THROW( Missing_Module );
if ( !varc )
return FT_THROW( Missing_Module );
if ( !FT_HAS_MULTIPLE_MASTERS( face ) &&
cff &&
cff->cff2 &&
cff->vstore.axisCount )
{
error = varc->init_axes( face, cff->vstore.axisCount );
if ( error )
return error;
}
return varc->load_glyph( face, glyph_slot,
glyph_index, load_flags );
}
FT_DEFINE_SERVICE_VARCREC(
cff_service_varc,
cff_varc_init_axes,
cff_varc_load,
cff_varc_done,
cff_varc_has_glyph,

View File

@@ -608,6 +608,7 @@
FT_DEFINE_SERVICE_VARCREC(
tt_service_varc,
tt_face_init_varc_axes,
tt_face_load_varc,
tt_face_free_varc,
tt_face_has_varc_glyph,

View File

@@ -1871,16 +1871,22 @@
static FT_Error
ft_var_load_gvar( TT_Face face )
{
FT_Stream stream = FT_FACE_STREAM( face );
FT_Memory memory = stream->memory;
GX_Blend blend = face->blend;
FT_Error error;
FT_UInt i, j;
FT_Byte* bytes;
FT_ULong table_len;
FT_ULong gvar_start;
FT_ULong offsetToData;
FT_ULong offsets_len;
FT_Error error;
FT_Stream stream = FT_FACE_STREAM( face );
FT_Memory memory = stream->memory;
GX_Blend blend = face->blend;
FT_Bool blend_created = FALSE;
FT_UInt i, j;
FT_Byte* bytes;
FT_ULong table_len;
FT_ULong gvar_start;
FT_ULong offsetToData;
FT_ULong offsets_len;
GX_GVar_Head gvar_head;
static const FT_Frame_Field gvar_fields[] =
@@ -1923,7 +1929,23 @@
goto Exit;
}
if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
/*
* VARC can use normalized coordinates for component-internal axes in a
* static font, where 'gvar' is present without 'fvar'. Set up the
* private part of the blend machinery in that case; it deliberately has
* no `mmvar`, so the axes remain invisible through the public MM API.
*/
if ( !blend )
{
if ( FT_SET_ERROR( tt_var_init_blend( FT_FACE( face ),
gvar_head.axisCount ) ) )
goto Exit;
blend = face->blend;
blend_created = TRUE;
}
if ( gvar_head.axisCount != (FT_UShort)blend->num_axis )
{
FT_TRACE1(( "ft_var_load_gvar:"
" number of axes in `gvar' and `cvar'\n" ));
@@ -2105,7 +2127,16 @@
FT_FRAME_EXIT();
}
if ( blend_created )
{
for ( i = 0; i < blend->tuplecount; i++ )
blend->tuplescalars[i] = (FT_Fixed)-0x20000L;
}
Exit:
if ( error && blend_created )
tt_done_blend( FT_FACE( face ) );
return error;
Fail2:
@@ -2118,6 +2149,70 @@
}
/**************************************************************************
*
* @Function:
* tt_var_init_gvar
*
* @Description:
* Initialize the private 'gvar' blend state. This also supports the
* static-font case where VARC addresses component-internal axes and no
* 'fvar' table is present.
*/
FT_LOCAL_DEF( FT_Error )
tt_var_init_gvar( FT_Face face )
{
TT_Face ttface = (TT_Face)face;
if ( ttface->blend && ttface->blend->glyphoffsets )
return FT_Err_Ok;
return ft_var_load_gvar( ttface );
}
/**************************************************************************
*
* @Function:
* tt_var_init_blend
*
* @Description:
* Initialize a private normalized-coordinate vector without 'fvar'.
* CFF2 VARC glyphs use this with the axis count from their variation
* store; TrueType VARC glyphs get the count from 'gvar' above.
*/
FT_LOCAL_DEF( FT_Error )
tt_var_init_blend( FT_Face face,
FT_UInt axis_count )
{
TT_Face ttface = (TT_Face)face;
FT_Memory memory = FT_FACE_MEMORY( face );
FT_Error error = FT_Err_Ok;
GX_Blend blend = ttface->blend;
if ( blend )
return blend->num_axis == axis_count
? FT_Err_Ok
: FT_THROW( Invalid_Table );
if ( FT_NEW( blend ) )
return error;
blend->num_axis = axis_count;
ttface->blend = blend;
if ( FT_NEW_ARRAY( blend->normalizedcoords, axis_count ) )
{
FT_FREE( blend );
ttface->blend = NULL;
}
return error;
}
/**************************************************************************
*
* @Function:
@@ -2606,6 +2701,11 @@
FT_FRAME_END
};
/* A static VARC face can have a private blend initialized from 'gvar' */
/* without exposing variation axes through the public MM API. */
if ( ttface->blend && !ttface->blend->mmvar )
return FT_THROW( Table_Missing );
/* `num_instances` holds the number of all named instances including */
/* the default instance, which might be missing in the table of named */
/* instances (in 'fvar'). This value is validated in `sfobjs.c` and */
@@ -5007,8 +5107,7 @@
FT_UInt i, num_axes;
/* blend->num_axis might not be set up yet */
num_axes = blend->mmvar->num_axis;
num_axes = blend->num_axis;
FT_FREE( blend->coords );
FT_FREE( blend->normalizedcoords );

View File

@@ -448,6 +448,13 @@ FT_BEGIN_HEADER
FT_Fixed* *normalizedcoords,
FT_MM_Var* *mm_var );
FT_LOCAL( FT_Error )
tt_var_init_gvar( FT_Face face );
FT_LOCAL( FT_Error )
tt_var_init_blend( FT_Face face,
FT_UInt axis_count );
FT_LOCAL( void )
tt_done_blend( FT_Face face );

View File

@@ -54,6 +54,21 @@
#define FT_COMPONENT ttvarc
FT_LOCAL_DEF( FT_Error )
tt_face_init_varc_axes( FT_Face face,
FT_UInt axis_count )
{
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
return tt_var_init_blend( face, axis_count );
#else
FT_UNUSED( face );
FT_UNUSED( axis_count );
return FT_Err_Ok;
#endif
}
/* Macro for bounds checking when reading from table */
#define CHECK_TABLE_BOUNDS( p, size ) \
( (FT_Byte*)(p) >= \
@@ -2986,6 +3001,9 @@
FT_Fixed* parent_coords = NULL;
FT_UInt num_coords = 0;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
FT_Bool static_axes = FALSE;
#endif
FT_Fixed stack_new_coords[VARC_STACK_COORD_COUNT];
FT_UInt stack_axis_indices[VARC_STACK_INDICES_COUNT];
@@ -3029,6 +3047,22 @@
FT_Done_MM_Var( face->root.driver->root.library,
master );
}
else if ( error &&
!FT_HAS_MULTIPLE_MASTERS( (FT_Face)face ) )
{
if ( !face->blend )
(void)tt_var_init_gvar( (FT_Face)face );
if ( face->blend && !face->blend->mmvar )
{
(void)tt_get_var_blend( (FT_Face)face,
&context->num_font_coords,
NULL,
NULL,
NULL );
static_axes = TRUE;
}
}
error = FT_Err_Ok; /* non-fatal */
}
@@ -3037,9 +3071,16 @@
if ( !FT_NEW_ARRAY( context->font_coords,
context->num_font_coords ) )
{
error = FT_Get_Var_Blend_Coordinates( (FT_Face)face,
context->num_font_coords,
context->font_coords );
if ( static_axes )
FT_MEM_COPY( context->font_coords,
face->blend->normalizedcoords,
context->num_font_coords * sizeof ( FT_Fixed ) );
else
error = FT_Get_Var_Blend_Coordinates(
(FT_Face)face,
context->num_font_coords,
context->font_coords );
if ( error )
{
FT_FREE( context->font_coords );
@@ -3054,18 +3095,21 @@
context->current_coords = context->font_coords;
context->num_current_coords = context->num_font_coords;
/* Force blend initialization: allocate `normalizedcoords`, */
/* load gvar, etc. This ensures */
/* `tt_varc_set_normalized_coords` can work as a lightweight */
/* coord swap later. */
face->root.autohint.data = NULL;
face->root.autohint.finalizer = NULL;
if ( !static_axes )
{
/* Force blend initialization: allocate */
/* `normalizedcoords`, load 'gvar', etc. This ensures */
/* `tt_varc_set_normalized_coords` can work as a */
/* lightweight coordinate swapper later. */
face->root.autohint.data = NULL;
face->root.autohint.finalizer = NULL;
FT_Set_Var_Blend_Coordinates( (FT_Face)face,
context->num_font_coords,
context->font_coords );
FT_Set_Var_Blend_Coordinates( (FT_Face)face,
context->num_font_coords,
context->font_coords );
face->root.autohint = saved_autohint;
face->root.autohint = saved_autohint;
}
}
}
}

View File

@@ -34,6 +34,11 @@ FT_BEGIN_HEADER
#ifdef TT_CONFIG_OPTION_VARC
FT_LOCAL( FT_Error )
tt_face_init_varc_axes( FT_Face face,
FT_UInt axis_count );
/**************************************************************************
*
* VARC component flags from the specification.

Binary file not shown.

Binary file not shown.

View File

@@ -85,7 +85,8 @@ main( void )
{
static const TestFont test_fonts[] =
{
{ NULL, 0, 0, 0 }
{ "varc-static-gvar.ttf", 1, 3, 50 },
{ "varc-static-cff2.otf", 2, 60, 36 }
};
FT_Library library;
@@ -105,7 +106,9 @@ main( void )
}
ret = 0;
for ( i = 0; test_fonts[i].filename; i++ )
for ( i = 0;
i < sizeof ( test_fonts ) / sizeof ( test_fonts[0] );
i++ )
ret |= test_font( library, testdata_dir, &test_fonts[i] );
FT_Done_FreeType( library );