diff --git a/README b/README index 23c10abb1..1a975e69b 100644 --- a/README +++ b/README @@ -1,3 +1,12 @@ + Branch of FreeType to support OpenType 1.8 + ========================================== + + This branch contains changes for supporting OpenType 1.8. + The changes will be merged back upstream in September 2016, + when the specification for OpenType 1.8 is final and has + been published. + + FreeType 2.6.5 ============== diff --git a/include/freetype/ftmm.h b/include/freetype/ftmm.h index 6c05f0c39..c447f1cce 100644 --- a/include/freetype/ftmm.h +++ b/include/freetype/ftmm.h @@ -171,6 +171,7 @@ FT_BEGIN_HEADER { FT_Fixed* coords; FT_UInt strid; + FT_UInt psid; } FT_Var_Named_Style; @@ -329,6 +330,10 @@ FT_BEGIN_HEADER FT_UInt num_coords, FT_Fixed* coords ); + FT_EXPORT( FT_Error ) + FT_Get_Var_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); /*************************************************************************/ /* */ @@ -375,6 +380,11 @@ FT_BEGIN_HEADER /* */ + FT_EXPORT( FT_Error ) + FT_Get_Var_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + FT_END_HEADER diff --git a/include/freetype/internal/fttrace.h b/include/freetype/internal/fttrace.h index efb335595..54ca595bb 100644 --- a/include/freetype/internal/fttrace.h +++ b/include/freetype/internal/fttrace.h @@ -90,6 +90,7 @@ FT_TRACE_DEF( cffparse ) FT_TRACE_DEF( cf2blues ) FT_TRACE_DEF( cf2hints ) +FT_TRACE_DEF( cf2font ) FT_TRACE_DEF( cf2interp ) /* Type 42 driver component */ diff --git a/include/freetype/internal/services/svmm.h b/include/freetype/internal/services/svmm.h index b78a19f8e..6a1254d00 100644 --- a/include/freetype/internal/services/svmm.h +++ b/include/freetype/internal/services/svmm.h @@ -58,6 +58,16 @@ FT_BEGIN_HEADER FT_UInt num_coords, FT_Long* coords ); + typedef FT_Error + (*FT_Get_Var_Design_Func)( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + typedef FT_Error + (*FT_Get_Var_Blend_Func)( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + FT_DEFINE_SERVICE( MultiMasters ) { @@ -66,6 +76,8 @@ FT_BEGIN_HEADER FT_Set_MM_Blend_Func set_mm_blend; FT_Get_MM_Var_Func get_mm_var; FT_Set_Var_Design_Func set_var_design; + FT_Get_Var_Design_Func get_var_design; + FT_Get_Var_Blend_Func get_var_blend; }; @@ -76,10 +88,13 @@ FT_BEGIN_HEADER set_mm_design_, \ set_mm_blend_, \ get_mm_var_, \ - set_var_design_ ) \ + set_var_design_, \ + get_var_design_, \ + get_var_blend_ ) \ static const FT_Service_MultiMastersRec class_ = \ { \ - get_mm_, set_mm_design_, set_mm_blend_, get_mm_var_, set_var_design_ \ + get_mm_, set_mm_design_, set_mm_blend_, get_mm_var_, set_var_design_, \ + get_var_design_, get_var_blend_ \ }; #else /* FT_CONFIG_OPTION_PIC */ @@ -89,7 +104,9 @@ FT_BEGIN_HEADER set_mm_design_, \ set_mm_blend_, \ get_mm_var_, \ - set_var_design_ ) \ + set_var_design_, \ + get_var_design_, \ + get_var_blend_ ) \ void \ FT_Init_Class_ ## class_( FT_Service_MultiMastersRec* clazz ) \ { \ @@ -98,6 +115,8 @@ FT_BEGIN_HEADER clazz->set_mm_blend = set_mm_blend_; \ clazz->get_mm_var = get_mm_var_; \ clazz->set_var_design = set_var_design_; \ + clazz->get_var_design = get_var_design_; \ + clazz->get_var_blend = get_var_blend_; \ } #endif /* FT_CONFIG_OPTION_PIC */ diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index 4110d5028..efade9547 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -1346,6 +1346,7 @@ FT_BEGIN_HEADER FT_ULong glyf_len; #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + FT_Bool isCFF2; FT_Bool doblend; GX_Blend blend; #endif diff --git a/include/freetype/tttags.h b/include/freetype/tttags.h index f3c9aa5fc..49eecd2e9 100644 --- a/include/freetype/tttags.h +++ b/include/freetype/tttags.h @@ -43,6 +43,7 @@ FT_BEGIN_HEADER #define TTAG_CBDT FT_MAKE_TAG( 'C', 'B', 'D', 'T' ) #define TTAG_CBLC FT_MAKE_TAG( 'C', 'B', 'L', 'C' ) #define TTAG_CFF FT_MAKE_TAG( 'C', 'F', 'F', ' ' ) +#define TTAG_CFF2 FT_MAKE_TAG( 'C', 'F', 'F', '2' ) #define TTAG_CID FT_MAKE_TAG( 'C', 'I', 'D', ' ' ) #define TTAG_cmap FT_MAKE_TAG( 'c', 'm', 'a', 'p' ) #define TTAG_cvar FT_MAKE_TAG( 'c', 'v', 'a', 'r' ) @@ -61,6 +62,7 @@ FT_BEGIN_HEADER #define TTAG_GPOS FT_MAKE_TAG( 'G', 'P', 'O', 'S' ) #define TTAG_GSUB FT_MAKE_TAG( 'G', 'S', 'U', 'B' ) #define TTAG_gvar FT_MAKE_TAG( 'g', 'v', 'a', 'r' ) +#define TTAG_HVAR FT_MAKE_TAG( 'H', 'V', 'A', 'R' ) #define TTAG_hdmx FT_MAKE_TAG( 'h', 'd', 'm', 'x' ) #define TTAG_head FT_MAKE_TAG( 'h', 'e', 'a', 'd' ) #define TTAG_hhea FT_MAKE_TAG( 'h', 'h', 'e', 'a' ) diff --git a/src/base/ftmm.c b/src/base/ftmm.c index 6b759ca46..b6bc3c506 100644 --- a/src/base/ftmm.c +++ b/src/base/ftmm.c @@ -172,6 +172,34 @@ } + /* documentation is in ftmm.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Var_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !coords ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->get_var_design ) + error = service->get_var_design( face, num_coords, coords ); + } + + return error; + } + + /* documentation is in ftmm.h */ FT_EXPORT_DEF( FT_Error ) @@ -230,5 +258,32 @@ return error; } + /* documentation is in ftmm.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Var_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ) + { + FT_Error error; + FT_Service_MultiMasters service; + + + /* check of `face' delayed to `ft_face_get_mm_service' */ + + if ( !coords ) + return FT_THROW( Invalid_Argument ); + + error = ft_face_get_mm_service( face, &service ); + if ( !error ) + { + error = FT_ERR( Invalid_Argument ); + if ( service->get_var_blend ) + error = service->get_var_blend( face, num_coords, coords ); + } + + return error; + } + /* END */ diff --git a/src/cff/cf2fixed.h b/src/cff/cf2fixed.h index 74af37708..2e4b5032f 100644 --- a/src/cff/cf2fixed.h +++ b/src/cff/cf2fixed.h @@ -51,8 +51,8 @@ FT_BEGIN_HEADER #define CF2_FIXED_MAX ( (CF2_Fixed)0x7FFFFFFFL ) #define CF2_FIXED_MIN ( (CF2_Fixed)0x80000000L ) -#define CF2_FIXED_ONE 0x10000L -#define CF2_FIXED_EPSILON 0x0001 +#define CF2_FIXED_ONE ( (CF2_Fixed)0x10000L ) +#define CF2_FIXED_EPSILON ( (CF2_Fixed)0x0001 ) /* in C 89, left and right shift of negative numbers is */ /* implementation specific behaviour in the general case */ diff --git a/src/cff/cf2font.c b/src/cff/cf2font.c index 83fd348f2..9fd1531a4 100644 --- a/src/cff/cf2font.c +++ b/src/cff/cf2font.c @@ -38,6 +38,7 @@ #include #include FT_INTERNAL_CALC_H +#include FT_INTERNAL_DEBUG_H #include "cf2ft.h" @@ -46,6 +47,8 @@ #include "cf2error.h" #include "cf2intrp.h" +#undef FT_COMPONENT +#define FT_COMPONENT trace_cf2font /* Compute a stem darkening amount in character space. */ static void @@ -233,25 +236,155 @@ *darkenAmount += boldenAmount / 2; } + /* compute a blend vector from variation store index and normalized vector */ + /* return size of blend vector and allocated storage for it */ + /* caller must free this */ + /* lenNormalizedVector == 0 produces a default blend vector */ + /* Note: normalizedVector uses FT_Fixed, not CF2_Fixed */ + static void + cf2_buildBlendVector( CF2_Font font, CF2_UInt vsindex, + CF2_UInt lenNormalizedVector, FT_Fixed * normalizedVector, + CF2_UInt * lenBlendVector, CF2_Fixed ** blendVector ) + { + FT_Error error = FT_Err_Ok; /* for FT_REALLOC */ + FT_Memory memory = font->memory; /* for FT_REALLOC */ + CF2_UInt len; + CFF_VStore vs; + CFF_VarData* varData; + CF2_UInt master; + + FT_UNUSED( lenNormalizedVector ); + FT_UNUSED( vsindex ); + + FT_ASSERT( lenBlendVector && blendVector ); + FT_ASSERT( lenNormalizedVector == 0 || normalizedVector ); + FT_TRACE4(( "cf2_buildBlendVector\n" )); + + vs = cf2_getVStore( font->decoder ); + + /* VStore and fvar must be consistent */ + if ( lenNormalizedVector != 0 && lenNormalizedVector != vs->axisCount ) + { + FT_TRACE4(( "cf2_buildBlendVector: Axis count mismatch\n" )); + CF2_SET_ERROR( &font->error, Invalid_File_Format ); + goto Exit; + } + if ( vsindex >= vs->dataCount ) + { + FT_TRACE4(( "cf2_buildBlendVector: vsindex out of range\n" )); + CF2_SET_ERROR( &font->error, Invalid_File_Format ); + goto Exit; + } + + /* select the item variation data structure */ + varData = &vs->varData[vsindex]; + + /* prepare an output buffer for the blend vector */ + len = varData->regionIdxCount + 1; /* add 1 for default */ + if ( FT_REALLOC( *blendVector, *lenBlendVector, len * sizeof( **blendVector )) ) + return; + *lenBlendVector = len; + + /* outer loop steps through master designs to be blended */ + for ( master=0; masterregionIndices[master-1]; + varRegion = &vs->varRegionList[idx]; + + if ( idx >= vs->regionCount ) + { + FT_TRACE4(( "cf2_buildBlendVector: region index out of range\n" )); + CF2_SET_ERROR( &font->error, Invalid_File_Format ); + goto Exit; + } + + /* Note: lenNormalizedVector could be zero */ + /* In that case, build default blend vector */ + if ( lenNormalizedVector != 0 ) + (*blendVector)[master] = CF2_FIXED_ONE; /* default */ + + /* inner loop steps through axes in this region */ + for ( j=0; jaxisList[j]; + CF2_Fixed axisScalar; + + /* compute the scalar contribution of this axis */ + /* ignore invalid ranges */ + if ( axis->startCoord > axis->peakCoord || axis->peakCoord > axis->endCoord ) + axisScalar = CF2_FIXED_ONE; + else if ( axis->startCoord < 0 && axis->endCoord > 0 && axis->peakCoord != 0 ) + axisScalar = CF2_FIXED_ONE; + /* peak of 0 means ignore this axis */ + else if ( axis->peakCoord == 0 ) + axisScalar = CF2_FIXED_ONE; + /* ignore this region if coords are out of range */ + else if ( normalizedVector[j] < axis->startCoord || normalizedVector[j] > axis->endCoord ) + axisScalar = 0; + /* calculate a proportional factor */ + else + { + if ( normalizedVector[j] == axis->peakCoord ) + axisScalar = CF2_FIXED_ONE; + else if ( normalizedVector[j] < axis->peakCoord ) + axisScalar = FT_DivFix( normalizedVector[j] - axis->startCoord, + axis->peakCoord - axis->startCoord ); + else + axisScalar = FT_DivFix( axis->endCoord - normalizedVector[j], + axis->endCoord - axis->peakCoord ); + } + /* take product of all the axis scalars */ + (*blendVector)[master] = FT_MulFix( (*blendVector)[master], axisScalar ); + } + FT_TRACE4(( ", %f ", (double)(*blendVector)[master] / 65536 )); + } + FT_TRACE4(( "]\n" )); + + Exit: + return; + } + /* set up values for the current FontDict and matrix */ + /* called for each glyph to be rendered */ /* caller's transform is adjusted for subpixel positioning */ static void cf2_font_setup( CF2_Font font, const CF2_Matrix* transform ) { + FT_Error error = FT_Err_Ok; /* for FT_REALLOC */ + FT_Memory memory = font->memory; /* for FT_REALLOC */ + /* pointer to parsed font object */ CFF_Decoder* decoder = font->decoder; FT_Bool needExtraSetup = FALSE; + CFF_VStoreRec* vstore; + FT_Bool hasVariations = FALSE; + /* character space units */ CF2_Fixed boldenX = font->syntheticEmboldeningAmountX; CF2_Fixed boldenY = font->syntheticEmboldeningAmountY; CFF_SubFont subFont; CF2_Fixed ppem; + CF2_UInt lenNormalizedV = 0; + FT_Fixed * normalizedV = NULL; /* clear previous error */ @@ -266,6 +399,33 @@ needExtraSetup = TRUE; } + /* check for variation vectors */ + vstore = cf2_getVStore( decoder ); + hasVariations = ( vstore->dataCount != 0 ); + + if ( hasVariations ) + { + if ( font->lenBlendVector == 0 ) + needExtraSetup = TRUE; /* a blend vector is required */ + + /* Note: lenNormalizedVector is zero until FT_Get_MM_Var() is called */ + cf2_getNormalizedVector( decoder, &lenNormalizedV, &normalizedV ); + + /* determine if blend vector needs to be recomputed */ + if ( font->lastVsindex != subFont->font_dict.vsindex || + lenNormalizedV == 0 || + font->lenNormalizedVector != lenNormalizedV || + ( lenNormalizedV && + memcmp( normalizedV, + &font->lastNormalizedVector, + lenNormalizedV * sizeof( *normalizedV ) ) != 0 ) ) + { + font->lastVsindex = subFont->font_dict.vsindex; + /* vectors will be copied below, during ExtraSetup */ + needExtraSetup = TRUE; + } + } + /* if ppem has changed, we need to recompute some cached data */ /* note: because of CID font matrix concatenation, ppem and transform */ /* do not necessarily track. */ @@ -423,7 +583,37 @@ /* compute blue zones for this instance */ cf2_blues_init( &font->blues, font ); - } + + /* copy normalized vector used to compute blend vector */ + if ( hasVariations ) + { + /* if user has set a normalized vector, use it */ + /* otherwise, assume default */ + if ( lenNormalizedV != 0 ) + { + /* user has set a normalized vector */ + if ( FT_REALLOC( font->lastNormalizedVector, + font->lenNormalizedVector, + lenNormalizedV * sizeof( *normalizedV )) ) + { + CF2_SET_ERROR( &font->error, Out_Of_Memory ); + return; + } + font->lenNormalizedVector = lenNormalizedV; + FT_MEM_COPY( font->lastNormalizedVector, + normalizedV, + lenNormalizedV * sizeof( *normalizedV )); + } + + /* build blend vector for this instance */ + cf2_buildBlendVector( font, font->lastVsindex, + font->lenNormalizedVector, + font->lastNormalizedVector, + &font->lenBlendVector, + &font->blendVector ); + } + + } /* needExtraSetup */ } diff --git a/src/cff/cf2font.h b/src/cff/cf2font.h index bd05e69e7..a6fa31076 100644 --- a/src/cff/cf2font.h +++ b/src/cff/cf2font.h @@ -47,7 +47,7 @@ FT_BEGIN_HEADER -#define CF2_OPERAND_STACK_SIZE 48 +#define CF2_OPERAND_STACK_SIZE 193/* TODO: this is temporary for CFF2 */ #define CF2_MAX_SUBR 16 /* maximum subroutine nesting; */ /* only 10 are allowed but there exist */ /* fonts like `HiraKakuProN-W3.ttf' */ @@ -63,6 +63,7 @@ FT_BEGIN_HEADER FT_Memory memory; FT_Error error; /* shared error for this instance */ + FT_Bool isCFF2; CF2_RenderingFlags renderingFlags; /* variables that depend on Transform: */ @@ -74,6 +75,13 @@ FT_BEGIN_HEADER CF2_Matrix outerTransform; /* post hinting; includes rotations */ CF2_Fixed ppem; /* transform-dependent */ + /* variation data */ + CF2_UInt lastVsindex; /* last vsindex used */ + CF2_UInt lenNormalizedVector; /* normDV length (aka numAxes) */ + FT_Fixed * lastNormalizedVector;/* last normDV used */ + CF2_UInt lenBlendVector; /* blendV length (aka numMasters) */ + CF2_Fixed * blendVector; /* current blendV (per glyph) */ + CF2_Int unitsPerEm; CF2_Fixed syntheticEmboldeningAmountX; /* character space units */ diff --git a/src/cff/cf2ft.c b/src/cff/cf2ft.c index 55f3206ac..3f8334f5f 100644 --- a/src/cff/cf2ft.c +++ b/src/cff/cf2ft.c @@ -102,9 +102,10 @@ if ( font ) { FT_Memory memory = font->memory; - - (void)memory; + + FT_FREE( font->lastNormalizedVector ); + FT_FREE( font->blendVector ); } } @@ -366,6 +367,9 @@ &hinted, &scaled ); + /* copy isCFF2 boolean from TT_Face to CF2_Font */ + font->isCFF2 = builder->face->isCFF2; + font->renderingFlags = 0; if ( hinted ) font->renderingFlags |= CF2_FlagsHinted; @@ -412,6 +416,39 @@ return decoder->current_subfont; } + /* get pointer to VStore structure */ + FT_LOCAL_DEF( CFF_VStore ) + cf2_getVStore( CFF_Decoder* decoder ) + { + FT_ASSERT( decoder && decoder->cff ); + + return &decoder->cff->vstore; + } + + /* get normalized design vector for current render request */ + /* returns pointer and length */ + /* if blend struct is not initialized, return length zero */ + /* Note: use FT_Fixed not CF2_Fixed for the vector */ + FT_LOCAL_DEF( void ) + cf2_getNormalizedVector( CFF_Decoder* decoder, CF2_UInt * len, FT_Fixed ** vec ) + { + GX_Blend blend; + + FT_ASSERT( decoder && decoder->builder.face ); + FT_ASSERT( vec && len ); + + blend = decoder->builder.face->blend; + if ( blend ) + { + *vec = blend->normalizedcoords; + *len = blend->num_axis; + } + else + { + *vec = NULL; + *len = 0; + } + } /* get `y_ppem' from `CFF_Size' */ FT_LOCAL_DEF( CF2_Fixed ) diff --git a/src/cff/cf2ft.h b/src/cff/cf2ft.h index 8e55e841a..7c1311aab 100644 --- a/src/cff/cf2ft.h +++ b/src/cff/cf2ft.h @@ -64,6 +64,14 @@ FT_BEGIN_HEADER FT_LOCAL( CFF_SubFont ) cf2_getSubfont( CFF_Decoder* decoder ); + FT_LOCAL( CFF_VStore ) + cf2_getVStore( CFF_Decoder* decoder ); + + + FT_LOCAL_DEF( void ) + cf2_getNormalizedVector( CFF_Decoder* decoder, + CF2_UInt * len, + FT_Fixed ** vec ); FT_LOCAL( CF2_Fixed ) cf2_getPpemY( CFF_Decoder* decoder ); diff --git a/src/cff/cf2intrp.c b/src/cff/cf2intrp.c index 7d663dd0e..a011e288a 100644 --- a/src/cff/cf2intrp.c +++ b/src/cff/cf2intrp.c @@ -216,7 +216,7 @@ cf2_cmdRESERVED_13, /* 13 */ cf2_cmdENDCHAR, /* 14 */ cf2_cmdRESERVED_15, /* 15 */ - cf2_cmdRESERVED_16, /* 16 */ + cf2_cmdBLEND, /* 16 */ cf2_cmdRESERVED_17, /* 17 */ cf2_cmdHSTEMHM, /* 18 */ cf2_cmdHINTMASK, /* 19 */ @@ -402,6 +402,34 @@ *curY = vals[13]; } + /* Blend numOperands on the stack, */ + /* store results into the first numBlends values, */ + /* then pop remaining arguments. */ + static void + cf2_doBlend( const CF2_Font font, + CF2_Stack opStack, + CF2_UInt numBlends ) + { + CF2_UInt delta; + CF2_UInt base; + CF2_UInt i, j; + CF2_UInt numOperands = (CF2_UInt)(numBlends * font->lenBlendVector); + + base = cf2_stack_count( opStack ) - numOperands; + delta = base + numBlends; + for ( i = 0; i < numBlends; i++ ) + { + const CF2_Fixed * weight = &font->blendVector[1]; + CF2_Fixed sum = cf2_stack_getReal( opStack, i+base ); /* start with first term */ + for ( j = 1; j < font->lenBlendVector; j++ ) + { + sum += FT_MulFix( *weight++, cf2_stack_getReal( opStack, delta++ )); + } + cf2_stack_setReal( opStack, i+base, sum ); /* store blended result */ + } + /* leave only numBlends results on stack */ + cf2_stack_pop( opStack, numOperands - numBlends ); +} /* * `error' is a shared error code used by many objects in this @@ -585,12 +613,20 @@ case cf2_cmdRESERVED_9: case cf2_cmdRESERVED_13: case cf2_cmdRESERVED_15: - case cf2_cmdRESERVED_16: case cf2_cmdRESERVED_17: /* we may get here if we have a prior error */ FT_TRACE4(( " unknown op (%d)\n", op1 )); break; + case cf2_cmdBLEND: + { + FT_UInt numBlends = (FT_UInt)cf2_stack_popInt( opStack ); + FT_TRACE4(( " blend\n" )); + cf2_doBlend( font, opStack, numBlends ); + } + continue; /* do not clear the stack */ + + case cf2_cmdHSTEMHM: case cf2_cmdHSTEM: FT_TRACE4(( op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n" )); diff --git a/src/cff/cf2stack.c b/src/cff/cf2stack.c index 6fafd901f..5001b68cf 100644 --- a/src/cff/cf2stack.c +++ b/src/cff/cf2stack.c @@ -194,6 +194,34 @@ } } + /* provide random access to stack */ + FT_LOCAL_DEF( void ) + cf2_stack_setReal( CF2_Stack stack, + CF2_UInt idx, + CF2_Fixed val ) + { + if ( idx > cf2_stack_count( stack ) ) + { + CF2_SET_ERROR( stack->error, Stack_Overflow ); + return; + } + + stack->buffer[idx].u.r = val; + stack->buffer[idx].type = CF2_NumberFixed; + } + + /* discard (pop) num values from stack */ + FT_LOCAL_DEF( void ) + cf2_stack_pop( CF2_Stack stack, + CF2_UInt num ) + { + if ( num > cf2_stack_count( stack ) ) + { + CF2_SET_ERROR( stack->error, Stack_Underflow ); + return; + } + stack->top -= num; + } FT_LOCAL( void ) cf2_stack_roll( CF2_Stack stack, diff --git a/src/cff/cf2stack.h b/src/cff/cf2stack.h index e740a7ac4..acec07470 100644 --- a/src/cff/cf2stack.h +++ b/src/cff/cf2stack.h @@ -93,6 +93,15 @@ FT_BEGIN_HEADER cf2_stack_getReal( CF2_Stack stack, CF2_UInt idx ); + FT_LOCAL( void ) + cf2_stack_setReal( CF2_Stack stack, + CF2_UInt idx, + CF2_Fixed val ); + + FT_LOCAL( void ) + cf2_stack_pop( CF2_Stack stack, + CF2_UInt num ); + FT_LOCAL( void ) cf2_stack_roll( CF2_Stack stack, CF2_Int count, diff --git a/src/cff/cffdrivr.c b/src/cff/cffdrivr.c index 67bf09d8c..b03dacbd8 100644 --- a/src/cff/cffdrivr.c +++ b/src/cff/cffdrivr.c @@ -32,6 +32,12 @@ #include "cffcmap.h" #include "cffparse.h" +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include FT_MULTIPLE_MASTERS_H +#include FT_SERVICE_MULTIPLE_MASTERS_H +#include "../truetype/ttgxvar.h" +#endif + #include "cfferrs.h" #include "cffpic.h" @@ -291,6 +297,14 @@ FT_Error error; + /* TODO: for testing: cff2 does not have glyph names */ + /* will need to use post table method */ + if ( font->version_major == 2 ) + { + FT_STRCPYN( buffer, "noname", buffer_max ); + return FT_Err_Ok; + } + if ( !font->psnames ) { FT_ERROR(( "cff_get_glyph_name:" @@ -871,15 +885,31 @@ /*************************************************************************/ /*************************************************************************/ +/* reuse some of the TT functions for the cff multi_master service */ +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + FT_DEFINE_SERVICE_MULTIMASTERSREC( + cff_service_multi_masters, + (FT_Get_MM_Func) NULL, /* get_mm */ + (FT_Set_MM_Design_Func) NULL, /* set_mm_design */ + (FT_Set_MM_Blend_Func) TT_Set_MM_Blend, /* set_mm_blend */ + (FT_Get_MM_Var_Func) TT_Get_MM_Var, /* get_mm_var */ + (FT_Set_Var_Design_Func) TT_Set_Var_Design, /* set_var_design */ + (FT_Get_Var_Design_Func) TT_Get_Var_Design, /* get_var_design */ + (FT_Get_Var_Blend_Func) TT_Get_Var_Blend ) /* get_var_blend */ + +#endif + +/* TODO: fix up ifdefs and we really need an 8-parameter FT_DEFINE_SERVICEDESCREC8 */ #ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES FT_DEFINE_SERVICEDESCREC7( cff_services, FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_CFF, + FT_SERVICE_ID_MULTI_MASTERS, &CFF_SERVICE_MULTI_MASTERS_GET, FT_SERVICE_ID_POSTSCRIPT_INFO, &CFF_SERVICE_PS_INFO_GET, FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &CFF_SERVICE_PS_NAME_GET, FT_SERVICE_ID_GLYPH_DICT, &CFF_SERVICE_GLYPH_DICT_GET, FT_SERVICE_ID_TT_CMAP, &CFF_SERVICE_GET_CMAP_INFO_GET, - FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET, + /*FT_SERVICE_ID_CID, &CFF_SERVICE_CID_INFO_GET,*/ FT_SERVICE_ID_PROPERTIES, &CFF_SERVICE_PROPERTIES_GET ) #else diff --git a/src/cff/cffload.c b/src/cff/cffload.c index 3d1bda97b..c357a1acb 100644 --- a/src/cff/cffload.c +++ b/src/cff/cffload.c @@ -589,12 +589,15 @@ FT_UInt element ) { CFF_Index idx = &font->name_index; - FT_Memory memory = idx->stream->memory; + FT_Memory memory; FT_Byte* bytes; FT_ULong byte_len; FT_Error error; FT_String* name = 0; + if ( !idx->stream ) /* CFF2 does not include a name index */ + goto Exit; + memory = idx->stream->memory; error = cff_index_access_element( idx, element, &bytes, &byte_len ); if ( error ) @@ -862,6 +865,32 @@ charset->offset = 0; } + static void + cff_vstore_done( CFF_VStoreRec* vstore, + FT_Memory memory ) + { + FT_UInt i; + + /* free regionList and axisLists */ + if ( vstore->varRegionList ) + { + for ( i=0; iregionCount; i++ ) + { + FT_FREE( vstore->varRegionList[i].axisList ); + } + } + FT_FREE( vstore->varRegionList ); + + /* free varData and indices */ + if ( vstore->varData ) + { + for ( i=0; idataCount; i++ ) + { + FT_FREE( vstore->varData[i].regionIndices ); + } + } + FT_FREE( vstore->varData ); + } static FT_Error cff_charset_load( CFF_Charset charset, @@ -1053,6 +1082,130 @@ } + /* convert 2.14 to Fixed */ + #define FT_fdot14ToFixed( x ) \ + (((FT_Fixed)((FT_Int16)(x))) << 2 ) + + static FT_Error + cff_vstore_load( CFF_VStoreRec* vstore, + FT_Stream stream, + FT_ULong base_offset, + FT_ULong offset ) + { + FT_Memory memory = stream->memory; + FT_Error error = FT_THROW( Invalid_File_Format ); + FT_UInt i,j; + + /* no offset means no vstore to parse */ + if ( offset ) + { + FT_UInt vsSize; /* currently unused */ + FT_UInt vsOffset; + FT_UInt format; + FT_ULong regionListOffset; + FT_ULong dataOffsetArrayOffset; + FT_ULong varDataOffset; + + /* we need to parse the table to determine its size */ + if ( FT_STREAM_SEEK( base_offset + offset ) || + FT_READ_USHORT( vsSize ) ) + goto Exit; + + /* actual variation store begins after the length */ + vsOffset = FT_STREAM_POS(); + + /* check the header */ + if ( FT_READ_USHORT( format ) ) + goto Exit; + if ( format != 1 ) + { + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* read top level fields */ + if ( FT_READ_ULONG( regionListOffset ) || + FT_READ_USHORT( vstore->dataCount ) ) + goto Exit; + + /* save position of item variation data offsets */ + /* we'll parse region list first, then come back */ + dataOffsetArrayOffset = FT_STREAM_POS(); + + /* parse regionList and axisLists*/ + if ( FT_STREAM_SEEK( vsOffset + regionListOffset ) || + FT_READ_USHORT( vstore->axisCount ) || + FT_READ_USHORT( vstore->regionCount ) ) + goto Exit; + + if ( FT_NEW_ARRAY( vstore->varRegionList, vstore->regionCount ) ) + goto Exit; + + for ( i=0; iregionCount; i++ ) + { + CFF_VarRegion* region = &vstore->varRegionList[i]; + if ( FT_NEW_ARRAY( region->axisList, vstore->axisCount ) ) + goto Exit; + for ( j=0; jaxisCount; j++ ) + { + CFF_AxisCoords* axis = ®ion->axisList[j]; + FT_Int16 start14, peak14, end14; + if ( FT_READ_SHORT( start14 ) || + FT_READ_SHORT( peak14 ) || + FT_READ_SHORT( end14 ) ) + goto Exit; + axis->startCoord = FT_fdot14ToFixed( start14 ); + axis->peakCoord = FT_fdot14ToFixed( peak14 ); + axis->endCoord = FT_fdot14ToFixed( end14 ); + } + } + + /* re-position to parse varData and regionIndices */ + if ( FT_STREAM_SEEK( dataOffsetArrayOffset ) ) + goto Exit; + + if ( FT_NEW_ARRAY( vstore->varData, vstore->dataCount ) ) + goto Exit; + + for ( i=0; idataCount; i++ ) + { + FT_UInt itemCount, shortDeltaCount; + CFF_VarData* data = &vstore->varData[i]; + + if ( FT_READ_ULONG( varDataOffset ) ) + goto Exit; + + if ( FT_STREAM_SEEK( vsOffset + varDataOffset ) ) + goto Exit; + + /* ignore these two values because CFF2 has no delta sets */ + if ( FT_READ_USHORT( itemCount ) || + FT_READ_USHORT( shortDeltaCount ) ) + goto Exit; + + /* Note: just record values; consistency is checked later */ + /* by cf2_buildBlendVector when it consumes vstore */ + + if ( FT_READ_USHORT( data->regionIdxCount ) ) + goto Exit; + if ( FT_NEW_ARRAY( data->regionIndices, data->regionIdxCount ) ) + goto Exit; + for ( j=0; jregionIdxCount; j++ ) + { + if ( FT_READ_USHORT( data->regionIndices[j] ) ) + goto Exit; + } + } + + } + error = FT_Err_Ok; + + Exit: + if ( error ) + cff_vstore_done( vstore, memory ); + return error; + } + static void cff_encoding_done( CFF_Encoding encoding ) { @@ -1442,7 +1595,8 @@ FT_Stream stream, FT_Int face_index, CFF_Font font, - FT_Bool pure_cff ) + FT_Bool pure_cff, + FT_Bool cff2 ) { static const FT_Frame_Field cff_header_fields[] = { @@ -1478,7 +1632,7 @@ goto Exit; /* check format */ - if ( font->version_major != 1 || + if ( font->version_major != ( cff2 ? 2 : 1 ) || font->header_size < 4 || font->absolute_offsize > 4 ) { @@ -1492,8 +1646,9 @@ goto Exit; /* read the name, top dict, string and global subrs index */ - if ( FT_SET_ERROR( cff_index_init( &font->name_index, - stream, 0 ) ) || + /* CFF2 does not contain a name index */ + if ( ( !cff2 && FT_SET_ERROR( cff_index_init( &font->name_index, + stream, 0 ) ) ) || FT_SET_ERROR( cff_index_init( &font->font_dict_index, stream, 0 ) ) || FT_SET_ERROR( cff_index_init( &string_index, @@ -1636,7 +1791,7 @@ goto Exit; /* read the Charset and Encoding tables if available */ - if ( font->num_glyphs > 0 ) + if ( !cff2 && font->num_glyphs > 0 ) { FT_Bool invert = FT_BOOL( dict->cid_registry != 0xFFFFU && pure_cff ); @@ -1660,6 +1815,14 @@ } } + /* read the Variation Store if available */ + error = cff_vstore_load( &font->vstore, + stream, + base_offset, + dict->vstore_offset ); + if ( error ) + goto Exit; + /* get the font name (/CIDFontName for CID-keyed fonts, */ /* /FontName otherwise) */ font->font_name = cff_index_get_name( font, subfont_index ); @@ -1696,6 +1859,7 @@ cff_encoding_done( &font->encoding ); cff_charset_done( &font->charset, font->stream ); + cff_vstore_done( &font->vstore, memory ); cff_subfont_done( memory, &font->top_font ); diff --git a/src/cff/cffload.h b/src/cff/cffload.h index 1dd07baf1..992e5b5f5 100644 --- a/src/cff/cffload.h +++ b/src/cff/cffload.h @@ -64,7 +64,8 @@ FT_BEGIN_HEADER FT_Stream stream, FT_Int face_index, CFF_Font font, - FT_Bool pure_cff ); + FT_Bool pure_cff, + FT_Bool cff2 ); FT_LOCAL( void ) cff_font_done( CFF_Font font ); diff --git a/src/cff/cffobjs.c b/src/cff/cffobjs.c index 0f0769677..b246f753e 100644 --- a/src/cff/cffobjs.c +++ b/src/cff/cffobjs.c @@ -491,6 +491,7 @@ FT_Service_PsCMaps psnames; PSHinter_Service pshinter; FT_Bool pure_cff = 1; + FT_Bool cff2 = 0; FT_Bool sfnt_format = 0; FT_Library library = cffface->driver->root.library; @@ -554,9 +555,22 @@ } /* now load the CFF part of the file */ - error = face->goto_table( face, TTAG_CFF, stream, 0 ); +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + /* give priority to CFF2 */ + error = face->goto_table( face, TTAG_CFF2, stream, 0 ); + if ( !error ) + { + cff2 = 1; + face->isCFF2 = cff2; + } + if ( FT_ERR_EQ( error, Table_Missing ) ) +#endif + { + error = face->goto_table( face, TTAG_CFF, stream, 0 ); + } if ( error ) goto Exit; + } else { @@ -579,7 +593,7 @@ goto Exit; face->extra.data = cff; - error = cff_font_load( library, stream, face_index, cff, pure_cff ); + error = cff_font_load( library, stream, face_index, cff, pure_cff, cff2 ); if ( error ) goto Exit; @@ -1079,6 +1093,11 @@ FT_FREE( face->extra.data ); } } +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + tt_done_blend( memory, face->blend ); + face->blend = NULL; +#endif + } diff --git a/src/cff/cffparse.c b/src/cff/cffparse.c index a4f986b67..f007f858c 100644 --- a/src/cff/cffparse.c +++ b/src/cff/cffparse.c @@ -782,6 +782,32 @@ return error; } + /* TODO: replace this test code with doBlend */ + static FT_Error + cff_parse_blend( CFF_Parser parser ) + { + FT_UInt num_args = (FT_UInt)( parser->top - parser->stack ); + FT_UInt numBlends; + FT_Error error; + + error = FT_ERR( Stack_Underflow ); + FT_TRACE1(( " cff_parse_blend\n" )); + + if ( parser->top >= parser->stack + 1 ) /* at least one operand */ + { + /* top of stack gives number of blends */ + numBlends = (FT_UInt)cff_parse_num( parser->top - 1 ); + + if ( numBlends < num_args ) + { + /* for testing, just reduce stack to first numBlends values */ + parser->top = parser->stack + numBlends; + + error = FT_Err_Ok; + } + } + return error; + } #define CFF_FIELD_NUM( code, name, id ) \ CFF_FIELD( code, name, id, cff_kind_num ) @@ -817,6 +843,15 @@ 0, 0 \ }, +#define CFF_FIELD_BLEND( code, id ) \ + { \ + cff_kind_blend, \ + code | CFFCODE, \ + 0, 0, \ + cff_parse_blend, \ + 0, 0 \ + }, + #define CFF_FIELD( code, name, id, kind ) \ { \ kind, \ @@ -860,6 +895,16 @@ id \ }, +#define CFF_FIELD_BLEND( code, id ) \ + { \ + cff_kind_blend, \ + code | CFFCODE, \ + 0, 0, \ + cff_parse_blend, \ + 0, 0, \ + id \ + }, + #define CFF_FIELD( code, name, id, kind ) \ { \ kind, \ @@ -1386,7 +1431,7 @@ } break; - default: /* callback */ + default: /* callback or blend */ error = field->reader( parser ); if ( error ) goto Exit; @@ -1400,7 +1445,8 @@ Found: /* clear stack */ - parser->top = parser->stack; + if ( field->kind != cff_kind_blend ) + parser->top = parser->stack; } p++; } diff --git a/src/cff/cffparse.h b/src/cff/cffparse.h index a95970edc..0134efbf0 100644 --- a/src/cff/cffparse.h +++ b/src/cff/cffparse.h @@ -28,7 +28,7 @@ FT_BEGIN_HEADER -#define CFF_MAX_STACK_DEPTH 96 +#define CFF_MAX_STACK_DEPTH 193 #define CFF_CODE_TOPDICT 0x1000 #define CFF_CODE_PRIVATE 0x2000 @@ -77,6 +77,7 @@ FT_BEGIN_HEADER cff_kind_bool, cff_kind_delta, cff_kind_callback, + cff_kind_blend, cff_kind_max /* do not remove */ }; diff --git a/src/cff/cffpic.h b/src/cff/cffpic.h index bed6b35a8..4737b9e38 100644 --- a/src/cff/cffpic.h +++ b/src/cff/cffpic.h @@ -32,6 +32,7 @@ #define CFF_SERVICE_CID_INFO_GET cff_service_cid_info #define CFF_SERVICE_PROPERTIES_GET cff_service_properties #define CFF_SERVICES_GET cff_services +#define CFF_SERVICE_MULTI_MASTERS_GET cff_service_multi_masters #define CFF_CMAP_ENCODING_CLASS_REC_GET cff_cmap_encoding_class_rec #define CFF_CMAP_UNICODE_CLASS_REC_GET cff_cmap_unicode_class_rec #define CFF_FIELD_HANDLERS_GET cff_field_handlers diff --git a/src/cff/cfftoken.h b/src/cff/cfftoken.h index 22637c780..77e93afb5 100644 --- a/src/cff/cfftoken.h +++ b/src/cff/cfftoken.h @@ -45,6 +45,9 @@ CFF_FIELD_NUM ( 16, encoding_offset, "Encoding" ) CFF_FIELD_NUM ( 17, charstrings_offset, "CharStrings" ) CFF_FIELD_CALLBACK( 18, private_dict, "Private" ) + CFF_FIELD_NUM ( 22, vsindex, "vsindex" ) + CFF_FIELD_NUM ( 24, vstore_offset, "vstore" ) + CFF_FIELD_NUM ( 25, maxstack, "maxstack" ) CFF_FIELD_NUM ( 0x114, synthetic_base, "SyntheticBase" ) CFF_FIELD_STRING ( 0x115, embedded_postscript, "PostScript" ) @@ -101,5 +104,6 @@ CFF_FIELD_NUM ( 20, default_width, "defaultWidthX" ) CFF_FIELD_NUM ( 21, nominal_width, "nominalWidthX" ) + CFF_FIELD_BLEND ( 31, "blend" ) /* END */ diff --git a/src/cff/cfftypes.h b/src/cff/cfftypes.h index 4426c7e4f..d8fc1e9ab 100644 --- a/src/cff/cfftypes.h +++ b/src/cff/cfftypes.h @@ -101,6 +101,38 @@ FT_BEGIN_HEADER } CFF_CharsetRec, *CFF_Charset; + typedef struct CFF_VarData_ + { + /* FT_UInt itemCount; not used; always zero */ + /* FT_UInt shortDeltaCount; not used; always zero */ + FT_UInt regionIdxCount; /* # regions in this var data */ + FT_UInt* regionIndices; /* array of regionCount indices */ + /* these index the varRegionList */ + } CFF_VarData; + + typedef struct CFF_AxisCoords_ /* contribution of one axis to a region */ + { + FT_Fixed startCoord; + FT_Fixed peakCoord; /* zero peak means no effect (factor = 1) */ + FT_Fixed endCoord; + } CFF_AxisCoords; + + typedef struct CFF_VarRegion_ + { + CFF_AxisCoords* axisList; /* array of axisCount records */ + } CFF_VarRegion; + + typedef struct CFF_VstoreRec_ + { + FT_UInt dataCount; + CFF_VarData* varData; /* array of dataCount records */ + /* vsindex indexes this array */ + FT_UShort axisCount; + FT_UInt regionCount; /* total # regions defined */ + CFF_VarRegion* varRegionList; + + } CFF_VStoreRec, *CFF_VStore; + typedef struct CFF_FontRecDictRec_ { @@ -151,6 +183,11 @@ FT_BEGIN_HEADER FT_UShort num_designs; FT_UShort num_axes; + /* fields for CFF2 */ + FT_UInt vsindex; + FT_ULong vstore_offset; + FT_UInt maxstack; + } CFF_FontRecDictRec, *CFF_FontRecDict; @@ -280,6 +317,8 @@ FT_BEGIN_HEADER /* since version 2.4.12 */ FT_Generic cf2_instance; + CFF_VStoreRec vstore; + } CFF_FontRec, *CFF_Font; diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 4413bbcf2..4ca8cb7c6 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -1085,10 +1085,12 @@ #ifdef FT_CONFIG_OPTION_INCREMENTAL has_outline = FT_BOOL( face->root.internal->incremental_interface != 0 || tt_face_lookup_table( face, TTAG_glyf ) != 0 || - tt_face_lookup_table( face, TTAG_CFF ) != 0 ); + tt_face_lookup_table( face, TTAG_CFF ) != 0 || + tt_face_lookup_table( face, TTAG_CFF2 )); #else has_outline = FT_BOOL( tt_face_lookup_table( face, TTAG_glyf ) != 0 || - tt_face_lookup_table( face, TTAG_CFF ) != 0 ); + tt_face_lookup_table( face, TTAG_CFF ) != 0 || + tt_face_lookup_table( face, TTAG_CFF2 )); #endif is_apple_sbit = 0; @@ -1331,6 +1333,9 @@ tt_face_lookup_table( face, TTAG_fvar ) != 0 && tt_face_lookup_table( face, TTAG_gvar ) != 0 ) flags |= FT_FACE_FLAG_MULTIPLE_MASTERS; + if ( tt_face_lookup_table( face, TTAG_CFF2 ) != 0 && + tt_face_lookup_table( face, TTAG_fvar ) != 0 ) + flags |= FT_FACE_FLAG_MULTIPLE_MASTERS; #endif root->face_flags = flags; diff --git a/src/sfnt/ttmtx.c b/src/sfnt/ttmtx.c index 186f873da..a54472842 100644 --- a/src/sfnt/ttmtx.c +++ b/src/sfnt/ttmtx.c @@ -22,6 +22,10 @@ #include FT_TRUETYPE_TAGS_H #include "ttmtx.h" +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT +#include "../truetype/ttgxvar.h" +#endif + #include "sferrors.h" @@ -274,6 +278,12 @@ *abearing = 0; *aadvance = 0; } +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + /* TODO: handle VVAR and LSB */ + if ( !vertical ) + tt_adjust_advance( face, gindex, aadvance ); +#endif + } diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c index 2659b9c9b..ce0be7ffe 100644 --- a/src/truetype/ttdriver.c +++ b/src/truetype/ttdriver.c @@ -449,7 +449,9 @@ (FT_Set_MM_Design_Func) NULL, /* set_mm_design */ (FT_Set_MM_Blend_Func) TT_Set_MM_Blend, /* set_mm_blend */ (FT_Get_MM_Var_Func) TT_Get_MM_Var, /* get_mm_var */ - (FT_Set_Var_Design_Func)TT_Set_Var_Design ) /* set_var_design */ + (FT_Set_Var_Design_Func)TT_Set_Var_Design, /* set_var_design */ + (FT_Get_Var_Design_Func)TT_Get_Var_Design, /* get_var_design */ + (FT_Get_Var_Blend_Func) TT_Get_Var_Blend ) /* get_var_blend */ #endif 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(); } + /*************************************************************************/ + /* */ + /* */ + /* ft_var_load_hvar */ + /* */ + /* */ + /* 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. */ + /* */ + /* */ + /* 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; iregionCount; i++ ) + { + GX_AxisCoords axisCoords; + + if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, itemStore->axisCount ) ) + goto Exit; + + axisCoords = itemStore->varRegionList[i].axisList; + + for ( j=0; jaxisCount; 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; idataCount; 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; jregionCount; 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; jitemCount * hvarData->regionCount; ) + { + for ( k=0; kdeltaSet[j] = delta; + } + for ( ; kregionCount; 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; imapCount; i++ ) + { + FT_UInt mapData = 0; + FT_UInt outerIndex, innerIndex; + + /* read map data one unsigned byte at a time, big endian */ + for ( j=0; j> 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; + } + + /*************************************************************************/ + /* */ + /* */ + /* tt_adjust_advance */ + /* */ + /* */ + /* Apply HVAR adjustment to advance width of gindex */ + /* */ + /* */ + /* gindex :: The glyph */ + /* */ + /* 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; masterregionCount; 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; jblend->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 @@ /* */ /* 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 @@ /* */ /* 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 @@ /* */ /* 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 @@ /* */ /* 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; ihvar_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; ihvar_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 ); diff --git a/src/truetype/ttgxvar.h b/src/truetype/ttgxvar.h index aa8f6ea59..956000c76 100644 --- a/src/truetype/ttgxvar.h +++ b/src/truetype/ttgxvar.h @@ -61,6 +61,64 @@ FT_BEGIN_HEADER } GX_AVarSegmentRec, *GX_AVarSegment; + /*************************************************************************/ + /* */ + /* */ + /* GX_HVarRec */ + /* */ + /* */ + /* Data from the `HVAR' table. */ + /* */ + /* See similar variation store structures in cfftypes.h */ + /* */ + typedef struct GX_HVarData_ + { + FT_UInt itemCount; /* # delta sets per item */ + FT_UInt regionCount; /* # regions in this var data */ + FT_UInt* regionIndices; /* array of regionCount indices */ + /* these index the varRegionList */ + FT_Short* deltaSet; /* array of itemCount deltas */ + /* use innerIndex for this array */ + } GX_HVarDataRec, *GX_HVarData; + + typedef struct GX_AxisCoords_ /* contribution of one axis to a region */ + { + FT_Fixed startCoord; + FT_Fixed peakCoord; /* zero means no effect (factor = 1) */ + FT_Fixed endCoord; + } GX_AxisCoordsRec, *GX_AxisCoords; + + typedef struct GX_HVarRegion_ + { + GX_AxisCoords axisList; /* array of axisCount records */ + } GX_HVarRegionRec, *GX_HVarRegion; + + typedef struct GX_HVStoreRec_ /* HVAR item variation store */ + { + FT_UInt dataCount; + GX_HVarData varData; /* array of dataCount records */ + /* use outerIndex for this array */ + FT_UShort axisCount; + FT_UInt regionCount; /* total # regions defined */ + GX_HVarRegion varRegionList; + + } GX_HVStoreRec, *GX_HVStore; + + typedef struct GX_WidthMapRec_ + { + FT_UInt mapCount; + FT_UInt* outerIndex; /* indices to item var data */ + FT_UInt* innerIndex; /* indices to delta set */ + } GX_WidthMapRec, *GX_WidthMap; + + typedef struct GX_HVarRec_ + { + GX_HVStoreRec itemStore; /* Item Variation Store */ + GX_WidthMapRec widthMap; /* Advance Width Mapping */ + /* GX_LSBMap LsbMap; Not implemented */ + /* GX_RSBMap RsbMap; Not implemented */ + } GX_HVarTableRec, *GX_HVarTable; + /*************************************************************************/ /* */ /* */ @@ -89,6 +147,9 @@ FT_BEGIN_HEADER FT_Bool avar_checked; GX_AVarSegment avar_segment; + FT_Bool hvar_checked; + GX_HVarTable hvar_table; + FT_UInt tuplecount; /* shared tuples in `gvar' */ FT_Fixed* tuplecoords; /* tuplecoords[tuplecount][num_axis] */ @@ -143,20 +204,29 @@ FT_BEGIN_HEADER #define TTAG_slnt FT_MAKE_TAG( 's', 'l', 'n', 't' ) - FT_LOCAL( FT_Error ) + FT_EXPORT( FT_Error ) TT_Set_MM_Blend( TT_Face face, FT_UInt num_coords, FT_Fixed* coords ); - FT_LOCAL( FT_Error ) + FT_EXPORT( FT_Error ) TT_Set_Var_Design( TT_Face face, FT_UInt num_coords, FT_Fixed* coords ); - FT_LOCAL( FT_Error ) + FT_EXPORT( FT_Error ) TT_Get_MM_Var( TT_Face face, FT_MM_Var* *master ); + FT_EXPORT( FT_Error ) + TT_Get_Var_Design( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + FT_EXPORT( FT_Error ) + TT_Get_Var_Blend( TT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); FT_LOCAL( FT_Error ) tt_face_vary_cvt( TT_Face face, @@ -169,8 +239,12 @@ FT_BEGIN_HEADER FT_Outline* outline, FT_UInt n_points ); + FT_EXPORT( void ) + tt_adjust_advance( TT_Face face, + FT_UInt gindex, + FT_UShort *aadvance ); - FT_LOCAL( void ) + FT_EXPORT( void ) tt_done_blend( FT_Memory memory, GX_Blend blend );