[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.
This commit is contained in:
Dave Arnold 2016-11-01 10:45:45 -07:00
parent 54d9993505
commit 776a712be8
8 changed files with 542 additions and 181 deletions

View File

@ -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 */
}

View File

@ -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;

View File

@ -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 );
}
}

View File

@ -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 */

View File

@ -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_subfont_load( CFF_SubFont font,
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 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 );

View File

@ -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

View File

@ -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 );
if ( numBlends < num_args )
{
/* for testing, just reduce stack to first numBlends values */
parser->top = parser->stack + numBlends;
error = FT_Err_Ok;
}
error = FT_ERR( Invalid_File_Format );
goto Exit;
}
subFont = priv->subfont;
blend = &subFont->blend;
if ( cff_blend_check_vector( blend, priv->vsindex, subFont->lenNDV, subFont->NDV ) )
cff_blend_build_vector( blend, priv->vsindex, subFont->lenNDV, subFont->NDV );
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++;
}

View File

@ -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;