/**************************************************************************** * * ttgxvar.c * * TrueType GX Font Variation loader * * Copyright (C) 2004-2023 by * David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. * * This file is part of the FreeType project, and may only be used, * modified, and distributed under the terms of the FreeType project * license, LICENSE.TXT. By continuing to use, modify, or distribute * this file you indicate that you have read the license and * understand and accept it fully. * */ /************************************************************************** * * Apple documents the `fvar', `gvar', `cvar', and `avar' tables at * * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html * * The documentation for `gvar' is not intelligible; `cvar' refers you * to `gvar' and is thus also incomprehensible. * * The documentation for `avar' appears correct, but Apple has no fonts * with an `avar' table, so it is hard to test. * * Many thanks to John Jenkins (at Apple) in figuring this out. * * * Apple's `kern' table has some references to tuple indices, but as * there is no indication where these indices are defined, nor how to * interpolate the kerning values (different tuples have different * classes) this issue is ignored. * */ #include <ft2build.h> #include <freetype/internal/ftdebug.h> #include FT_CONFIG_CONFIG_H #include <freetype/internal/ftcalc.h> #include <freetype/internal/ftstream.h> #include <freetype/internal/sfnt.h> #include <freetype/internal/services/svmetric.h> #include <freetype/tttags.h> #include <freetype/ttnameid.h> #include <freetype/ftmm.h> #include <freetype/ftlist.h> #include "ttpload.h" #include "ttgxvar.h" #include "tterrors.h" #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT #define FT_Stream_FTell( stream ) … #define FT_Stream_SeekSet( stream, off ) … /* some macros we need */ #define FT_fdot14ToFixed( x ) … #define FT_intToFixed( i ) … #define FT_fdot6ToFixed( i ) … #define FT_fixedToInt( x ) … #define FT_fixedToFdot6( x ) … /************************************************************************** * * The macro FT_COMPONENT is used in trace mode. It is an implicit * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log * messages during execution. */ #undef FT_COMPONENT #define FT_COMPONENT … /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** Internal Routines *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /************************************************************************** * * The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It * indicates that there is a delta for every point without needing to * enumerate all of them. */ /* ensure that value `0' has the same width as a pointer */ #define ALL_POINTS … #define GX_PT_POINTS_ARE_WORDS … #define GX_PT_POINT_RUN_COUNT_MASK … /************************************************************************** * * @Function: * ft_var_readpackedpoints * * @Description: * Read a set of points to which the following deltas will apply. * Points are packed with a run length encoding. * * @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 * enumerating them individually. * * @Return: * An array of FT_UShort containing the affected points or the * special value ALL_POINTS. */ static FT_UShort* ft_var_readpackedpoints( FT_Stream stream, FT_ULong size, FT_UInt *point_cnt ) { … } #define GX_DT_DELTAS_ARE_ZERO … #define GX_DT_DELTAS_ARE_WORDS … #define GX_DT_DELTA_RUN_COUNT_MASK … /************************************************************************** * * @Function: * ft_var_readpackeddeltas * * @Description: * Read a set of deltas. These are packed slightly differently than * points. In particular there is no overall count. * * @Input: * stream :: * The data stream. * * size :: * The size of the table holding the data. * * delta_cnt :: * The number of deltas to be read. * * @Return: * An array of FT_Fixed containing the deltas for the affected * points. (This only gets the deltas for one dimension. It will * generally be called twice, once for x, once for y. When used in * cvt table, it will only be called once.) * * We use FT_Fixed to avoid accumulation errors while summing up all * deltas (the rounding to integer values happens as the very last * step). */ static FT_Fixed* ft_var_readpackeddeltas( FT_Stream stream, FT_ULong size, FT_UInt delta_cnt ) { … } /************************************************************************** * * @Function: * ft_var_load_avar * * @Description: * Parse the `avar' table if present. It need not be, so we return * nothing. * * @InOut: * face :: * The font face. */ static void ft_var_load_avar( TT_Face face ) { … } FT_LOCAL_DEF( FT_Error ) tt_var_load_item_variation_store( FT_Face face, /* TT_Face */ FT_ULong offset, GX_ItemVarStore itemStore ) { … } FT_LOCAL_DEF( FT_Error ) tt_var_load_delta_set_index_mapping( FT_Face face, /* TT_Face */ FT_ULong offset, GX_DeltaSetIdxMap map, GX_ItemVarStore itemStore, FT_ULong table_len ) { … } /************************************************************************** * * @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_LOCAL_DEF( FT_ItemVarDelta ) tt_var_get_item_delta( FT_Face face, /* TT_Face */ GX_ItemVarStore itemStore, FT_UInt outerIndex, FT_UInt innerIndex ) { … } /************************************************************************** * * @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_LOCAL_DEF( FT_Error ) tt_hadvance_adjust( FT_Face face, /* TT_Face */ FT_UInt gindex, FT_Int *avalue ) { … } FT_LOCAL_DEF( FT_Error ) tt_vadvance_adjust( FT_Face face, /* TT_Face */ FT_UInt gindex, FT_Int *avalue ) { … } #define GX_VALUE_SIZE … /* all values are FT_Short or FT_UShort entities; */ /* we treat them consistently as FT_Short */ #define GX_VALUE_CASE( tag, dflt ) … #define GX_GASP_CASE( idx ) … static FT_Short* ft_var_get_value_pointer( TT_Face face, FT_ULong mvar_tag ) { … } /************************************************************************** * * @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 ) { … } static FT_Error ft_size_reset_iterator( FT_ListNode node, void* user ) { … } /************************************************************************** * * @Function: * tt_apply_mvar * * @Description: * Apply `MVAR' table adjustments. * * @InOut: * face :: * The font face. */ FT_LOCAL_DEF( void ) tt_apply_mvar( FT_Face face ) /* TT_Face */ { … } GX_GVar_Head; /************************************************************************** * * @Function: * ft_var_load_gvar * * @Description: * Parse the `gvar' table if present. If `fvar' is there, `gvar' had * better be there too. * * @InOut: * face :: * The font face. * * @Return: * FreeType error code. 0 means success. */ static FT_Error ft_var_load_gvar( TT_Face face ) { … } /************************************************************************** * * @Function: * ft_var_apply_tuple * * @Description: * Figure out whether a given tuple (design) applies to the current * blend, and if so, what is the scaling factor. * * @Input: * blend :: * The current blend of the font. * * tupleIndex :: * A flag saying whether this is an intermediate * tuple or not. * * tuple_coords :: * The coordinates of the tuple in normalized axis * units. * * im_start_coords :: * The initial coordinates where this tuple starts * to apply (for intermediate coordinates). * * im_end_coords :: * The final coordinates after which this tuple no * longer applies (for intermediate coordinates). * * @Return: * An FT_Fixed value containing the scaling factor. */ static FT_Fixed ft_var_apply_tuple( GX_Blend blend, FT_UShort tupleIndex, FT_Fixed* tuple_coords, FT_Fixed* im_start_coords, FT_Fixed* im_end_coords ) { … } /* 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 ) { … } /* 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 ) { … } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ GX_FVar_Head; GX_FVar_Axis; /************************************************************************** * * @Function: * TT_Get_MM_Var * * @Description: * Check that the font's `fvar' table is valid, parse it, and return * those data. It also loads (and parses) the `MVAR' table, if * possible. * * @InOut: * face :: * The font face. * TT_Get_MM_Var initializes the blend structure. * * @Output: * master :: * The `fvar' data (must be freed by caller). Can be NULL, * which makes this function simply load MM support. * * @Return: * FreeType error code. 0 means success. */ FT_LOCAL_DEF( FT_Error ) TT_Get_MM_Var( FT_Face face, /* TT_Face */ FT_MM_Var* *master ) { … } static FT_Error tt_set_mm_blend( TT_Face face, FT_UInt num_coords, FT_Fixed* coords, FT_Bool set_design_coords ) { … } /************************************************************************** * * @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, -1 means success and unchanged * axis values. */ FT_LOCAL_DEF( FT_Error ) TT_Set_MM_Blend( FT_Face face, /* TT_Face */ FT_UInt num_coords, FT_Fixed* coords ) { … } /************************************************************************** * * @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, -1 means success and unchanged * axis values. */ FT_LOCAL_DEF( FT_Error ) TT_Get_MM_Blend( FT_Face face, /* TT_Face */ FT_UInt num_coords, FT_Fixed* coords ) { … } /************************************************************************** * * @Function: * TT_Set_Var_Design * * @Description: * Set the coordinates for the instance, measured in the user * coordinate system. Parse the `avar' table (if present) to convert * from user to normalized coordinates. * * @InOut: * face :: * The font face. * Initialize the blend struct 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 values for the remaining axes. * * coords :: * A coordinate array with `num_coords' elements. * * @Return: * FreeType error code. 0 means success. */ FT_LOCAL_DEF( FT_Error ) TT_Set_Var_Design( FT_Face face, /* TT_Face */ FT_UInt num_coords, FT_Fixed* coords ) { … } /************************************************************************** * * @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( FT_Face face, /* TT_Face */ FT_UInt num_coords, FT_Fixed* coords ) { … } /************************************************************************** * * @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, -1 means success and unchanged * axis values. */ FT_LOCAL_DEF( FT_Error ) TT_Set_Named_Instance( FT_Face face, /* TT_Face */ FT_UInt instance_index ) { … } /************************************************************************** * * @Function: * TT_Get_Default_Named_Instance * * @Description: * Get the default named instance. * * @Input: * face :: * A handle to the source face. * * @Output: * instance_index :: * The default named instance index. * * @Return: * FreeType error code. 0~means success. */ FT_LOCAL_DEF( FT_Error ) TT_Get_Default_Named_Instance( FT_Face face, FT_UInt *instance_index ) { … } /* This function triggers (lazy) recomputation of the `postscript_name` */ /* field in `TT_Face`. */ FT_LOCAL_DEF( void ) tt_construct_ps_name( FT_Face face ) { … } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GX VAR PARSING ROUTINES *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER static FT_Error tt_cvt_ready_iterator( FT_ListNode node, void* user ) { … } #endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ /************************************************************************** * * @Function: * tt_face_vary_cvt * * @Description: * Modify the loaded cvt table according to the `cvar' table and the * font's blend. * * @InOut: * face :: * A handle to the target face object. * * @Input: * stream :: * A handle to the input stream. * * @Return: * FreeType error code. 0 means success. * * Most errors are ignored. It is perfectly valid not to have a * `cvar' table even if there is a `gvar' and `fvar' table. */ FT_LOCAL_DEF( FT_Error ) tt_face_vary_cvt( TT_Face face, FT_Stream stream ) { … } /* Shift the original coordinates of all points between indices `p1' */ /* and `p2', using the same difference as given by index `ref'. */ /* modeled after `af_iup_shift' */ static void tt_delta_shift( int p1, int p2, int ref, FT_Vector* in_points, FT_Vector* out_points ) { … } /* Interpolate the original coordinates of all points with indices */ /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ /* point indices. */ /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ /* `Ins_IUP' with spec differences in handling ill-defined cases. */ static void tt_delta_interpolate( int p1, int p2, int ref1, int ref2, FT_Vector* in_points, FT_Vector* out_points ) { … } /* Interpolate points without delta values, similar to */ /* the `IUP' hinting instruction. */ /* modeled after `Ins_IUP */ static void tt_interpolate_deltas( FT_Outline* outline, FT_Vector* out_points, FT_Vector* in_points, FT_Bool* has_delta ) { … } /************************************************************************** * * @Function: * TT_Vary_Apply_Glyph_Deltas * * @Description: * Apply the appropriate deltas to the current glyph. * * @InOut: * loader :: * A handle to the loader object. * * outline :: * The outline to change, with appended phantom points. * * @Output: * unrounded :: * An array with `n_points' elements that is filled with unrounded * point coordinates (in 26.6 format). * * @Return: * FreeType error code. 0 means success. */ FT_LOCAL_DEF( FT_Error ) TT_Vary_Apply_Glyph_Deltas( TT_Loader loader, FT_Outline* outline, FT_Vector* unrounded ) { … } /************************************************************************** * * @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( FT_Face face, /* TT_Face */ FT_UInt *num_coords, FT_Fixed* *coords, FT_Fixed* *normalizedcoords, FT_MM_Var* *mm_var ) { … } FT_LOCAL_DEF( void ) tt_var_done_item_variation_store( FT_Face face, GX_ItemVarStore itemStore ) { … } FT_LOCAL_DEF( void ) tt_var_done_delta_set_index_map( FT_Face face, GX_DeltaSetIdxMap deltaSetIdxMap ) { … } /************************************************************************** * * @Function: * tt_done_blend * * @Description: * Free the blend internal data structure. */ FT_LOCAL_DEF( void ) tt_done_blend( FT_Face face ) { … } #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 */