(root)/
freetype-2.13.2/
src/
psaux/
psfont.c
       1  /****************************************************************************
       2   *
       3   * psfont.c
       4   *
       5   *   Adobe's code for font instances (body).
       6   *
       7   * Copyright 2007-2014 Adobe Systems Incorporated.
       8   *
       9   * This software, and all works of authorship, whether in source or
      10   * object code form as indicated by the copyright notice(s) included
      11   * herein (collectively, the "Work") is made available, and may only be
      12   * used, modified, and distributed under the FreeType Project License,
      13   * LICENSE.TXT.  Additionally, subject to the terms and conditions of the
      14   * FreeType Project License, each contributor to the Work hereby grants
      15   * to any individual or legal entity exercising permissions granted by
      16   * the FreeType Project License and this section (hereafter, "You" or
      17   * "Your") a perpetual, worldwide, non-exclusive, no-charge,
      18   * royalty-free, irrevocable (except as stated in this section) patent
      19   * license to make, have made, use, offer to sell, sell, import, and
      20   * otherwise transfer the Work, where such license applies only to those
      21   * patent claims licensable by such contributor that are necessarily
      22   * infringed by their contribution(s) alone or by combination of their
      23   * contribution(s) with the Work to which such contribution(s) was
      24   * submitted.  If You institute patent litigation against any entity
      25   * (including a cross-claim or counterclaim in a lawsuit) alleging that
      26   * the Work or a contribution incorporated within the Work constitutes
      27   * direct or contributory patent infringement, then any patent licenses
      28   * granted to You under this License for that Work shall terminate as of
      29   * the date such litigation is filed.
      30   *
      31   * By using, modifying, or distributing the Work you indicate that you
      32   * have read and understood the terms and conditions of the
      33   * FreeType Project License as well as those provided in this section,
      34   * and you accept them fully.
      35   *
      36   */
      37  
      38  
      39  #include <freetype/internal/ftcalc.h>
      40  
      41  #include "psft.h"
      42  
      43  #include "psglue.h"
      44  #include "psfont.h"
      45  #include "pserror.h"
      46  #include "psintrp.h"
      47  
      48  
      49    /* Compute a stem darkening amount in character space. */
      50    static void
      51    cf2_computeDarkening( CF2_Fixed   emRatio,
      52                          CF2_Fixed   ppem,
      53                          CF2_Fixed   stemWidth,
      54                          CF2_Fixed*  darkenAmount,
      55                          CF2_Fixed   boldenAmount,
      56                          FT_Bool     stemDarkened,
      57                          FT_Int*     darkenParams )
      58    {
      59      /*
      60       * Total darkening amount is computed in 1000 unit character space
      61       * using the modified 5 part curve as Adobe's Avalon rasterizer.
      62       * The darkening amount is smaller for thicker stems.
      63       * It becomes zero when the stem is thicker than 2.333 pixels.
      64       *
      65       * By default, we use
      66       *
      67       *   darkenAmount = 0.4 pixels   if scaledStem <= 0.5 pixels,
      68       *   darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels,
      69       *   darkenAmount = 0 pixel      if scaledStem >= 2.333 pixels,
      70       *
      71       * and piecewise linear in-between:
      72       *
      73       *
      74       *   darkening
      75       *       ^
      76       *       |
      77       *       |      (x1,y1)
      78       *       |--------+
      79       *       |         \
      80       *       |          \
      81       *       |           \          (x3,y3)
      82       *       |            +----------+
      83       *       |        (x2,y2)         \
      84       *       |                         \
      85       *       |                          \
      86       *       |                           +-----------------
      87       *       |                         (x4,y4)
      88       *       +--------------------------------------------->   stem
      89       *                                                       thickness
      90       *
      91       *
      92       * This corresponds to the following values for the
      93       * `darkening-parameters' property:
      94       *
      95       *   (x1, y1) = (500, 400)
      96       *   (x2, y2) = (1000, 275)
      97       *   (x3, y3) = (1667, 275)
      98       *   (x4, y4) = (2333, 0)
      99       *
     100       */
     101  
     102      /* Internal calculations are done in units per thousand for */
     103      /* convenience. The x axis is scaled stem width in          */
     104      /* thousandths of a pixel. That is, 1000 is 1 pixel.        */
     105      /* The y axis is darkening amount in thousandths of a pixel.*/
     106      /* In the code, below, dividing by ppem and                 */
     107      /* adjusting for emRatio converts darkenAmount to character */
     108      /* space (font units).                                      */
     109      CF2_Fixed  stemWidthPer1000, scaledStem;
     110      FT_Int     logBase2;
     111  
     112  
     113      *darkenAmount = 0;
     114  
     115      if ( boldenAmount == 0 && !stemDarkened )
     116        return;
     117  
     118      /* protect against range problems and divide by zero */
     119      if ( emRatio < cf2_doubleToFixed( .01 ) )
     120        return;
     121  
     122      if ( stemDarkened )
     123      {
     124        FT_Int  x1 = darkenParams[0];
     125        FT_Int  y1 = darkenParams[1];
     126        FT_Int  x2 = darkenParams[2];
     127        FT_Int  y2 = darkenParams[3];
     128        FT_Int  x3 = darkenParams[4];
     129        FT_Int  y3 = darkenParams[5];
     130        FT_Int  x4 = darkenParams[6];
     131        FT_Int  y4 = darkenParams[7];
     132  
     133  
     134        /* convert from true character space to 1000 unit character space; */
     135        /* add synthetic emboldening effect                                */
     136  
     137        /* `stemWidthPer1000' will not overflow for a legitimate font      */
     138  
     139        stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio );
     140  
     141        /* `scaledStem' can easily overflow, so we must clamp its maximum  */
     142        /* value; the test doesn't need to be precise, but must be         */
     143        /* conservative.  The clamp value (default 2333) where             */
     144        /* `darkenAmount' is zero is well below the overflow value of      */
     145        /* 32767.                                                          */
     146        /*                                                                 */
     147        /* FT_MSB computes the integer part of the base 2 logarithm.  The  */
     148        /* number of bits for the product is 1 or 2 more than the sum of   */
     149        /* logarithms; remembering that the 16 lowest bits of the fraction */
     150        /* are dropped this is correct to within a factor of almost 4.     */
     151        /* For example, 0x80.0000 * 0x80.0000 = 0x4000.0000 is 23+23 and   */
     152        /* is flagged as possible overflow because 0xFF.FFFF * 0xFF.FFFF = */
     153        /* 0xFFFF.FE00 is also 23+23.                                      */
     154  
     155        logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) +
     156                     FT_MSB( (FT_UInt32)ppem );
     157  
     158        if ( logBase2 >= 46 )
     159          /* possible overflow */
     160          scaledStem = cf2_intToFixed( x4 );
     161        else
     162          scaledStem = FT_MulFix( stemWidthPer1000, ppem );
     163  
     164        /* now apply the darkening parameters */
     165  
     166        if ( scaledStem < cf2_intToFixed( x1 ) )
     167          *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem );
     168  
     169        else if ( scaledStem < cf2_intToFixed( x2 ) )
     170        {
     171          FT_Int  xdelta = x2 - x1;
     172          FT_Int  ydelta = y2 - y1;
     173          FT_Int  x      = stemWidthPer1000 -
     174                             FT_DivFix( cf2_intToFixed( x1 ), ppem );
     175  
     176  
     177          if ( !xdelta )
     178            goto Try_x3;
     179  
     180          *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
     181                            FT_DivFix( cf2_intToFixed( y1 ), ppem );
     182        }
     183  
     184        else if ( scaledStem < cf2_intToFixed( x3 ) )
     185        {
     186        Try_x3:
     187          {
     188            FT_Int  xdelta = x3 - x2;
     189            FT_Int  ydelta = y3 - y2;
     190            FT_Int  x      = stemWidthPer1000 -
     191                               FT_DivFix( cf2_intToFixed( x2 ), ppem );
     192  
     193  
     194            if ( !xdelta )
     195              goto Try_x4;
     196  
     197            *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
     198                              FT_DivFix( cf2_intToFixed( y2 ), ppem );
     199          }
     200        }
     201  
     202        else if ( scaledStem < cf2_intToFixed( x4 ) )
     203        {
     204        Try_x4:
     205          {
     206            FT_Int  xdelta = x4 - x3;
     207            FT_Int  ydelta = y4 - y3;
     208            FT_Int  x      = stemWidthPer1000 -
     209                               FT_DivFix( cf2_intToFixed( x3 ), ppem );
     210  
     211  
     212            if ( !xdelta )
     213              goto Use_y4;
     214  
     215            *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
     216                              FT_DivFix( cf2_intToFixed( y3 ), ppem );
     217          }
     218        }
     219  
     220        else
     221        {
     222        Use_y4:
     223          *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem );
     224        }
     225  
     226        /* use half the amount on each side and convert back to true */
     227        /* character space                                           */
     228        *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio );
     229      }
     230  
     231      /* add synthetic emboldening effect in character space */
     232      *darkenAmount += boldenAmount / 2;
     233    }
     234  
     235  
     236    /* set up values for the current FontDict and matrix; */
     237    /* called for each glyph to be rendered               */
     238  
     239    /* caller's transform is adjusted for subpixel positioning */
     240    static void
     241    cf2_font_setup( CF2_Font           font,
     242                    const CF2_Matrix*  transform )
     243    {
     244      /* pointer to parsed font object */
     245      PS_Decoder*  decoder = font->decoder;
     246  
     247      FT_Bool  needExtraSetup = FALSE;
     248  
     249      CFF_VStoreRec*  vstore;
     250      FT_Bool         hasVariations = FALSE;
     251  
     252      /* character space units */
     253      CF2_Fixed  boldenX = font->syntheticEmboldeningAmountX;
     254      CF2_Fixed  boldenY = font->syntheticEmboldeningAmountY;
     255  
     256      CFF_SubFont  subFont;
     257      CF2_Fixed    ppem;
     258  
     259      CF2_UInt   lenNormalizedV = 0;
     260      FT_Fixed*  normalizedV    = NULL;
     261  
     262      /* clear previous error */
     263      font->error = FT_Err_Ok;
     264  
     265      /* if a CID fontDict has changed, we need to recompute some cached */
     266      /* data                                                            */
     267      subFont = cf2_getSubfont( decoder );
     268      if ( font->lastSubfont != subFont )
     269      {
     270        font->lastSubfont = subFont;
     271        needExtraSetup    = TRUE;
     272      }
     273  
     274      if ( !font->isT1 )
     275      {
     276        /* check for variation vectors */
     277        vstore        = cf2_getVStore( decoder );
     278        hasVariations = ( vstore->dataCount != 0 );
     279  
     280        if ( hasVariations )
     281        {
     282  #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
     283          FT_Service_CFFLoad  cffload = (FT_Service_CFFLoad)font->cffload;
     284  
     285  
     286          /* check whether Private DICT in this subfont needs to be reparsed */
     287          font->error = cf2_getNormalizedVector( decoder,
     288                                                 &lenNormalizedV,
     289                                                 &normalizedV );
     290          if ( font->error )
     291            return;
     292  
     293          if ( cffload->blend_check_vector( &subFont->blend,
     294                                            subFont->private_dict.vsindex,
     295                                            lenNormalizedV,
     296                                            normalizedV ) )
     297          {
     298            /* blend has changed, reparse */
     299            cffload->load_private_dict( decoder->cff,
     300                                        subFont,
     301                                        lenNormalizedV,
     302                                        normalizedV );
     303            needExtraSetup = TRUE;
     304          }
     305  #endif
     306  
     307          /* copy from subfont */
     308          font->blend.font = subFont->blend.font;
     309  
     310          /* clear state of charstring blend */
     311          font->blend.usedBV = FALSE;
     312  
     313          /* initialize value for charstring */
     314          font->vsindex = subFont->private_dict.vsindex;
     315  
     316          /* store vector inputs for blends in charstring */
     317          font->lenNDV = lenNormalizedV;
     318          font->NDV    = normalizedV;
     319        }
     320      }
     321  
     322      /* if ppem has changed, we need to recompute some cached data         */
     323      /* note: because of CID font matrix concatenation, ppem and transform */
     324      /*       do not necessarily track.                                    */
     325      ppem = cf2_getPpemY( decoder );
     326      if ( font->ppem != ppem )
     327      {
     328        font->ppem     = ppem;
     329        needExtraSetup = TRUE;
     330      }
     331  
     332      /* copy hinted flag on each call */
     333      font->hinted = FT_BOOL( font->renderingFlags & CF2_FlagsHinted );
     334  
     335      /* determine if transform has changed;       */
     336      /* include Fontmatrix but ignore translation */
     337      if ( ft_memcmp( transform,
     338                      &font->currentTransform,
     339                      4 * sizeof ( CF2_Fixed ) ) != 0 )
     340      {
     341        /* save `key' information for `cache of one' matrix data; */
     342        /* save client transform, without the translation         */
     343        font->currentTransform    = *transform;
     344        font->currentTransform.tx =
     345        font->currentTransform.ty = cf2_intToFixed( 0 );
     346  
     347        /* TODO: FreeType transform is simple scalar; for now, use identity */
     348        /*       for outer                                                  */
     349        font->innerTransform   = *transform;
     350        font->outerTransform.a =
     351        font->outerTransform.d = cf2_intToFixed( 1 );
     352        font->outerTransform.b =
     353        font->outerTransform.c = cf2_intToFixed( 0 );
     354  
     355        needExtraSetup = TRUE;
     356      }
     357  
     358      /*
     359       * font->darkened is set to true if there is a stem darkening request or
     360       * the font is synthetic emboldened.
     361       * font->darkened controls whether to adjust blue zones, winding order,
     362       * and hinting.
     363       *
     364       */
     365      if ( font->stemDarkened != ( font->renderingFlags & CF2_FlagsDarkened ) )
     366      {
     367        font->stemDarkened =
     368          FT_BOOL( font->renderingFlags & CF2_FlagsDarkened );
     369  
     370        /* blue zones depend on darkened flag */
     371        needExtraSetup = TRUE;
     372      }
     373  
     374      /* recompute variables that are dependent on transform or FontDict or */
     375      /* darken flag                                                        */
     376      if ( needExtraSetup )
     377      {
     378        /* StdVW is found in the private dictionary;                       */
     379        /* recompute darkening amounts whenever private dictionary or      */
     380        /* transform change                                                */
     381        /* Note: a rendering flag turns darkening on or off, so we want to */
     382        /*       store the `on' amounts;                                   */
     383        /*       darkening amount is computed in character space           */
     384        /* TODO: testing size-dependent darkening here;                    */
     385        /*       what to do for rotations?                                 */
     386  
     387        CF2_Fixed  emRatio;
     388        CF2_Fixed  stdHW;
     389        CF2_Int    unitsPerEm = font->unitsPerEm;
     390  
     391  
     392        if ( unitsPerEm == 0 )
     393          unitsPerEm = 1000;
     394  
     395        ppem = FT_MAX( cf2_intToFixed( 4 ),
     396                       font->ppem ); /* use minimum ppem of 4 */
     397  
     398  #if 0
     399        /* since vstem is measured in the x-direction, we use the `a' member */
     400        /* of the fontMatrix                                                 */
     401        emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->a );
     402  #endif
     403  
     404        /* Freetype does not preserve the fontMatrix when parsing; use */
     405        /* unitsPerEm instead.                                         */
     406        /* TODO: check precision of this                               */
     407        emRatio     = cf2_intToFixed( 1000 ) / unitsPerEm;
     408        font->stdVW = cf2_getStdVW( decoder );
     409  
     410        if ( font->stdVW <= 0 )
     411          font->stdVW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
     412  
     413        if ( boldenX > 0 )
     414        {
     415          /* Ensure that boldenX is at least 1 pixel for synthetic bold font */
     416          /* (similar to what Avalon does)                                   */
     417          boldenX = FT_MAX( boldenX,
     418                            FT_DivFix( cf2_intToFixed( unitsPerEm ), ppem ) );
     419  
     420          /* Synthetic emboldening adds at least 1 pixel to darkenX, while */
     421          /* stem darkening adds at most half pixel.  Since the purpose of */
     422          /* stem darkening (readability at small sizes) is met with       */
     423          /* synthetic emboldening, no need to add stem darkening for a    */
     424          /* synthetic bold font.                                          */
     425          cf2_computeDarkening( emRatio,
     426                                ppem,
     427                                font->stdVW,
     428                                &font->darkenX,
     429                                boldenX,
     430                                FALSE,
     431                                font->darkenParams );
     432        }
     433        else
     434          cf2_computeDarkening( emRatio,
     435                                ppem,
     436                                font->stdVW,
     437                                &font->darkenX,
     438                                0,
     439                                font->stemDarkened,
     440                                font->darkenParams );
     441  
     442  #if 0
     443        /* since hstem is measured in the y-direction, we use the `d' member */
     444        /* of the fontMatrix                                                 */
     445        /* TODO: use the same units per em as above; check this              */
     446        emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->d );
     447  #endif
     448  
     449        /* set the default stem width, because it must be the same for all */
     450        /* family members;                                                 */
     451        /* choose a constant for StdHW that depends on font contrast       */
     452        stdHW = cf2_getStdHW( decoder );
     453  
     454        if ( stdHW > 0 && font->stdVW > MUL_INT32( 2, stdHW ) )
     455          font->stdHW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
     456        else
     457        {
     458          /* low contrast font gets less hstem darkening */
     459          font->stdHW = FT_DivFix( cf2_intToFixed( 110 ), emRatio );
     460        }
     461  
     462        cf2_computeDarkening( emRatio,
     463                              ppem,
     464                              font->stdHW,
     465                              &font->darkenY,
     466                              boldenY,
     467                              font->stemDarkened,
     468                              font->darkenParams );
     469  
     470        if ( font->darkenX != 0 || font->darkenY != 0 )
     471          font->darkened = TRUE;
     472        else
     473          font->darkened = FALSE;
     474  
     475        font->reverseWinding = FALSE; /* initial expectation is CCW */
     476  
     477        /* compute blue zones for this instance */
     478        cf2_blues_init( &font->blues, font );
     479  
     480      } /* needExtraSetup */
     481    }
     482  
     483  
     484    /* equivalent to AdobeGetOutline */
     485    FT_LOCAL_DEF( FT_Error )
     486    cf2_getGlyphOutline( CF2_Font           font,
     487                         CF2_Buffer         charstring,
     488                         const CF2_Matrix*  transform,
     489                         CF2_F16Dot16*      glyphWidth )
     490    {
     491      FT_Error  lastError = FT_Err_Ok;
     492  
     493      FT_Vector  translation;
     494  
     495  #if 0
     496      FT_Vector  advancePoint;
     497  #endif
     498  
     499      CF2_Fixed  advWidth = 0;
     500      FT_Bool    needWinding;
     501  
     502  
     503      /* Note: use both integer and fraction for outlines.  This allows bbox */
     504      /*       to come out directly.                                         */
     505  
     506      translation.x = transform->tx;
     507      translation.y = transform->ty;
     508  
     509      /* set up values based on transform */
     510      cf2_font_setup( font, transform );
     511      if ( font->error )
     512        goto exit;                      /* setup encountered an error */
     513  
     514      /* reset darken direction */
     515      font->reverseWinding = FALSE;
     516  
     517      /* winding order only affects darkening */
     518      needWinding = font->darkened;
     519  
     520      while ( 1 )
     521      {
     522        /* reset output buffer */
     523        cf2_outline_reset( &font->outline );
     524  
     525        /* build the outline, passing the full translation */
     526        cf2_interpT2CharString( font,
     527                                charstring,
     528                                (CF2_OutlineCallbacks)&font->outline,
     529                                &translation,
     530                                FALSE,
     531                                0,
     532                                0,
     533                                &advWidth );
     534  
     535        if ( font->error )
     536          goto exit;
     537  
     538        if ( !needWinding )
     539          break;
     540  
     541        /* check winding order */
     542        if ( font->outline.root.windingMomentum >= 0 ) /* CFF is CCW */
     543          break;
     544  
     545        /* invert darkening and render again                            */
     546        /* TODO: this should be a parameter to getOutline-computeOffset */
     547        font->reverseWinding = TRUE;
     548  
     549        needWinding = FALSE;    /* exit after next iteration */
     550      }
     551  
     552      /* finish storing client outline */
     553      cf2_outline_close( &font->outline );
     554  
     555    exit:
     556      /* FreeType just wants the advance width; there is no translation */
     557      *glyphWidth = advWidth;
     558  
     559      /* free resources and collect errors from objects we've used */
     560      cf2_setError( &font->error, lastError );
     561  
     562      return font->error;
     563    }
     564  
     565  
     566  /* END */