summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Arnold <darnold@adobe.com>2016-11-01 10:45:45 -0700
committerDave Arnold <darnold@adobe.com>2016-11-02 15:30:36 -0700
commit776a712be821d744edd82bab932325b382d3799c (patch)
tree3452f65fe0487314503c53ac3753de25101a2325
parent54d9993505557af909d92291e1db549a2d4643e8 (diff)
downloadfreetype2-776a712be821d744edd82bab932325b382d3799c.tar.gz
[cff] Add CFF2 blend support to cff parser.
* src/cff/cf2font.c (cf2_font_setup): before rendering a glyph, check blend vector and reparse private DICT if it has changed. * src/cff/cf2intrp.c (cf2_interpT2CharString): check blend vector and build blend vector if needed at cf2_doBlend. * src/cff/cffload.c (cff_vstore_load): fix bug parsing vstore with > 1 data item. (cff_load_private_dict): factor out private dict parsing so we can reparse when vector changes. Add functions for handling CFF_Blend object. (cff_blend_clear): clear blend stack (cff_blend_check_vector): test if inputs have changed and blend vector needs rebuilding. (cff_blend_build_vector): construct blend vector from design vector using algorithm in OpenType Font Variations Overview. (cff_blend_doBlend): compute blended array, same as in cf2_intrp.c but with parser blend_stack instead of charstring operand stack. * src/cff/cffparse.c Add blend_stack alongside parser stack, so we have a place to write blend results. (cff_parse_num): add internal number type, opcode 255, for use with blend_stack. Change limit check on several number parsing functions, since stack is no longer contiguous. (cff_parse_blend): check blend vector and re-build it if needed. (cff_parser_run): ignore opcode 255 if it occurs in a font. * src/cff/cfftypes.h (CFF_Blend): add object data for cff_blend_* functions. (CFF_SubFont): add CFF_Blend and blend_stack.
-rw-r--r--src/cff/cf2font.c56
-rw-r--r--src/cff/cf2font.h9
-rw-r--r--src/cff/cf2ft.c4
-rw-r--r--src/cff/cf2intrp.c26
-rw-r--r--src/cff/cffload.c449
-rw-r--r--src/cff/cffload.h23
-rw-r--r--src/cff/cffparse.c106
-rw-r--r--src/cff/cfftypes.h48
8 files changed, 541 insertions, 180 deletions
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; i<vstore->dataCount; 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; master<len; master++ )
+ {
+ FT_UInt j;
+ FT_UInt idx;
+ CFF_VarRegion* varRegion;
+
+ /* default factor is always one */
+ if ( master == 0 )
+ {
+ blend->BV[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; j<lenNDV; j++ )
+ {
+ CFF_AxisCoords* axis = &varRegion->axisList[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 <ft2build.h>
#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;