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:
committed by
Werner Lemberg
parent
c7b5673f3d
commit
a69e39ad9e
@@ -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_, \
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
BIN
tests/data/varc-static-cff2.otf
Normal file
BIN
tests/data/varc-static-cff2.otf
Normal file
Binary file not shown.
BIN
tests/data/varc-static-gvar.ttf
Normal file
BIN
tests/data/varc-static-gvar.ttf
Normal file
Binary file not shown.
@@ -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 );
|
||||
|
||||
Reference in New Issue
Block a user