diff --git a/src/cff/cf2font.c b/src/cff/cf2font.c index 87b0fab6f..6c3da5cfa 100644 --- a/src/cff/cf2font.c +++ b/src/cff/cf2font.c @@ -366,9 +366,6 @@ 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; @@ -405,25 +402,23 @@ if ( hasVariations ) { - if ( font->lenBlendVector == 0 ) - needExtraSetup = TRUE; /* a blend vector is required */ - + /* see if Private DICT in this subfont needs to be reparsed */ /* 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 ) ) + if ( cff_blend_check_vector( &subFont->blend, + subFont->private_dict.vsindex, + lenNormalizedV, normalizedV ) ) { - font->lastVsindex = subFont->font_dict.vsindex; - /* vectors will be copied below, during ExtraSetup */ + /* blend has changed, reparse */ + cff_load_private_dict( decoder->cff, subFont, lenNormalizedV, normalizedV ); needExtraSetup = TRUE; } + /* store vector inputs for blends in charstring */ + font->blend.font = subFont->blend.font; /* copy from subfont */ + font->vsindex = subFont->private_dict.vsindex; /* initial value for charstring */ + font->lenNDV = lenNormalizedV; + font->NDV = normalizedV; } /* if ppem has changed, we need to recompute some cached data */ @@ -584,35 +579,6 @@ /* 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 a6fa31076..c86b8f83f 100644 --- a/src/cff/cf2font.h +++ b/src/cff/cf2font.h @@ -76,11 +76,10 @@ FT_BEGIN_HEADER 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) */ + CFF_BlendRec blend; /* cached charstring blend vector */ + CF2_UInt vsindex; /* current vsindex */ + CF2_UInt lenNDV; /* current length NDV or zero */ + FT_Fixed * NDV; /* ptr to current NDV or NULL */ CF2_Int unitsPerEm; diff --git a/src/cff/cf2ft.c b/src/cff/cf2ft.c index 3f8334f5f..fd95a6b2a 100644 --- a/src/cff/cf2ft.c +++ b/src/cff/cf2ft.c @@ -104,8 +104,8 @@ FT_Memory memory = font->memory; (void)memory; - FT_FREE( font->lastNormalizedVector ); - FT_FREE( font->blendVector ); + FT_FREE( font->blend.lastNDV ); + FT_FREE( font->blend.BV ); } } diff --git a/src/cff/cf2intrp.c b/src/cff/cf2intrp.c index a011e288a..978bcf4bd 100644 --- a/src/cff/cf2intrp.c +++ b/src/cff/cf2intrp.c @@ -406,22 +406,22 @@ /* store results into the first numBlends values, */ /* then pop remaining arguments. */ static void - cf2_doBlend( const CF2_Font font, + cf2_doBlend( const CFF_Blend blend, CF2_Stack opStack, CF2_UInt numBlends ) { CF2_UInt delta; CF2_UInt base; CF2_UInt i, j; - CF2_UInt numOperands = (CF2_UInt)(numBlends * font->lenBlendVector); + CF2_UInt numOperands = (CF2_UInt)(numBlends * blend->lenBV); base = cf2_stack_count( opStack ) - numOperands; delta = base + numBlends; for ( i = 0; i < numBlends; i++ ) { - const CF2_Fixed * weight = &font->blendVector[1]; + const CF2_Fixed * weight = &blend->BV[1]; CF2_Fixed sum = cf2_stack_getReal( opStack, i+base ); /* start with first term */ - for ( j = 1; j < font->lenBlendVector; j++ ) + for ( j = 1; j < blend->lenBV; j++ ) { sum += FT_MulFix( *weight++, cf2_stack_getReal( opStack, delta++ )); } @@ -620,9 +620,23 @@ case cf2_cmdBLEND: { - FT_UInt numBlends = (FT_UInt)cf2_stack_popInt( opStack ); + FT_UInt numBlends; + FT_TRACE4(( " blend\n" )); - cf2_doBlend( font, opStack, numBlends ); + + if ( !font->isCFF2 ) + break; /* clear stack & ignore */ + + /* check cached blend vector */ + if ( cff_blend_check_vector( &font->blend, font->vsindex, font->lenNDV, font->NDV ) ) + { + cff_blend_build_vector( &font->blend, font->vsindex, font->lenNDV, font->NDV ); + } + /* do the blend */ + numBlends = (FT_UInt)cf2_stack_popInt( opStack ); + cf2_doBlend( &font->blend, opStack, numBlends ); + + font->blend.usedBV = TRUE; } continue; /* do not clear the stack */ diff --git a/src/cff/cffload.c b/src/cff/cffload.c index 112a02107..44e71f1d1 100644 --- a/src/cff/cffload.c +++ b/src/cff/cffload.c @@ -28,6 +28,7 @@ #include "cfferrs.h" +#define FT_FIXED_ONE ((FT_Fixed)0x10000) #if 1 @@ -1113,6 +1114,7 @@ { FT_Memory memory = stream->memory; FT_Error error = FT_THROW( Invalid_File_Format ); + FT_ULong * dataOffsetArray = NULL; FT_UInt i,j; /* no offset means no vstore to parse */ @@ -1122,8 +1124,6 @@ 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 ) || @@ -1147,9 +1147,15 @@ 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(); + /* make temporary copy of item variation data offsets */ + /* we'll parse region list first, then come back */ + if ( FT_NEW_ARRAY( dataOffsetArray, vstore->dataCount ) ) + goto Exit; + for ( i=0; idataCount; i++ ) + { + if ( FT_READ_ULONG( dataOffsetArray[i] ) ) + goto Exit; + } /* parse regionList and axisLists*/ if ( FT_STREAM_SEEK( vsOffset + regionListOffset ) || @@ -1179,10 +1185,7 @@ } } - /* re-position to parse varData and regionIndices */ - if ( FT_STREAM_SEEK( dataOffsetArrayOffset ) ) - goto Exit; - + /* use dataOffsetArray now to parse varData items */ if ( FT_NEW_ARRAY( vstore->varData, vstore->dataCount ) ) goto Exit; @@ -1191,10 +1194,7 @@ FT_UInt itemCount, shortDeltaCount; CFF_VarData* data = &vstore->varData[i]; - if ( FT_READ_ULONG( varDataOffset ) ) - goto Exit; - - if ( FT_STREAM_SEEK( vsOffset + varDataOffset ) ) + if ( FT_STREAM_SEEK( vsOffset + dataOffsetArray[i] ) ) goto Exit; /* ignore these two values because CFF2 has no delta sets */ @@ -1220,11 +1220,254 @@ error = FT_Err_Ok; Exit: + FT_FREE( dataOffsetArray ); if ( error ) cff_vstore_done( vstore, memory ); return error; } + /* clear blend stack (after blend values are consumed) */ + /* TODO: should do this in cff_run_parse, but subFont */ + /* ref is not available there. */ + /* allocation is not changed when stack is cleared */ + static void + cff_blend_clear( CFF_SubFont subFont ) + { + subFont->blend_top = subFont->blend_stack; + subFont->blend_used = 0; + } + + /* Blend numOperands on the stack, */ + /* store results into the first numBlends values, */ + /* then pop remaining arguments. */ + /* This is comparable to cf2_doBlend() but */ + /* the cffparse stack is different and can't be written. */ + /* Blended values are written to a different buffer, */ + /* using reserved operator 255. */ + /* Blend calculation is done in 16.16 fixed point. */ + static FT_Error + cff_blend_doBlend( CFF_SubFont subFont, + CFF_Parser parser, + FT_UInt numBlends ) + { + FT_UInt delta; + FT_UInt base; + FT_UInt i, j; + FT_UInt size; + CFF_Blend blend = &subFont->blend; + FT_Memory memory = subFont->blend.font->memory; /* for FT_REALLOC */ + FT_Error error = FT_Err_Ok; /* for FT_REALLOC */ + + FT_UInt numOperands = (FT_UInt)(numBlends * blend->lenBV); + + /* check if we have room for numBlends values at blend_top */ + size = 5 * numBlends; /* add 5 bytes per entry */ + if ( subFont->blend_used + size > subFont->blend_alloc ) + { + /* increase or allocate blend_stack and reset blend_top */ + /* prepare to append numBlends values to the buffer */ + if ( FT_REALLOC( subFont->blend_stack, subFont->blend_alloc, subFont->blend_alloc + size ) ) + goto Exit; + subFont->blend_top = subFont->blend_stack + subFont->blend_used; + subFont->blend_alloc += size; + } + subFont->blend_used += size; + + base = ( parser->top - 1 - parser->stack ) - numOperands; + delta = base + numBlends; + for ( i = 0; i < numBlends; i++ ) + { + const FT_Int32 * weight = &blend->BV[1]; + + /* convert inputs to 16.16 fixed point */ + FT_Int32 sum = cff_parse_num( parser, &parser->stack[ i+base ] ) << 16; + for ( j = 1; j < blend->lenBV; j++ ) + { + sum += FT_MulFix( *weight++, cff_parse_num( parser, &parser->stack[ delta++ ] ) << 16 ); + } + /* point parser stack to new value on blend_stack */ + parser->stack[ i+base ] = subFont->blend_top; + + /* push blended result as Type 2 5-byte fixed point number */ + /* (except that host byte order is used ) */ + /* this will not conflict with actual DICTs because 255 is a reserved opcode */ + /* in both CFF and CFF2 DICTs */ + /* see cff_parse_num() for decode of this, which rounds to an integer */ + *subFont->blend_top++ = 255; + *(( FT_UInt32 *)subFont->blend_top ) = sum; /* write 4 bytes */ + subFont->blend_top += 4; + } + /* leave only numBlends results on parser stack */ + parser->top = &parser->stack[ base + numBlends ]; + +Exit: + return error; + } + + /* compute a blend vector from variation store index and normalized vector */ + /* based on pseudo-code in OpenType Font Variations Overview */ + /* Note: lenNDV == 0 produces a default blend vector, (1,0,0,...) */ + static FT_Error + cff_blend_build_vector( CFF_Blend blend, + FT_UInt vsindex, + FT_UInt lenNDV, FT_Fixed * NDV ) + { + FT_Error error = FT_Err_Ok; /* for FT_REALLOC */ + FT_Memory memory = blend->font->memory; /* for FT_REALLOC */ + FT_UInt len; + CFF_VStore vs; + CFF_VarData* varData; + FT_UInt master; + + FT_UNUSED( lenNDV ); + FT_UNUSED( vsindex ); + + FT_ASSERT( lenNDV == 0 || NDV ); + FT_TRACE4(( "cff_blend_build_vector\n" )); + + blend->builtBV = FALSE; + + /* vs = cf2_getVStore( font->decoder ); */ + vs = &blend->font->vstore; + + /* VStore and fvar must be consistent */ + if ( lenNDV != 0 && lenNDV != vs->axisCount ) + { + FT_TRACE4(( "cff_blend_build_vector: Axis count mismatch\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + if ( vsindex >= vs->dataCount ) + { + FT_TRACE4(( "cff_blend_build_vector: vsindex out of range\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* select the item variation data structure */ + varData = &vs->varData[vsindex]; + + /* prepare buffer for the blend vector */ + len = varData->regionIdxCount + 1; /* add 1 for default component */ + if ( FT_REALLOC( blend->BV, blend->lenBV * sizeof( *blend->BV ), len * sizeof( *blend->BV )) ) + goto Exit; + blend->lenBV = len; + + /* outer loop steps through master designs to be blended */ + for ( master=0; masterBV[master] = FT_FIXED_ONE; + FT_TRACE4(( "blend vector len %d\n [ %f ", len, (double)(blend->BV[master] / 65536. ) )); + continue; + } + + /* VStore array does not include default master, so subtract one */ + idx = varData->regionIndices[master-1]; + varRegion = &vs->varRegionList[idx]; + + if ( idx >= vs->regionCount ) + { + FT_TRACE4(( "cf2_buildBlendVector: region index out of range\n" )); + error = FT_THROW( Invalid_File_Format ); + goto Exit; + } + + /* Note: lenNDV could be zero */ + /* In that case, build default blend vector (1,0,0...) */ + /* In the normal case, init each component to 1 before inner loop */ + if ( lenNDV != 0 ) + blend->BV[master] = FT_FIXED_ONE; /* default */ + + /* inner loop steps through axes in this region */ + for ( j=0; jaxisList[j]; + 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 ( NDV[j] < axis->startCoord || NDV[j] > axis->endCoord ) + axisScalar = 0; + /* calculate a proportional factor */ + else + { + if ( NDV[j] == axis->peakCoord ) + axisScalar = FT_FIXED_ONE; + else if ( NDV[j] < axis->peakCoord ) + axisScalar = FT_DivFix( NDV[j] - axis->startCoord, + axis->peakCoord - axis->startCoord ); + else + axisScalar = FT_DivFix( axis->endCoord - NDV[j], + axis->endCoord - axis->peakCoord ); + } + /* take product of all the axis scalars */ + blend->BV[master] = FT_MulFix( blend->BV[master], axisScalar ); + } + FT_TRACE4(( ", %f ", (double)blend->BV[master] / 65536. )); + } + FT_TRACE4(( "]\n" )); + + /* record the parameters used to build the blend vector */ + blend->lastVsindex = vsindex; + if ( lenNDV != 0 ) + { + /* user has set a normalized vector */ + if ( FT_REALLOC( blend->lastNDV, + blend->lenNDV * sizeof( *NDV ), + lenNDV * sizeof( *NDV )) ) + { + error = FT_THROW( Out_Of_Memory ); + goto Exit; + } + blend->lenNDV = lenNDV; + FT_MEM_COPY( blend->lastNDV, + NDV, + lenNDV * sizeof( *NDV )); + } + blend->builtBV = TRUE; + + Exit: + return error; + } + + /* lenNDV is zero for default vector */ + /* return TRUE if blend vector needs to be built */ + static FT_Bool + cff_blend_check_vector( CFF_Blend blend, + FT_UInt vsindex, + FT_UInt lenNDV, + FT_Fixed * NDV ) + { + if ( !blend->builtBV || + blend->lastVsindex != vsindex || + blend->lenNDV != lenNDV || + ( lenNDV && + memcmp( NDV, + blend->lastNDV, + lenNDV * sizeof( *NDV ) ) != 0 ) ) + { + /* need to build blend vector */ + return TRUE; + } + + return FALSE; + } + static void cff_encoding_done( CFF_Encoding encoding ) { @@ -1477,27 +1720,94 @@ } + /* parse private dictionary as separate function */ + /* first call is always from cff_face_init, so NDV has not been set */ + /* for CFF2 variation, cff_slot_load must call each time NDV changes */ + static FT_Error + cff_load_private_dict( CFF_Font font, + CFF_SubFont subfont, + FT_UInt lenNDV, FT_Fixed * NDV ) + { + FT_Error error = FT_Err_Ok; + CFF_ParserRec parser; + CFF_FontRecDict top = &subfont->font_dict; + CFF_Private priv = &subfont->private_dict; + FT_Stream stream = font->stream; + + if ( top->private_offset == 0 || top->private_size == 0 ) + goto Exit; /* no private DICT, do nothing */ + + /* store handle needed to access memory, vstore for blend */ + subfont->blend.font = font; + + /* set defaults */ + FT_MEM_ZERO( priv, sizeof ( *priv ) ); + + priv->blue_shift = 7; + priv->blue_fuzz = 1; + priv->lenIV = -1; + priv->expansion_factor = (FT_Fixed)( 0.06 * 0x10000L ); + priv->blue_scale = (FT_Fixed)( 0.039625 * 0x10000L * 1000 ); + + /* provide inputs for blend calculations */ + priv->subfont = subfont; + subfont->lenNDV = lenNDV; + subfont->NDV = NDV; + + cff_parser_init( &parser, + font->cff2 ? CFF2_CODE_PRIVATE : CFF_CODE_PRIVATE, + priv, + font->library, + top->num_designs, + top->num_axes ); + if ( FT_STREAM_SEEK( font->base_offset + top->private_offset ) || + FT_FRAME_ENTER( top->private_size ) ) + goto Exit; + + FT_TRACE4(( " private dictionary:\n" )); + error = cff_parser_run( &parser, + (FT_Byte*)stream->cursor, + (FT_Byte*)stream->limit ); + FT_FRAME_EXIT(); + if ( error ) + goto Exit; + + /* ensure that `num_blue_values' is even */ + priv->num_blue_values &= ~1; + + Exit: + cff_blend_clear( subfont ); + return error; + } + + /* There are 3 ways to call this function, distinguished by code: */ + /* CFF_CODE_TOPDICT for either a CFF Top DICT or a CFF Font DICT */ + /* CFF2_CODE_TOPDICT for CFF2 Top DICT */ + /* CFF2_CODE_FONTDICT for CFF2 Font DICT */ + static FT_Error - cff_subfont_load( CFF_SubFont font, + cff_subfont_load( CFF_SubFont subfont, CFF_Index idx, FT_UInt font_index, FT_Stream stream, FT_ULong base_offset, FT_Library library, - FT_UInt code ) + FT_UInt code, + CFF_Font font ) { FT_Error error; CFF_ParserRec parser; FT_Byte* dict = NULL; FT_ULong dict_len; - CFF_FontRecDict top = &font->font_dict; - CFF_Private priv = &font->private_dict; - FT_Bool cff2 = (code == CFF2_CODE_TOPDICT ); + CFF_FontRecDict top = &subfont->font_dict; + CFF_Private priv = &subfont->private_dict; + FT_Bool cff2 = (code == CFF2_CODE_TOPDICT || + code == CFF2_CODE_FONTDICT ); cff_parser_init( &parser, code, - &font->font_dict, + &subfont->font_dict, library, 0, 0 ); @@ -1555,40 +1865,12 @@ if ( top->cid_registry != 0xFFFFU ) goto Exit; - /* parse the private dictionary, if any */ - if ( top->private_offset && top->private_size ) - { - /* set defaults */ - FT_MEM_ZERO( priv, sizeof ( *priv ) ); - - priv->blue_shift = 7; - priv->blue_fuzz = 1; - priv->lenIV = -1; - priv->expansion_factor = (FT_Fixed)( 0.06 * 0x10000L ); - priv->blue_scale = (FT_Fixed)( 0.039625 * 0x10000L * 1000 ); - - cff_parser_init( &parser, - code == CFF2_CODE_FONTDICT ? CFF2_CODE_PRIVATE : CFF_CODE_PRIVATE, - priv, - library, - top->num_designs, - top->num_axes ); - - if ( FT_STREAM_SEEK( base_offset + font->font_dict.private_offset ) || - FT_FRAME_ENTER( font->font_dict.private_size ) ) - goto Exit; - - FT_TRACE4(( " private dictionary:\n" )); - error = cff_parser_run( &parser, - (FT_Byte*)stream->cursor, - (FT_Byte*)stream->limit ); - FT_FRAME_EXIT(); - if ( error ) - goto Exit; - - /* ensure that `num_blue_values' is even */ - priv->num_blue_values &= ~1; - } + /* parse the private dictionary, if any */ + /* CFF2 does not have a private dictionary in the Top DICT */ + /* but may have one in a Font DICT. We need to parse */ + /* the latter here in order to load any local subrs. */ + if ( cff_load_private_dict( font, subfont, 0, 0 ) ) + goto Exit; /* read the local subrs, if any */ if ( priv->local_subrs_offset ) @@ -1597,12 +1879,12 @@ priv->local_subrs_offset ) ) goto Exit; - error = cff_index_init( &font->local_subrs_index, stream, 1, cff2 ); + error = cff_index_init( &subfont->local_subrs_index, stream, 1, cff2 ); if ( error ) goto Exit; - error = cff_index_get_pointers( &font->local_subrs_index, - &font->local_subrs, NULL, NULL ); + error = cff_index_get_pointers( &subfont->local_subrs_index, + &subfont->local_subrs, NULL, NULL ); if ( error ) goto Exit; } @@ -1620,6 +1902,9 @@ { cff_index_done( &subfont->local_subrs_index ); FT_FREE( subfont->local_subrs ); + FT_FREE( subfont->blend.lastNDV ); + FT_FREE( subfont->blend.BV ); + FT_FREE( subfont->blend_stack ); } } @@ -1655,30 +1940,39 @@ FT_ZERO( font ); FT_ZERO( &string_index ); + font->library = library; font->stream = stream; font->memory = memory; font->cff2 = cff2; + base_offset = font->base_offset = FT_STREAM_POS(); dict = &font->top_font.font_dict; - base_offset = FT_STREAM_POS(); /* read CFF font header */ if ( FT_STREAM_READ_FIELDS( cff_header_fields, font ) ) goto Exit; - /* check format */ - if ( font->version_major != ( cff2 ? 2 : 1 ) || - font->header_size < 4 ) - { - FT_TRACE2(( " not a CFF font header\n" )); - error = FT_THROW( Unknown_File_Format ); - goto Exit; - } - if ( cff2 ) { + if ( font->version_major != 2 || + font->header_size < 5 ) + { + FT_TRACE2(( " not a CFF2 font header\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } if ( FT_READ_USHORT( font->top_dict_length ) ) goto Exit; } + else + { + if ( font->version_major != 1 || + font->header_size < 4 ) + { + FT_TRACE2(( " not a CFF font header\n" )); + error = FT_THROW( Unknown_File_Format ); + goto Exit; + } + } /* skip the rest of the header */ if ( FT_STREAM_SEEK( base_offset + font->header_size ) ) @@ -1767,7 +2061,8 @@ stream, base_offset, library, - cff2 ? CFF2_CODE_TOPDICT : CFF_CODE_TOPDICT ); + cff2 ? CFF2_CODE_TOPDICT : CFF_CODE_TOPDICT, + font ); if ( error ) goto Exit; @@ -1787,6 +2082,15 @@ FT_UInt idx; + /* for CFF2, read the Variation Store if available */ + /* this must follow the Top DICT parse and precede any Private DICT */ + error = cff_vstore_load( &font->vstore, + stream, + base_offset, + dict->vstore_offset ); + if ( error ) + goto Exit; + /* this is a CID-keyed font, we must now allocate a table of */ /* sub-fonts, then load each of them separately */ if ( FT_STREAM_SEEK( base_offset + dict->cid_fd_array_offset ) ) @@ -1819,7 +2123,8 @@ FT_TRACE4(( "parsing subfont %u\n", idx )); error = cff_subfont_load( sub, &fd_index, idx, stream, base_offset, library, - cff2 ? CFF2_CODE_FONTDICT : CFF_CODE_TOPDICT ); + cff2 ? CFF2_CODE_FONTDICT : CFF_CODE_TOPDICT, + font ); if ( error ) goto Fail_CID; } @@ -1882,14 +2187,6 @@ } } - /* 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 ); diff --git a/src/cff/cffload.h b/src/cff/cffload.h index 992e5b5f5..e1e778bff 100644 --- a/src/cff/cffload.h +++ b/src/cff/cffload.h @@ -22,6 +22,7 @@ #include #include "cfftypes.h" +#include "cffparse.h" FT_BEGIN_HEADER @@ -75,6 +76,28 @@ FT_BEGIN_HEADER cff_fd_select_get( CFF_FDSelect fdselect, FT_UInt glyph_index ); + FT_LOCAL( FT_Bool ) + cff_blend_check_vector( CFF_Blend blend, + FT_UInt vsindex, + FT_UInt lenNDV, + FT_Fixed * NDV ); + + FT_LOCAL( FT_Error ) + cff_blend_build_vector( CFF_Blend blend, + FT_UInt vsindex, + FT_UInt lenNDV, + FT_Fixed * NDV ); + + FT_LOCAL( void ) + cff_blend_clear( CFF_SubFont subFont ); + + FT_LOCAL( FT_Error ) + cff_blend_doBlend( CFF_SubFont subfont, + CFF_Parser parser, + FT_UInt numBlends ); + + FT_LOCAL( FT_Bool ) + cff_check_blend_vector( CFF_Blend blend ); FT_END_HEADER diff --git a/src/cff/cffparse.c b/src/cff/cffparse.c index 5a429d70c..d4a5d4b75 100644 --- a/src/cff/cffparse.c +++ b/src/cff/cffparse.c @@ -24,6 +24,7 @@ #include "cfferrs.h" #include "cffpic.h" #include "cffgload.h" +#include "cffload.h" /*************************************************************************/ @@ -403,23 +404,33 @@ /* read a number, either integer or real */ static FT_Long - cff_parse_num( FT_Byte** d ) + cff_parse_num( CFF_Parser parser, FT_Byte** d ) { - return **d == 30 ? ( cff_parse_real( d[0], d[1], 0, NULL ) >> 16 ) - : cff_parse_integer( d[0], d[1] ); + if ( **d == 30 ) + /* BCD is truncated to integer */ + return cff_parse_real( *d, parser->limit, 0, NULL ) >> 16; + else if ( **d == 255 ) + /* 16.16 fixed point is used internally for CFF2 blend results */ + /* Since these are trusted values, a limit check is not needed */ + + /* after the 255, 4 bytes are in host order */ + /* blend result is rounded to integer */ + return (FT_Long) ( *( (FT_UInt32 *) ( d[0] + 1 ) ) + 0x8000U ) >> 16; + else + return cff_parse_integer( *d, parser->limit ); } - /* read a floating point number, either integer or real */ static FT_Fixed - do_fixed( FT_Byte** d, + do_fixed( CFF_Parser parser, + FT_Byte** d, FT_Long scaling ) { if ( **d == 30 ) - return cff_parse_real( d[0], d[1], scaling, NULL ); + return cff_parse_real( *d, parser->limit, scaling, NULL ); else { - FT_Long val = cff_parse_integer( d[0], d[1] ); + FT_Long val = cff_parse_integer( *d, parser->limit ); if ( scaling ) @@ -447,19 +458,21 @@ /* read a floating point number, either integer or real */ static FT_Fixed - cff_parse_fixed( FT_Byte** d ) + cff_parse_fixed( CFF_Parser parser, + FT_Byte** d ) { - return do_fixed( d, 0 ); + return do_fixed( parser, d, 0 ); } /* read a floating point number, either integer or real, */ /* but return `10^scaling' times the number read in */ static FT_Fixed - cff_parse_fixed_scaled( FT_Byte** d, + cff_parse_fixed_scaled( CFF_Parser parser, + FT_Byte** d, FT_Long scaling ) { - return do_fixed( d, scaling ); + return do_fixed( parser, d, scaling ); } @@ -467,13 +480,14 @@ /* and return it as precise as possible -- `scaling' returns */ /* the scaling factor (as a power of 10) */ static FT_Fixed - cff_parse_fixed_dynamic( FT_Byte** d, + cff_parse_fixed_dynamic( CFF_Parser parser, + FT_Byte** d, FT_Long* scaling ) { FT_ASSERT( scaling ); if ( **d == 30 ) - return cff_parse_real( d[0], d[1], 0, scaling ); + return cff_parse_real( *d, parser->limit, 0, scaling ); else { FT_Long number; @@ -543,7 +557,7 @@ for ( i = 0; i < 6; i++ ) { - values[i] = cff_parse_fixed_dynamic( data++, &scalings[i] ); + values[i] = cff_parse_fixed_dynamic( parser, data++, &scalings[i] ); if ( values[i] ) { if ( scalings[i] > max_scaling ) @@ -640,10 +654,10 @@ if ( parser->top >= parser->stack + 4 ) { - bbox->xMin = FT_RoundFix( cff_parse_fixed( data++ ) ); - bbox->yMin = FT_RoundFix( cff_parse_fixed( data++ ) ); - bbox->xMax = FT_RoundFix( cff_parse_fixed( data++ ) ); - bbox->yMax = FT_RoundFix( cff_parse_fixed( data ) ); + bbox->xMin = FT_RoundFix( cff_parse_fixed( parser, data++ ) ); + bbox->yMin = FT_RoundFix( cff_parse_fixed( parser, data++ ) ); + bbox->xMax = FT_RoundFix( cff_parse_fixed( parser, data++ ) ); + bbox->yMax = FT_RoundFix( cff_parse_fixed( parser, data ) ); error = FT_Err_Ok; FT_TRACE4(( " [%d %d %d %d]\n", @@ -672,7 +686,7 @@ FT_Long tmp; - tmp = cff_parse_num( data++ ); + tmp = cff_parse_num( parser, data++ ); if ( tmp < 0 ) { FT_ERROR(( "cff_parse_private_dict: Invalid dictionary size\n" )); @@ -681,7 +695,7 @@ } dict->private_size = (FT_ULong)tmp; - tmp = cff_parse_num( data ); + tmp = cff_parse_num( parser, data ); if ( tmp < 0 ) { FT_ERROR(( "cff_parse_private_dict: Invalid dictionary offset\n" )); @@ -726,7 +740,7 @@ /* currently, we handle only the first argument */ if ( parser->top >= parser->stack + 5 ) { - FT_Long num_designs = cff_parse_num( parser->stack ); + FT_Long num_designs = cff_parse_num( parser, parser->stack ); if ( num_designs > 16 || num_designs < 2 ) @@ -763,11 +777,11 @@ if ( parser->top >= parser->stack + 3 ) { - dict->cid_registry = (FT_UInt)cff_parse_num( data++ ); - dict->cid_ordering = (FT_UInt)cff_parse_num( data++ ); + dict->cid_registry = (FT_UInt)cff_parse_num( parser, data++ ); + dict->cid_ordering = (FT_UInt)cff_parse_num( parser, data++ ); if ( **data == 30 ) FT_TRACE1(( "cff_parse_cid_ros: real supplement is rounded\n" )); - dict->cid_supplement = cff_parse_num( data ); + dict->cid_supplement = cff_parse_num( parser, data ); if ( dict->cid_supplement < 0 ) FT_TRACE1(( "cff_parse_cid_ros: negative supplement %d is found\n", dict->cid_supplement )); @@ -786,26 +800,31 @@ static FT_Error cff_parse_blend( CFF_Parser parser ) { - FT_UInt num_args = (FT_UInt)( parser->top - parser->stack ); + /* blend operator can only be used in a Private DICT */ + CFF_Private priv = (CFF_Private)parser->object; + CFF_SubFont subFont; + CFF_Blend blend; 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 */ + if ( !priv || !priv->subfont ) { - /* top of stack gives number of blends */ - numBlends = (FT_UInt)cff_parse_num( parser->top - 1 ); + error = FT_ERR( Invalid_File_Format ); + goto Exit; + } + subFont = priv->subfont; + blend = &subFont->blend; - if ( numBlends < num_args ) - { - /* for testing, just reduce stack to first numBlends values */ - parser->top = parser->stack + numBlends; + if ( cff_blend_check_vector( blend, priv->vsindex, subFont->lenNDV, subFont->NDV ) ) + cff_blend_build_vector( blend, priv->vsindex, subFont->lenNDV, subFont->NDV ); - error = FT_Err_Ok; - } - } + numBlends = (FT_UInt)cff_parse_num( parser, parser->top - 1 ); + + error = cff_blend_doBlend(subFont, parser, numBlends ); + Exit: return error; } @@ -1109,8 +1128,10 @@ { FT_UInt v = *p; - - if ( v >= 27 && v != 31 ) + /* opcode 31 is legacy MM T2 operator, not a number */ + /* opcode 255 is reserved and should not appear in fonts */ + /* it is used internally for CFF2 blends */ + if ( v >= 27 && v != 31 && v != 255 ) { /* it's a number; we will push its position on the stack */ if ( parser->top - parser->stack >= CFF_MAX_STACK_DEPTH ) @@ -1322,15 +1343,15 @@ case cff_kind_bool: case cff_kind_string: case cff_kind_num: - val = cff_parse_num( parser->stack ); + val = cff_parse_num( parser, parser->stack ); goto Store_Number; case cff_kind_fixed: - val = cff_parse_fixed( parser->stack ); + val = cff_parse_fixed( parser, parser->stack ); goto Store_Number; case cff_kind_fixed_thousand: - val = cff_parse_fixed_scaled( parser->stack, 3 ); + val = cff_parse_fixed_scaled( parser, parser->stack, 3 ); Store_Number: switch ( field->size ) @@ -1399,7 +1420,7 @@ val = 0; while ( num_args > 0 ) { - val += cff_parse_num( data++ ); + val += cff_parse_num( parser, data++ ); switch ( field->size ) { case (8 / FT_CHAR_BIT): @@ -1442,8 +1463,9 @@ Found: /* clear stack */ + /* TODO: could clear blend stack here, but we don't have access to subFont */ if ( field->kind != cff_kind_blend ) - parser->top = parser->stack; + parser->top = parser->stack; } p++; } diff --git a/src/cff/cfftypes.h b/src/cff/cfftypes.h index c6ed4df19..912ee5698 100644 --- a/src/cff/cfftypes.h +++ b/src/cff/cfftypes.h @@ -134,6 +134,24 @@ FT_BEGIN_HEADER } CFF_VStoreRec, *CFF_VStore; + /* forward reference */ + typedef struct CFF_FontRec_ *CFF_Font; + + typedef struct CFF_BlendRec_ + { + /* object to manage one cached blend vector */ + /* Note: NDV is long 32/64 bit, while BV is 16.16 (FT_Int32) */ + FT_Bool builtBV; /* blendV has been built */ + FT_Bool usedBV; /* blendV has been used */ + CFF_Font font; /* top level font struct */ + FT_UInt lastVsindex; /* last vsindex used */ + FT_UInt lenNDV; /* normDV length (aka numAxes) */ + FT_Fixed * lastNDV; /* last NDV used */ + FT_UInt lenBV; /* BlendV length (aka numMasters) */ + FT_Int32 * BV; /* current blendV (per DICT/glyph)*/ + + } CFF_BlendRec, *CFF_Blend; + typedef struct CFF_FontRecDictRec_ { @@ -185,13 +203,15 @@ FT_BEGIN_HEADER FT_UShort num_axes; /* fields for CFF2 */ - FT_UInt vsindex; FT_ULong vstore_offset; FT_UInt maxstack; } CFF_FontRecDictRec, *CFF_FontRecDict; + /* forward reference */ + typedef struct CFF_SubFontRec_ *CFF_SubFont; + typedef struct CFF_PrivateRec_ { FT_Byte num_blue_values; @@ -225,7 +245,8 @@ FT_BEGIN_HEADER FT_Pos nominal_width; /* fields for CFF2 */ - FT_UInt vsindex; + FT_UInt vsindex; + CFF_SubFont subfont; } CFF_PrivateRec, *CFF_Private; @@ -254,6 +275,23 @@ FT_BEGIN_HEADER CFF_FontRecDictRec font_dict; CFF_PrivateRec private_dict; + /* fields for CFF2 */ + CFF_BlendRec blend; /* current blend vector */ + FT_UInt lenNDV; /* current length NDV or zero */ + FT_Fixed * NDV; /* ptr to current NDV or NULL */ + + /* blend_stack is a writable buffer to hold blend results */ + /* this buffer is to the side of the normal cff parser stack */ + /* cff_parse_blend()/cff_blend_doBlend() pushes blend results here */ + /* the normal stack then points to these values instead of the DICT */ + /* because all other operators in Private DICT clear the stack, */ + /* blend_stack could be cleared at each operator other than blend */ + /* blended values are stored as 5-byte fixed point */ + FT_Byte * blend_stack; /* base of stack allocation */ + FT_Byte * blend_top; /* first empty slot */ + FT_UInt blend_used; /* number of bytes in use */ + FT_UInt blend_alloc; /* number of bytes allocated */ + CFF_IndexRec local_subrs_index; FT_Byte** local_subrs; /* array of pointers into Local Subrs INDEX data */ @@ -265,8 +303,10 @@ FT_BEGIN_HEADER typedef struct CFF_FontRec_ { + FT_Library library; FT_Stream stream; - FT_Memory memory; + FT_Memory memory; /* TODO: take this from stream->memory? */ + FT_ULong base_offset; /* offset to start of CFF */ FT_UInt num_faces; FT_UInt num_glyphs; @@ -323,7 +363,7 @@ FT_BEGIN_HEADER /* since version 2.4.12 */ FT_Generic cf2_instance; - CFF_VStoreRec vstore; + CFF_VStoreRec vstore; /* parsed vstore structure */ } CFF_FontRec, *CFF_Font;