freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[freetype2] OpenType-1.8 776a712 1/2: [cff] Add CFF2 blend support to cf


From: Dave Arnold
Subject: [freetype2] OpenType-1.8 776a712 1/2: [cff] Add CFF2 blend support to cff parser.
Date: Wed, 2 Nov 2016 22:41:12 +0000 (UTC)

branch: OpenType-1.8
commit 776a712be821d744edd82bab932325b382d3799c
Author: Dave Arnold <address@hidden>
Commit: Dave Arnold <address@hidden>

    [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.
---
 src/cff/cf2font.c  |   56 ++-----
 src/cff/cf2font.h  |    9 +-
 src/cff/cf2ft.c    |    4 +-
 src/cff/cf2intrp.c |   26 ++-
 src/cff/cffload.c  |  449 +++++++++++++++++++++++++++++++++++++++++++---------
 src/cff/cffload.h  |   23 +++
 src/cff/cffparse.c |  106 ++++++++-----
 src/cff/cfftypes.h |   48 +++++-
 8 files changed, 541 insertions(+), 180 deletions(-)

diff --git a/src/cff/cf2font.c b/src/cff/cf2font.c
index 87b0fab..6c3da5c 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 a6fa310..c86b8f8 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 3f8334f..fd95a6b 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 a011e28..978bcf4 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 112a021..44e71f1 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 992e5b5..e1e778b 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 5a429d7..d4a5d4b 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 c6ed4df..912ee56 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;
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]