/* get scaling and hint flag from GlyphSlot */
  static void
  cf2_getScaleAndHintFlag( CFF_Decoder*  decoder,
                           CF2_Fixed*    x_scale,
                           CF2_Fixed*    y_scale,
                           FT_Bool*      hinted,
                           FT_Bool*      scaled )
  {
    FT_ASSERT( decoder && decoder->builder.glyph );

    /* note: FreeType scale includes a factor of 64 */
    *hinted = decoder->builder.glyph->hint;
    *scaled = decoder->builder.glyph->scaled;

    if ( *hinted )
    {
      *x_scale = FT_DivFix( decoder->builder.glyph->x_scale,
                            cf2_intToFixed( 64 ) );
      *y_scale = FT_DivFix( decoder->builder.glyph->y_scale,
                            cf2_intToFixed( 64 ) );
    }
    else
    {
      /* for unhinted outlines, `cff_slot_load' does the scaling, */
      /* thus render at `unity' scale                             */

      *x_scale = 0x0400;   /* 1/64 as 16.16 */
      *y_scale = 0x0400;
    }
  }
Beispiel #2
0
  cf2_getBlueMetrics( CFF_Decoder*  decoder,
                      CF2_Fixed*    blueScale,
                      CF2_Fixed*    blueShift,
                      CF2_Fixed*    blueFuzz )
  {
    FT_ASSERT( decoder && decoder->current_subfont );

    *blueScale = FT_DivFix(
                   decoder->current_subfont->private_dict.blue_scale,
                   cf2_intToFixed( 1000 ) );
    *blueShift = cf2_intToFixed(
                   decoder->current_subfont->private_dict.blue_shift );
    *blueFuzz  = cf2_intToFixed(
                   decoder->current_subfont->private_dict.blue_fuzz );
  }
Beispiel #3
0
  cf2_getNominalWidthX( CFF_Decoder*  decoder )
  {
    FT_ASSERT( decoder && decoder->current_subfont );

    return cf2_intToFixed(
             decoder->current_subfont->private_dict.nominal_width );
  }
Beispiel #4
0
  cf2_getDefaultWidthX( CFF_Decoder*  decoder )
  {
    FT_ASSERT( decoder && decoder->current_subfont );

    return cf2_intToFixed(
             decoder->current_subfont->private_dict.default_width );
  }
Beispiel #5
0
  /*
   * This check should avoid most internal overflow cases.  Clients should
   * generally respond to `Glyph_Too_Big' by getting a glyph outline
   * at EM size, scaling it and filling it as a graphics operation.
   *
   */
  static FT_Error
  cf2_checkTransform( const CF2_Matrix*  transform,
                      CF2_Int            unitsPerEm )
  {
    CF2_Fixed  maxScale;


    FT_ASSERT( unitsPerEm > 0 );

    if ( transform->a <= 0 || transform->d <= 0 )
      return FT_THROW( Invalid_Size_Handle );

    FT_ASSERT( transform->b == 0 && transform->c == 0 );
    FT_ASSERT( transform->tx == 0 && transform->ty == 0 );

    if ( unitsPerEm > 0x7FFF )
      return FT_THROW( Glyph_Too_Big );

    maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) );

    if ( transform->a > maxScale || transform->d > maxScale )
      return FT_THROW( Glyph_Too_Big );

    return FT_Err_Ok;
  }
Beispiel #6
0
  cf2_getStdHW( CFF_Decoder*  decoder )
  {
    FT_ASSERT( decoder && decoder->current_subfont );

    return cf2_intToFixed(
             decoder->current_subfont->private_dict.standard_width );
  }
  cf2_getPpemY( CFF_Decoder*  decoder )
  {
    FT_ASSERT( decoder                          &&
               decoder->builder.face            &&
               decoder->builder.face->root.size );
    FT_ASSERT( decoder->builder.face->root.size->metrics.y_ppem );

    return cf2_intToFixed(
             decoder->builder.face->root.size->metrics.y_ppem );
  }
Beispiel #8
0
  cf2_stack_popFixed( CF2_Stack  stack )
  {
    if ( stack->top == &stack->buffer[0] )
    {
      CF2_SET_ERROR( stack->error, Stack_Underflow );
      return cf2_intToFixed( 0 );    /* underflow */
    }

    --stack->top;

    switch ( stack->top->type )
    {
    case CF2_NumberInt:
      return cf2_intToFixed( stack->top->u.i );
    case CF2_NumberFrac:
      return cf2_fracToFixed( stack->top->u.f );
    default:
      return stack->top->u.r;
    }
  }
Beispiel #9
0
  cf2_stack_getReal( CF2_Stack  stack,
                     CF2_UInt   idx )
  {
    FT_ASSERT( cf2_stack_count( stack ) <= CF2_OPERAND_STACK_SIZE );

    if ( idx >= cf2_stack_count( stack ) )
    {
      CF2_SET_ERROR( stack->error, Stack_Overflow );
      return cf2_intToFixed( 0 );    /* bounds error */
    }

    switch ( stack->buffer[idx].type )
    {
    case CF2_NumberInt:
      return cf2_intToFixed( stack->buffer[idx].u.i );
    case CF2_NumberFrac:
      return cf2_fracToFixed( stack->buffer[idx].u.f );
    default:
      return stack->buffer[idx].u.r;
    }
  }
Beispiel #10
0
  cf2_getPpemY( CFF_Decoder*  decoder )
  {
    FT_ASSERT( decoder                          &&
               decoder->builder.face            &&
               decoder->builder.face->root.size );

    /*
     * Note that `y_ppem' can be zero if there wasn't a call to
     * `FT_Set_Char_Size' or something similar.  However, this isn't a
     * problem since we come to this place in the code only if
     * FT_LOAD_NO_SCALE is set (the other case gets caught by
     * `cf2_checkTransform').  The ppem value is needed to compute the stem
     * darkening, which is disabled for getting the unscaled outline.
     *
     */
    return cf2_intToFixed(
             decoder->builder.face->root.size->metrics.y_ppem );
  }
Beispiel #11
0
  cf2_blues_init( CF2_Blues  blues,
                  CF2_Font   font )
  {
    /* pointer to parsed font object */
    CFF_Decoder*  decoder = font->decoder;

    CF2_Fixed  zoneHeight;
    CF2_Fixed  maxZoneHeight = 0;
    CF2_Fixed  csUnitsPerPixel;

    size_t  numBlueValues;
    size_t  numOtherBlues;
    size_t  numFamilyBlues;
    size_t  numFamilyOtherBlues;

    FT_Pos*  blueValues;
    FT_Pos*  otherBlues;
    FT_Pos*  familyBlues;
    FT_Pos*  familyOtherBlues;

    size_t     i;
    CF2_Fixed  emBoxBottom, emBoxTop;

    CF2_Int  unitsPerEm = font->unitsPerEm;


    if ( unitsPerEm == 0 )
      unitsPerEm = 1000;

    FT_ZERO( blues );
    blues->scale = font->innerTransform.d;

    cf2_getBlueMetrics( decoder,
                        &blues->blueScale,
                        &blues->blueShift,
                        &blues->blueFuzz );

    cf2_getBlueValues( decoder, &numBlueValues, &blueValues );
    cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues );
    cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues );
    cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues );

    /*
     * synthetic em box hint heuristic
     *
     * Apply this when ideographic dictionary (LanguageGroup 1) has no
     * real alignment zones.  Adobe tools generate dummy zones at -250 and
     * 1100 for a 1000 unit em.  Fonts with ICF-based alignment zones
     * should not enable the heuristic.  When the heuristic is enabled,
     * the font's blue zones are ignored.
     *
     */

    /* get em box from OS/2 typoAscender/Descender                      */
    /* TODO: FreeType does not parse these metrics.  Skip them for now. */
#if 0
    FCM_getHorizontalLineMetrics( &e,
                                  font->font,
                                  &ascender,
                                  &descender,
                                  &linegap );
    if ( ascender - descender == unitsPerEm )
    {
      emBoxBottom = cf2_intToFixed( descender );
      emBoxTop    = cf2_intToFixed( ascender );
    }
    else
#endif
    {
      emBoxBottom = CF2_ICF_Bottom;
      emBoxTop    = CF2_ICF_Top;
    }

    if ( cf2_getLanguageGroup( decoder ) == 1                   &&
         ( numBlueValues == 0                                 ||
           ( numBlueValues == 4                             &&
             cf2_blueToFixed( blueValues[0] ) < emBoxBottom &&
             cf2_blueToFixed( blueValues[1] ) < emBoxBottom &&
             cf2_blueToFixed( blueValues[2] ) > emBoxTop    &&
             cf2_blueToFixed( blueValues[3] ) > emBoxTop    ) ) )
    {
      /*
       * Construct hint edges suitable for synthetic ghost hints at top
       * and bottom of em box.  +-CF2_MIN_COUNTER allows for unhinted
       * features above or below the last hinted edge.  This also gives a
       * net 1 pixel boost to the height of ideographic glyphs.
       *
       * Note: Adjust synthetic hints outward by epsilon (0x.0001) to
       *       avoid interference.  E.g., some fonts have real hints at
       *       880 and -120.
       */

      blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON;
      blues->emBoxBottomEdge.dsCoord = cf2_fixedRound(
                                         FT_MulFix(
                                           blues->emBoxBottomEdge.csCoord,
                                           blues->scale ) ) -
                                       CF2_MIN_COUNTER;
      blues->emBoxBottomEdge.scale   = blues->scale;
      blues->emBoxBottomEdge.flags   = CF2_GhostBottom |
                                       CF2_Locked |
                                       CF2_Synthetic;

      blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON +
                                    2 * font->darkenY;
      blues->emBoxTopEdge.dsCoord = cf2_fixedRound(
                                      FT_MulFix(
                                        blues->emBoxTopEdge.csCoord,
                                        blues->scale ) ) +
                                    CF2_MIN_COUNTER;
      blues->emBoxTopEdge.scale   = blues->scale;
      blues->emBoxTopEdge.flags   = CF2_GhostTop |
                                    CF2_Locked |
                                    CF2_Synthetic;

      blues->doEmBoxHints = TRUE;    /* enable the heuristic */

      return;
    }

    /* copy `BlueValues' and `OtherBlues' to a combined array of top and */
    /* bottom zones                                                      */
    for ( i = 0; i < numBlueValues; i += 2 )
    {
      blues->zone[blues->count].csBottomEdge =
        cf2_blueToFixed( blueValues[i] );
      blues->zone[blues->count].csTopEdge =
        cf2_blueToFixed( blueValues[i + 1] );

      zoneHeight = blues->zone[blues->count].csTopEdge -
                   blues->zone[blues->count].csBottomEdge;

      if ( zoneHeight < 0 )
      {
        FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
        continue;   /* reject this zone */
      }

      if ( zoneHeight > maxZoneHeight )
      {
        /* take maximum before darkening adjustment      */
        /* so overshoot suppression point doesn't change */
        maxZoneHeight = zoneHeight;
      }

      /* adjust both edges of top zone upward by twice darkening amount */
      if ( i != 0 )
      {
        blues->zone[blues->count].csTopEdge    += 2 * font->darkenY;
        blues->zone[blues->count].csBottomEdge += 2 * font->darkenY;
      }

      /* first `BlueValue' is bottom zone; others are top */
      if ( i == 0 )
      {
        blues->zone[blues->count].bottomZone =
          TRUE;
        blues->zone[blues->count].csFlatEdge =
          blues->zone[blues->count].csTopEdge;
      }
      else
      {
        blues->zone[blues->count].bottomZone =
          FALSE;
        blues->zone[blues->count].csFlatEdge =
          blues->zone[blues->count].csBottomEdge;
      }

      blues->count += 1;
    }

    for ( i = 0; i < numOtherBlues; i += 2 )
    {
      blues->zone[blues->count].csBottomEdge =
        cf2_blueToFixed( otherBlues[i] );
      blues->zone[blues->count].csTopEdge =
        cf2_blueToFixed( otherBlues[i + 1] );

      zoneHeight = blues->zone[blues->count].csTopEdge -
                   blues->zone[blues->count].csBottomEdge;

      if ( zoneHeight < 0 )
      {
        FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
        continue;   /* reject this zone */
      }

      if ( zoneHeight > maxZoneHeight )
      {
        /* take maximum before darkening adjustment      */
        /* so overshoot suppression point doesn't change */
        maxZoneHeight = zoneHeight;
      }

      /* Note: bottom zones are not adjusted for darkening amount */

      /* all OtherBlues are bottom zone */
      blues->zone[blues->count].bottomZone =
        TRUE;
      blues->zone[blues->count].csFlatEdge =
        blues->zone[blues->count].csTopEdge;

      blues->count += 1;
    }

    /* Adjust for FamilyBlues */

    /* Search for the nearest flat edge in `FamilyBlues' or                */
    /* `FamilyOtherBlues'.  According to the Black Book, any matching edge */
    /* must be within one device pixel                                     */

    csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale );

    /* loop on all zones in this font */
    for ( i = 0; i < blues->count; i++ )
    {
      size_t     j;
      CF2_Fixed  minDiff;
      CF2_Fixed  flatFamilyEdge, diff;
      /* value for this font */
      CF2_Fixed  flatEdge = blues->zone[i].csFlatEdge;


      if ( blues->zone[i].bottomZone )
      {
        /* In a bottom zone, the top edge is the flat edge.             */
        /* Search `FamilyOtherBlues' for bottom zones; look for closest */
        /* Family edge that is within the one pixel threshold.          */

        minDiff = CF2_FIXED_MAX;

        for ( j = 0; j < numFamilyOtherBlues; j += 2 )
        {
          /* top edge */
          flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] );

          diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );

          if ( diff < minDiff && diff < csUnitsPerPixel )
          {
            blues->zone[i].csFlatEdge = flatFamilyEdge;
            minDiff                   = diff;

            if ( diff == 0 )
              break;
          }
        }

        /* check the first member of FamilyBlues, which is a bottom zone */
        if ( numFamilyBlues >= 2 )
        {
          /* top edge */
          flatFamilyEdge = cf2_blueToFixed( familyBlues[1] );

          diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );

          if ( diff < minDiff && diff < csUnitsPerPixel )
            blues->zone[i].csFlatEdge = flatFamilyEdge;
        }
      }
      else
      {
        /* In a top zone, the bottom edge is the flat edge.                */
        /* Search `FamilyBlues' for top zones; skip first zone, which is a */
        /* bottom zone; look for closest Family edge that is within the    */
        /* one pixel threshold                                             */

        minDiff = CF2_FIXED_MAX;

        for ( j = 2; j < numFamilyBlues; j += 2 )
        {
          /* bottom edge */
          flatFamilyEdge = cf2_blueToFixed( familyBlues[j] );

          /* adjust edges of top zone upward by twice darkening amount */
          flatFamilyEdge += 2 * font->darkenY;      /* bottom edge */

          diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );

          if ( diff < minDiff && diff < csUnitsPerPixel )
          {
            blues->zone[i].csFlatEdge = flatFamilyEdge;
            minDiff                   = diff;

            if ( diff == 0 )
              break;
          }
        }
      }
    }

    /* TODO: enforce separation of zones, including BlueFuzz */

    /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */
    /* `bcsetup.c'.                                               */

    if ( maxZoneHeight > 0 )
    {
      if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ),
                                         maxZoneHeight ) )
      {
        /* clamp at maximum scale */
        blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ),
                                      maxZoneHeight );
      }

      /*
       * TODO: Revisit the bug fix for 613448.  The minimum scale
       *       requirement catches a number of library fonts.  For
       *       example, with default BlueScale (.039625) and 0.4 minimum,
       *       the test below catches any font with maxZoneHeight < 10.1.
       *       There are library fonts ranging from 2 to 10 that get
       *       caught, including e.g., Eurostile LT Std Medium with
       *       maxZoneHeight of 6.
       *
       */
#if 0
      if ( blueScale < .4 / maxZoneHeight )
      {
        tetraphilia_assert( 0 );
        /* clamp at minimum scale, per bug 0613448 fix */
        blueScale = .4 / maxZoneHeight;
      }
#endif

    }

    /*
     * Suppress overshoot and boost blue zones at small sizes.  Boost
     * amount varies linearly from 0.5 pixel near 0 to 0 pixel at
     * blueScale cutoff.
     * Note: This boost amount is different from the coretype heuristic.
     *
     */

    if ( blues->scale < blues->blueScale )
    {
      blues->suppressOvershoot = TRUE;

      /* Change rounding threshold for `dsFlatEdge'.                    */
      /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */
      /*       10ppem Arial                                             */

      blues->boost = FT_MulFix(
                       cf2_floatToFixed( .6 ),
                       ( cf2_intToFixed( 1 ) -
                         FT_DivFix( blues->scale,
                                    blues->blueScale ) ) );
      if ( blues->boost > 0x7FFF )
      {
        /* boost must remain less than 0.5, or baseline could go negative */
        blues->boost = 0x7FFF;
      }
    }

    /* boost and darkening have similar effects; don't do both */
    if ( font->stemDarkened )
      blues->boost = 0;

    /* set device space alignment for each zone;    */
    /* apply boost amount before rounding flat edge */

    for ( i = 0; i < blues->count; i++ )
    {
      if ( blues->zone[i].bottomZone )
        blues->zone[i].dsFlatEdge = cf2_fixedRound(
                                      FT_MulFix(
                                        blues->zone[i].csFlatEdge,
                                        blues->scale ) -
                                      blues->boost );
      else
        blues->zone[i].dsFlatEdge = cf2_fixedRound(
                                      FT_MulFix(
                                        blues->zone[i].csFlatEdge,
                                        blues->scale ) +
                                      blues->boost );
    }
  }
Beispiel #12
0
  cf2_blues_capture( const CF2_Blues  blues,
                     CF2_Hint         bottomHintEdge,
                     CF2_Hint         topHintEdge )
  {
    /* TODO: validate? */
    CF2_Fixed  csFuzz = blues->blueFuzz;

    /* new position of captured edge */
    CF2_Fixed  dsNew;

    /* amount that hint is moved when positioned */
    CF2_Fixed  dsMove = 0;

    FT_Bool   captured = FALSE;
    CF2_UInt  i;


    /* assert edge flags are consistent */
    FT_ASSERT( !cf2_hint_isTop( bottomHintEdge ) &&
               !cf2_hint_isBottom( topHintEdge ) );

    /* TODO: search once without blue fuzz for compatibility with coretype? */
    for ( i = 0; i < blues->count; i++ )
    {
      if ( blues->zone[i].bottomZone           &&
           cf2_hint_isBottom( bottomHintEdge ) )
      {
        if ( ( blues->zone[i].csBottomEdge - csFuzz ) <=
               bottomHintEdge->csCoord                   &&
             bottomHintEdge->csCoord <=
               ( blues->zone[i].csTopEdge + csFuzz )     )
        {
          /* bottom edge captured by bottom zone */

          if ( blues->suppressOvershoot )
            dsNew = blues->zone[i].dsFlatEdge;

          else if ( ( blues->zone[i].csTopEdge - bottomHintEdge->csCoord ) >=
                      blues->blueShift )
          {
            /* guarantee minimum of 1 pixel overshoot */
            dsNew = FT_MIN(
                      cf2_fixedRound( bottomHintEdge->dsCoord ),
                      blues->zone[i].dsFlatEdge - cf2_intToFixed( 1 ) );
          }

          else
          {
            /* simply round captured edge */
            dsNew = cf2_fixedRound( bottomHintEdge->dsCoord );
          }

          dsMove   = dsNew - bottomHintEdge->dsCoord;
          captured = TRUE;

          break;
        }
      }

      if ( !blues->zone[i].bottomZone && cf2_hint_isTop( topHintEdge ) )
      {
        if ( ( blues->zone[i].csBottomEdge - csFuzz ) <=
               topHintEdge->csCoord                      &&
             topHintEdge->csCoord <=
               ( blues->zone[i].csTopEdge + csFuzz )     )
        {
          /* top edge captured by top zone */

          if ( blues->suppressOvershoot )
            dsNew = blues->zone[i].dsFlatEdge;

          else if ( ( topHintEdge->csCoord - blues->zone[i].csBottomEdge ) >=
                      blues->blueShift )
          {
            /* guarantee minimum of 1 pixel overshoot */
            dsNew = FT_MAX(
                      cf2_fixedRound( topHintEdge->dsCoord ),
                      blues->zone[i].dsFlatEdge + cf2_intToFixed( 1 ) );
          }

          else
          {
            /* simply round captured edge */
            dsNew = cf2_fixedRound( topHintEdge->dsCoord );
          }

          dsMove   = dsNew - topHintEdge->dsCoord;
          captured = TRUE;

          break;
        }
      }
    }

    if ( captured )
    {
      /* move both edges and flag them `locked' */
      if ( cf2_hint_isValid( bottomHintEdge ) )
      {
        bottomHintEdge->dsCoord += dsMove;
        cf2_hint_lock( bottomHintEdge );
      }

      if ( cf2_hint_isValid( topHintEdge ) )
      {
        topHintEdge->dsCoord += dsMove;
        cf2_hint_lock( topHintEdge );
      }
    }

    return captured;
  }
  /* Compute a stem darkening amount in character space. */
  static void
  cf2_computeDarkening( CF2_Fixed   emRatio,
                        CF2_Fixed   ppem,
                        CF2_Fixed   stemWidth,
                        CF2_Fixed*  darkenAmount,
                        CF2_Fixed   boldenAmount,
                        FT_Bool     stemDarkened )
  {
    /* Internal calculations are done in units per thousand for */
    /* convenience.                                             */
    CF2_Fixed  stemWidthPer1000, scaledStem;


    *darkenAmount = 0;

    if ( boldenAmount == 0 && !stemDarkened )
      return;

    /* protect against range problems and divide by zero */
    if ( emRatio < cf2_floatToFixed( .01 ) )
      return;

    if ( stemDarkened )
    {
      /* convert from true character space to 1000 unit character space; */
      /* add synthetic emboldening effect                                */

      /* we have to assure that the computation of `scaledStem' */
      /* and `stemWidthPer1000' don't overflow                  */

      stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio );

      if ( emRatio > CF2_FIXED_ONE                          &&
           stemWidthPer1000 <= ( stemWidth + boldenAmount ) )
      {
        stemWidthPer1000 = 0;                      /* to pacify compiler */
        scaledStem       = cf2_intToFixed( 2333 );
      }
      else
      {
        scaledStem = FT_MulFix( stemWidthPer1000, ppem );

        if ( ppem > CF2_FIXED_ONE           &&
             scaledStem <= stemWidthPer1000 )
          scaledStem = cf2_intToFixed( 2333 );
      }

      /*
       * Total darkening amount is computed in 1000 unit character space
       * using the modified 5 part curve as Avalon rasterizer.
       * The darkening amount is smaller for thicker stems.
       * It becomes zero when the stem is thicker than 2.333 pixels.
       *
       * In Avalon rasterizer,
       *
       *   darkenAmount = 0.5 pixels   if scaledStem <= 0.5 pixels,
       *   darkenAmount = 0.333 pixels if 1 <= scaledStem <= 1.667 pixels,
       *   darkenAmount = 0 pixel      if scaledStem >= 2.333 pixels,
       *
       * and piecewise linear in-between.
       *
       */
      if ( scaledStem < cf2_intToFixed( 500 ) )
        *darkenAmount = FT_DivFix( cf2_intToFixed( 400 ), ppem );

      else if ( scaledStem < cf2_intToFixed( 1000 ) )
        *darkenAmount = FT_DivFix( cf2_intToFixed( 525 ), ppem ) -
                          FT_MulFix( stemWidthPer1000,
                                     cf2_floatToFixed( .25 ) );

      else if ( scaledStem < cf2_intToFixed( 1667 ) )
        *darkenAmount = FT_DivFix( cf2_intToFixed( 275 ), ppem );

      else if ( scaledStem < cf2_intToFixed( 2333 ) )
        *darkenAmount = FT_DivFix( cf2_intToFixed( 963 ), ppem ) -
                          FT_MulFix( stemWidthPer1000,
                                     cf2_floatToFixed( .413 ) );

      /* use half the amount on each side and convert back to true */
      /* character space                                           */
      *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio );
    }

    /* add synthetic emboldening effect in character space */
    *darkenAmount += boldenAmount / 2;
  }
  /* caller's transform is adjusted for subpixel positioning */
  static void
  cf2_font_setup( CF2_Font           font,
                  const CF2_Matrix*  transform )
  {
    /* pointer to parsed font object */
    CFF_Decoder*  decoder = font->decoder;

    FT_Bool  needExtraSetup = FALSE;

    /* character space units */
    CF2_Fixed  boldenX = font->syntheticEmboldeningAmountX;
    CF2_Fixed  boldenY = font->syntheticEmboldeningAmountY;

    CF2_Fixed  ppem;


    /* clear previous error */
    font->error = FT_Err_Ok;

    /* if a CID fontDict has changed, we need to recompute some cached */
    /* data                                                            */
    needExtraSetup = font->lastSubfont != cf2_getSubfont( decoder );

    /* if ppem has changed, we need to recompute some cached data         */
    /* note: because of CID font matrix concatenation, ppem and transform */
    /*       do not necessarily track.                                    */
    ppem = cf2_getPpemY( decoder );
    if ( font->ppem != ppem )
    {
      font->ppem     = ppem;
      needExtraSetup = TRUE;
    }

    /* copy hinted flag on each call */
    font->hinted = font->renderingFlags & CF2_FlagsHinted;

    /* determine if transform has changed;       */
    /* include Fontmatrix but ignore translation */
    if ( ft_memcmp( transform,
                    &font->currentTransform,
                    4 * sizeof ( CF2_Fixed ) ) != 0 )
    {
      /* save `key' information for `cache of one' matrix data; */
      /* save client transform, without the translation         */
      font->currentTransform    = *transform;
      font->currentTransform.tx =
      font->currentTransform.ty = cf2_intToFixed( 0 );

      /* TODO: FreeType transform is simple scalar; for now, use identity */
      /*       for outer                                                  */
      font->innerTransform   = *transform;
      font->outerTransform.a =
      font->outerTransform.d = cf2_intToFixed( 1 );
      font->outerTransform.b =
      font->outerTransform.c = cf2_intToFixed( 0 );

      needExtraSetup = TRUE;
    }

    /*
     * font->darkened is set to true if there is a stem darkening request or
     * the font is synthetic emboldened.
     * font->darkened controls whether to adjust blue zones, winding order,
     * and hinting.
     *
     */
    if ( font->stemDarkened != ( font->renderingFlags & CF2_FlagsDarkened ) )
    {
      font->stemDarkened = font->renderingFlags & CF2_FlagsDarkened;

      /* blue zones depend on darkened flag */
      needExtraSetup = TRUE;
    }

    /* recompute variables that are dependent on transform or FontDict or */
    /* darken flag                                                        */
    if ( needExtraSetup )
    {
      /* StdVW is found in the private dictionary;                       */
      /* recompute darkening amounts whenever private dictionary or      */
      /* transform change                                                */
      /* Note: a rendering flag turns darkening on or off, so we want to */
      /*       store the `on' amounts;                                   */
      /*       darkening amount is computed in character space           */
      /* TODO: testing size-dependent darkening here;                    */
      /*       what to do for rotations?                                 */

      CF2_Fixed  emRatio;
      CF2_Fixed  stdHW;
      CF2_Int    unitsPerEm = font->unitsPerEm;


      if ( unitsPerEm == 0 )
        unitsPerEm = 1000;

      ppem = FT_MAX( cf2_intToFixed( 4 ),
                     font->ppem ); /* use minimum ppem of 4 */

#if 0
      /* since vstem is measured in the x-direction, we use the `a' member */
      /* of the fontMatrix                                                 */
      emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->a );
#endif

      /* Freetype does not preserve the fontMatrix when parsing; use */
      /* unitsPerEm instead.                                         */
      /* TODO: check precision of this                               */
      emRatio     = cf2_intToFixed( 1000 ) / unitsPerEm;
      font->stdVW = cf2_getStdVW( decoder );

      if ( font->stdVW <= 0 )
        font->stdVW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );

      if ( boldenX > 0 )
      {
        /* Ensure that boldenX is at least 1 pixel for synthetic bold font */
        /* (similar to what Avalon does)                                   */
        boldenX = FT_MAX( boldenX,
                          FT_DivFix( cf2_intToFixed( unitsPerEm ), ppem ) );

        /* Synthetic emboldening adds at least 1 pixel to darkenX, while */
        /* stem darkening adds at most half pixel.  Since the purpose of */
        /* stem darkening (readability at small sizes) is met with       */
        /* synthetic emboldening, no need to add stem darkening for a    */
        /* synthetic bold font.                                          */
        cf2_computeDarkening( emRatio,
                              ppem,
                              font->stdVW,
                              &font->darkenX,
                              boldenX,
                              FALSE );
      }
      else
        cf2_computeDarkening( emRatio,
                              ppem,
                              font->stdVW,
                              &font->darkenX,
                              0,
                              font->stemDarkened );

#if 0
      /* since hstem is measured in the y-direction, we use the `d' member */
      /* of the fontMatrix                                                 */
      /* TODO: use the same units per em as above; check this              */
      emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->d );
#endif

      /* set the default stem width, because it must be the same for all */
      /* family members;                                                 */
      /* choose a constant for StdHW that depends on font contrast       */
      stdHW = cf2_getStdHW( decoder );

      if ( stdHW > 0 && font->stdVW > 2 * stdHW )
        font->stdHW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
      else
      {
        /* low contrast font gets less hstem darkening */
        font->stdHW = FT_DivFix( cf2_intToFixed( 110 ), emRatio );
      }

      cf2_computeDarkening( emRatio,
                            ppem,
                            font->stdHW,
                            &font->darkenY,
                            boldenY,
                            font->stemDarkened );

      if ( font->darkenX != 0 || font->darkenY != 0 )
        font->darkened = TRUE;
      else
        font->darkened = FALSE;

      font->reverseWinding = FALSE; /* initial expectation is CCW */

      /* compute blue zones for this instance */
      cf2_blues_init( &font->blues, font );
    }
  }
Beispiel #15
0
  /* Compute a stem darkening amount in character space. */
  static void
  cf2_computeDarkening( CF2_Fixed   emRatio,
                        CF2_Fixed   ppem,
                        CF2_Fixed   stemWidth,
                        CF2_Fixed*  darkenAmount,
                        CF2_Fixed   boldenAmount,
                        FT_Bool     stemDarkened,
                        FT_Int*     darkenParams )
  {
    /*
     * Total darkening amount is computed in 1000 unit character space
     * using the modified 5 part curve as Adobe's Avalon rasterizer.
     * The darkening amount is smaller for thicker stems.
     * It becomes zero when the stem is thicker than 2.333 pixels.
     *
     * By default, we use
     *
     *   darkenAmount = 0.4 pixels   if scaledStem <= 0.5 pixels,
     *   darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels,
     *   darkenAmount = 0 pixel      if scaledStem >= 2.333 pixels,
     *
     * and piecewise linear in-between:
     *
     *
     *   darkening
     *       ^
     *       |
     *       |      (x1,y1)
     *       |--------+
     *       |         \
     *       |          \
     *       |           \          (x3,y3)
     *       |            +----------+
     *       |        (x2,y2)         \
     *       |                         \
     *       |                          \
     *       |                           +-----------------
     *       |                         (x4,y4)
     *       +--------------------------------------------->   stem
     *                                                       thickness
     *
     *
     * This corresponds to the following values for the
     * `darkening-parameters' property:
     *
     *   (x1, y1) = (500, 400)
     *   (x2, y2) = (1000, 275)
     *   (x3, y3) = (1667, 275)
     *   (x4, y4) = (2333, 0)
     *
     */

    /* Internal calculations are done in units per thousand for */
    /* convenience. The x axis is scaled stem width in          */
    /* thousandths of a pixel. That is, 1000 is 1 pixel.        */
    /* The y axis is darkening amount in thousandths of a pixel.*/
    /* In the code, below, dividing by ppem and                 */
    /* adjusting for emRatio converts darkenAmount to character */
    /* space (font units).                                      */
    CF2_Fixed  stemWidthPer1000, scaledStem;


    *darkenAmount = 0;

    if ( boldenAmount == 0 && !stemDarkened )
      return;

    /* protect against range problems and divide by zero */
    if ( emRatio < cf2_floatToFixed( .01 ) )
      return;

    if ( stemDarkened )
    {
      FT_Int  x1 = darkenParams[0];
      FT_Int  y1 = darkenParams[1];
      FT_Int  x2 = darkenParams[2];
      FT_Int  y2 = darkenParams[3];
      FT_Int  x3 = darkenParams[4];
      FT_Int  y3 = darkenParams[5];
      FT_Int  x4 = darkenParams[6];
      FT_Int  y4 = darkenParams[7];


      /* convert from true character space to 1000 unit character space; */
      /* add synthetic emboldening effect                                */

      /* we have to assure that the computation of `scaledStem' */
      /* and `stemWidthPer1000' don't overflow                  */

      stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio );

      if ( emRatio > CF2_FIXED_ONE                          &&
           stemWidthPer1000 <= ( stemWidth + boldenAmount ) )
      {
        stemWidthPer1000 = 0;                      /* to pacify compiler */
        scaledStem       = cf2_intToFixed( x4 );
      }
      else
      {
        scaledStem = FT_MulFix( stemWidthPer1000, ppem );

        if ( ppem > CF2_FIXED_ONE           &&
             scaledStem <= stemWidthPer1000 )
          scaledStem = cf2_intToFixed( x4 );
      }

      /* now apply the darkening parameters */

      if ( scaledStem < cf2_intToFixed( x1 ) )
        *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem );

      else if ( scaledStem < cf2_intToFixed( x2 ) )
      {
        FT_Int  xdelta = x2 - x1;
        FT_Int  ydelta = y2 - y1;
        FT_Int  x      = stemWidthPer1000 -
                           FT_DivFix( cf2_intToFixed( x1 ), ppem );


        if ( !xdelta )
          goto Try_x3;

        *darkenAmount = FT_MulFix( x, FT_DivFix( ydelta, xdelta ) ) +
                          FT_DivFix( cf2_intToFixed( y1 ), ppem );
      }

      else if ( scaledStem < cf2_intToFixed( x3 ) )
      {
      Try_x3:
        {
          FT_Int  xdelta = x3 - x2;
          FT_Int  ydelta = y3 - y2;
          FT_Int  x      = stemWidthPer1000 -
                             FT_DivFix( cf2_intToFixed( x2 ), ppem );


          if ( !xdelta )
            goto Try_x4;

          *darkenAmount = FT_MulFix( x, FT_DivFix( ydelta, xdelta ) ) +
                            FT_DivFix( cf2_intToFixed( y2 ), ppem );
        }
      }

      else if ( scaledStem < cf2_intToFixed( x4 ) )
      {
      Try_x4:
        {
          FT_Int  xdelta = x4 - x3;
          FT_Int  ydelta = y4 - y3;
          FT_Int  x      = stemWidthPer1000 -
                             FT_DivFix( cf2_intToFixed( x3 ), ppem );


          if ( !xdelta )
            goto Use_y4;

          *darkenAmount = FT_MulFix( x, FT_DivFix( ydelta, xdelta ) ) +
                            FT_DivFix( cf2_intToFixed( y3 ), ppem );
        }
      }

      else
      {
      Use_y4:
        *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem );
      }

      /* use half the amount on each side and convert back to true */
      /* character space                                           */
      *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio );
    }

    /* add synthetic emboldening effect in character space */
    *darkenAmount += boldenAmount / 2;
  }
Beispiel #16
0
  /* Compute a stem darkening amount in character space. */
  static void
  cf2_computeDarkening( CF2_Fixed   emRatio,
                        CF2_Fixed   ppem,
                        CF2_Fixed   stemWidth,
                        CF2_Fixed*  darkenAmount,
                        CF2_Fixed   boldenAmount,
                        FT_Bool     stemDarkened,
                        FT_Int*     darkenParams )
  {
    /*
     * Total darkening amount is computed in 1000 unit character space
     * using the modified 5 part curve as Adobe's Avalon rasterizer.
     * The darkening amount is smaller for thicker stems.
     * It becomes zero when the stem is thicker than 2.333 pixels.
     *
     * By default, we use
     *
     *   darkenAmount = 0.4 pixels   if scaledStem <= 0.5 pixels,
     *   darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels,
     *   darkenAmount = 0 pixel      if scaledStem >= 2.333 pixels,
     *
     * and piecewise linear in-between:
     *
     *
     *   darkening
     *       ^
     *       |
     *       |      (x1,y1)
     *       |--------+
     *       |         \
     *       |          \
     *       |           \          (x3,y3)
     *       |            +----------+
     *       |        (x2,y2)         \
     *       |                         \
     *       |                          \
     *       |                           +-----------------
     *       |                         (x4,y4)
     *       +--------------------------------------------->   stem
     *                                                       thickness
     *
     *
     * This corresponds to the following values for the
     * `darkening-parameters' property:
     *
     *   (x1, y1) = (500, 400)
     *   (x2, y2) = (1000, 275)
     *   (x3, y3) = (1667, 275)
     *   (x4, y4) = (2333, 0)
     *
     */

    /* Internal calculations are done in units per thousand for */
    /* convenience. The x axis is scaled stem width in          */
    /* thousandths of a pixel. That is, 1000 is 1 pixel.        */
    /* The y axis is darkening amount in thousandths of a pixel.*/
    /* In the code, below, dividing by ppem and                 */
    /* adjusting for emRatio converts darkenAmount to character */
    /* space (font units).                                      */
    CF2_Fixed  stemWidthPer1000, scaledStem;
    FT_Int     logBase2;


    *darkenAmount = 0;

    if ( boldenAmount == 0 && !stemDarkened )
      return;

    /* protect against range problems and divide by zero */
    if ( emRatio < cf2_floatToFixed( .01 ) )
      return;

    if ( stemDarkened )
    {
      FT_Int  x1 = darkenParams[0];
      FT_Int  y1 = darkenParams[1];
      FT_Int  x2 = darkenParams[2];
      FT_Int  y2 = darkenParams[3];
      FT_Int  x3 = darkenParams[4];
      FT_Int  y3 = darkenParams[5];
      FT_Int  x4 = darkenParams[6];
      FT_Int  y4 = darkenParams[7];


      /* convert from true character space to 1000 unit character space; */
      /* add synthetic emboldening effect                                */

      /* `stemWidthPer1000' will not overflow for a legitimate font      */

      stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio );

      /* `scaledStem' can easily overflow, so we must clamp its maximum  */
      /* value; the test doesn't need to be precise, but must be         */
      /* conservative.  The clamp value (default 2333) where             */
      /* `darkenAmount' is zero is well below the overflow value of      */
      /* 32767.                                                          */
      /*                                                                 */
      /* FT_MSB computes the integer part of the base 2 logarithm.  The  */
      /* number of bits for the product is 1 or 2 more than the sum of   */
      /* logarithms; remembering that the 16 lowest bits of the fraction */
      /* are dropped this is correct to within a factor of almost 4.     */
      /* For example, 0x80.0000 * 0x80.0000 = 0x4000.0000 is 23+23 and   */
      /* is flagged as possible overflow because 0xFF.FFFF * 0xFF.FFFF = */
      /* 0xFFFF.FE00 is also 23+23.                                      */

      logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) +
                   FT_MSB( (FT_UInt32)ppem );

      if ( logBase2 >= 46 )
        /* possible overflow */
        scaledStem = cf2_intToFixed( x4 );
      else
        scaledStem = FT_MulFix( stemWidthPer1000, ppem );

      /* now apply the darkening parameters */

      if ( scaledStem < cf2_intToFixed( x1 ) )
        *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem );

      else if ( scaledStem < cf2_intToFixed( x2 ) )
      {
        FT_Int  xdelta = x2 - x1;
        FT_Int  ydelta = y2 - y1;
        FT_Int  x      = stemWidthPer1000 -
                           FT_DivFix( cf2_intToFixed( x1 ), ppem );


        if ( !xdelta )
          goto Try_x3;

        *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
                          FT_DivFix( cf2_intToFixed( y1 ), ppem );
      }

      else if ( scaledStem < cf2_intToFixed( x3 ) )
      {
      Try_x3:
        {
          FT_Int  xdelta = x3 - x2;
          FT_Int  ydelta = y3 - y2;
          FT_Int  x      = stemWidthPer1000 -
                             FT_DivFix( cf2_intToFixed( x2 ), ppem );


          if ( !xdelta )
            goto Try_x4;

          *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
                            FT_DivFix( cf2_intToFixed( y2 ), ppem );
        }
      }

      else if ( scaledStem < cf2_intToFixed( x4 ) )
      {
      Try_x4:
        {
          FT_Int  xdelta = x4 - x3;
          FT_Int  ydelta = y4 - y3;
          FT_Int  x      = stemWidthPer1000 -
                             FT_DivFix( cf2_intToFixed( x3 ), ppem );


          if ( !xdelta )
            goto Use_y4;

          *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) +
                            FT_DivFix( cf2_intToFixed( y3 ), ppem );
        }
      }

      else
      {
      Use_y4:
        *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem );
      }

      /* use half the amount on each side and convert back to true */
      /* character space                                           */
      *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio );
    }

    /* add synthetic emboldening effect in character space */
    *darkenAmount += boldenAmount / 2;
  }