1/***************************************************************************/
2/*                                                                         */
3/*  aflatin2.c                                                             */
4/*                                                                         */
5/*    Auto-fitter hinting routines for latin script (body).                */
6/*                                                                         */
7/*  Copyright 2003-2013 by                                                 */
8/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
9/*                                                                         */
10/*  This file is part of the FreeType project, and may only be used,       */
11/*  modified, and distributed under the terms of the FreeType project      */
12/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13/*  this file you indicate that you have read the license and              */
14/*  understand and accept it fully.                                        */
15/*                                                                         */
16/***************************************************************************/
17
18
19#include FT_ADVANCES_H
20
21#include "afglobal.h"
22#include "aflatin.h"
23#include "aflatin2.h"
24#include "aferrors.h"
25
26
27#ifdef AF_CONFIG_OPTION_USE_WARPER
28#include "afwarp.h"
29#endif
30
31
32  /*************************************************************************/
33  /*                                                                       */
34  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
35  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
36  /* messages during execution.                                            */
37  /*                                                                       */
38#undef  FT_COMPONENT
39#define FT_COMPONENT  trace_aflatin2
40
41
42  FT_LOCAL_DEF( FT_Error )
43  af_latin2_hints_compute_segments( AF_GlyphHints  hints,
44                                    AF_Dimension   dim );
45
46  FT_LOCAL_DEF( void )
47  af_latin2_hints_link_segments( AF_GlyphHints  hints,
48                                 AF_Dimension   dim );
49
50  /*************************************************************************/
51  /*************************************************************************/
52  /*****                                                               *****/
53  /*****            L A T I N   G L O B A L   M E T R I C S            *****/
54  /*****                                                               *****/
55  /*************************************************************************/
56  /*************************************************************************/
57
58  FT_LOCAL_DEF( void )
59  af_latin2_metrics_init_widths( AF_LatinMetrics  metrics,
60                                 FT_Face          face )
61  {
62    /* scan the array of segments in each direction */
63    AF_GlyphHintsRec  hints[1];
64
65
66    af_glyph_hints_init( hints, face->memory );
67
68    metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
69    metrics->axis[AF_DIMENSION_VERT].width_count = 0;
70
71    {
72      FT_Error             error;
73      FT_UInt              glyph_index;
74      int                  dim;
75      AF_LatinMetricsRec   dummy[1];
76      AF_Scaler            scaler = &dummy->root.scaler;
77
78
79      glyph_index = FT_Get_Char_Index( face,
80                                       metrics->root.clazz->standard_char );
81      if ( glyph_index == 0 )
82        goto Exit;
83
84      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
85      if ( error || face->glyph->outline.n_points <= 0 )
86        goto Exit;
87
88      FT_ZERO( dummy );
89
90      dummy->units_per_em = metrics->units_per_em;
91      scaler->x_scale     = scaler->y_scale = 0x10000L;
92      scaler->x_delta     = scaler->y_delta = 0;
93      scaler->face        = face;
94      scaler->render_mode = FT_RENDER_MODE_NORMAL;
95      scaler->flags       = 0;
96
97      af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy );
98
99      error = af_glyph_hints_reload( hints, &face->glyph->outline );
100      if ( error )
101        goto Exit;
102
103      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
104      {
105        AF_LatinAxis  axis    = &metrics->axis[dim];
106        AF_AxisHints  axhints = &hints->axis[dim];
107        AF_Segment    seg, limit, link;
108        FT_UInt       num_widths = 0;
109
110
111        error = af_latin2_hints_compute_segments( hints,
112                                                 (AF_Dimension)dim );
113        if ( error )
114          goto Exit;
115
116        af_latin2_hints_link_segments( hints,
117                                      (AF_Dimension)dim );
118
119        seg   = axhints->segments;
120        limit = seg + axhints->num_segments;
121
122        for ( ; seg < limit; seg++ )
123        {
124          link = seg->link;
125
126          /* we only consider stem segments there! */
127          if ( link && link->link == seg && link > seg )
128          {
129            FT_Pos  dist;
130
131
132            dist = seg->pos - link->pos;
133            if ( dist < 0 )
134              dist = -dist;
135
136            if ( num_widths < AF_LATIN_MAX_WIDTHS )
137              axis->widths[num_widths++].org = dist;
138          }
139        }
140
141        af_sort_widths( num_widths, axis->widths );
142        axis->width_count = num_widths;
143      }
144
145  Exit:
146      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
147      {
148        AF_LatinAxis  axis = &metrics->axis[dim];
149        FT_Pos        stdw;
150
151
152        stdw = ( axis->width_count > 0 )
153                 ? axis->widths[0].org
154                 : AF_LATIN_CONSTANT( metrics, 50 );
155
156        /* let's try 20% of the smallest width */
157        axis->edge_distance_threshold = stdw / 5;
158        axis->standard_width          = stdw;
159        axis->extra_light             = 0;
160      }
161    }
162
163    af_glyph_hints_done( hints );
164  }
165
166
167
168#define AF_LATIN_MAX_TEST_CHARACTERS  12
169
170
171  static const char af_latin2_blue_chars[AF_LATIN_MAX_BLUES]
172                                        [AF_LATIN_MAX_TEST_CHARACTERS+1] =
173  {
174    "THEZOCQS",
175    "HEZLOCUS",
176    "fijkdbh",
177    "xzroesc",
178    "xzroesc",
179    "pqgjy"
180  };
181
182
183  static void
184  af_latin2_metrics_init_blues( AF_LatinMetrics  metrics,
185                                FT_Face          face )
186  {
187    FT_Pos        flats [AF_LATIN_MAX_TEST_CHARACTERS];
188    FT_Pos        rounds[AF_LATIN_MAX_TEST_CHARACTERS];
189    FT_Int        num_flats;
190    FT_Int        num_rounds;
191    FT_Int        bb;
192    AF_LatinBlue  blue;
193    FT_Error      error;
194    AF_LatinAxis  axis  = &metrics->axis[AF_DIMENSION_VERT];
195    FT_GlyphSlot  glyph = face->glyph;
196
197
198    /* we compute the blues simply by loading each character from the     */
199    /* 'af_latin2_blue_chars[blues]' string, then compute its top-most or */
200    /* bottom-most points (depending on `AF_IS_TOP_BLUE')                 */
201
202    FT_TRACE5(( "blue zones computation\n"
203                "======================\n\n" ));
204
205    for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
206    {
207      const char*  p     = af_latin2_blue_chars[bb];
208      const char*  limit = p + AF_LATIN_MAX_TEST_CHARACTERS;
209      FT_Pos*      blue_ref;
210      FT_Pos*      blue_shoot;
211
212
213      FT_TRACE5(( "blue zone %d:\n", bb ));
214
215      num_flats  = 0;
216      num_rounds = 0;
217
218      for ( ; p < limit && *p; p++ )
219      {
220        FT_UInt     glyph_index;
221        FT_Int      best_point, best_y, best_first, best_last;
222        FT_Vector*  points;
223        FT_Bool     round;
224
225
226        /* load the character in the face -- skip unknown or empty ones */
227        glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
228        if ( glyph_index == 0 )
229          continue;
230
231        error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
232        if ( error || glyph->outline.n_points <= 0 )
233          continue;
234
235        /* now compute min or max point indices and coordinates */
236        points      = glyph->outline.points;
237        best_point  = -1;
238        best_y      = 0;  /* make compiler happy */
239        best_first  = 0;  /* ditto */
240        best_last   = 0;  /* ditto */
241
242        {
243          FT_Int  nn;
244          FT_Int  first = 0;
245          FT_Int  last  = -1;
246
247
248          for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ )
249          {
250            FT_Int  old_best_point = best_point;
251            FT_Int  pp;
252
253
254            last = glyph->outline.contours[nn];
255
256            /* Avoid single-point contours since they are never rasterized. */
257            /* In some fonts, they correspond to mark attachment points     */
258            /* which are way outside of the glyph's real outline.           */
259            if ( last == first )
260                continue;
261
262            if ( AF_LATIN_IS_TOP_BLUE( bb ) )
263            {
264              for ( pp = first; pp <= last; pp++ )
265                if ( best_point < 0 || points[pp].y > best_y )
266                {
267                  best_point = pp;
268                  best_y     = points[pp].y;
269                }
270            }
271            else
272            {
273              for ( pp = first; pp <= last; pp++ )
274                if ( best_point < 0 || points[pp].y < best_y )
275                {
276                  best_point = pp;
277                  best_y     = points[pp].y;
278                }
279            }
280
281            if ( best_point != old_best_point )
282            {
283              best_first = first;
284              best_last  = last;
285            }
286          }
287          FT_TRACE5(( "  %c  %d", *p, best_y ));
288        }
289
290        /* now check whether the point belongs to a straight or round   */
291        /* segment; we first need to find in which contour the extremum */
292        /* lies, then inspect its previous and next points              */
293        {
294          FT_Pos  best_x = points[best_point].x;
295          FT_Int  start, end, prev, next;
296          FT_Pos  dist;
297
298
299          /* now look for the previous and next points that are not on the */
300          /* same Y coordinate.  Threshold the `closeness'...              */
301          start = end = best_point;
302
303          do
304          {
305            prev = start - 1;
306            if ( prev < best_first )
307              prev = best_last;
308
309            dist = FT_ABS( points[prev].y - best_y );
310            /* accept a small distance or a small angle (both values are */
311            /* heuristic; value 20 corresponds to approx. 2.9 degrees)   */
312            if ( dist > 5 )
313              if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist )
314                break;
315
316            start = prev;
317
318          } while ( start != best_point );
319
320          do
321          {
322            next = end + 1;
323            if ( next > best_last )
324              next = best_first;
325
326            dist = FT_ABS( points[next].y - best_y );
327            if ( dist > 5 )
328              if ( FT_ABS( points[next].x - best_x ) <= 20 * dist )
329                break;
330
331            end = next;
332
333          } while ( end != best_point );
334
335          /* now, set the `round' flag depending on the segment's kind */
336          round = FT_BOOL(
337            FT_CURVE_TAG( glyph->outline.tags[start] ) != FT_CURVE_TAG_ON ||
338            FT_CURVE_TAG( glyph->outline.tags[ end ] ) != FT_CURVE_TAG_ON );
339
340          FT_TRACE5(( " (%s)\n", round ? "round" : "flat" ));
341        }
342
343        if ( round )
344          rounds[num_rounds++] = best_y;
345        else
346          flats[num_flats++]   = best_y;
347      }
348
349      if ( num_flats == 0 && num_rounds == 0 )
350      {
351        /*
352         *  we couldn't find a single glyph to compute this blue zone,
353         *  we will simply ignore it then
354         */
355        FT_TRACE5(( "  empty\n" ));
356        continue;
357      }
358
359      /* we have computed the contents of the `rounds' and `flats' tables, */
360      /* now determine the reference and overshoot position of the blue -- */
361      /* we simply take the median value after a simple sort               */
362      af_sort_pos( num_rounds, rounds );
363      af_sort_pos( num_flats,  flats );
364
365      blue       = & axis->blues[axis->blue_count];
366      blue_ref   = & blue->ref.org;
367      blue_shoot = & blue->shoot.org;
368
369      axis->blue_count++;
370
371      if ( num_flats == 0 )
372      {
373        *blue_ref   =
374        *blue_shoot = rounds[num_rounds / 2];
375      }
376      else if ( num_rounds == 0 )
377      {
378        *blue_ref   =
379        *blue_shoot = flats[num_flats / 2];
380      }
381      else
382      {
383        *blue_ref   = flats[num_flats / 2];
384        *blue_shoot = rounds[num_rounds / 2];
385      }
386
387      /* there are sometimes problems: if the overshoot position of top     */
388      /* zones is under its reference position, or the opposite for bottom  */
389      /* zones.  We must thus check everything there and correct the errors */
390      if ( *blue_shoot != *blue_ref )
391      {
392        FT_Pos   ref      = *blue_ref;
393        FT_Pos   shoot    = *blue_shoot;
394        FT_Bool  over_ref = FT_BOOL( shoot > ref );
395
396
397        if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref )
398        {
399          *blue_ref   =
400          *blue_shoot = ( shoot + ref ) / 2;
401
402          FT_TRACE5(( "  [overshoot smaller than reference,"
403                      " taking mean value]\n" ));
404        }
405      }
406
407      blue->flags = 0;
408      if ( AF_LATIN_IS_TOP_BLUE( bb ) )
409        blue->flags |= AF_LATIN_BLUE_TOP;
410
411      /*
412       * The following flags is used later to adjust the y and x scales
413       * in order to optimize the pixel grid alignment of the top of small
414       * letters.
415       */
416      if ( bb == AF_LATIN_BLUE_SMALL_TOP )
417        blue->flags |= AF_LATIN_BLUE_ADJUSTMENT;
418
419      FT_TRACE5(( "    -> reference = %ld\n"
420                  "       overshoot = %ld\n",
421                  *blue_ref, *blue_shoot ));
422    }
423
424    return;
425  }
426
427
428  FT_LOCAL_DEF( void )
429  af_latin2_metrics_check_digits( AF_LatinMetrics  metrics,
430                                  FT_Face          face )
431  {
432    FT_UInt   i;
433    FT_Bool   started = 0, same_width = 1;
434    FT_Fixed  advance, old_advance = 0;
435
436
437    /* check whether all ASCII digits have the same advance width; */
438    /* digit `0' is 0x30 in all supported charmaps                 */
439    for ( i = 0x30; i <= 0x39; i++ )
440    {
441      FT_UInt  glyph_index;
442
443
444      glyph_index = FT_Get_Char_Index( face, i );
445      if ( glyph_index == 0 )
446        continue;
447
448      if ( FT_Get_Advance( face, glyph_index,
449                           FT_LOAD_NO_SCALE         |
450                           FT_LOAD_NO_HINTING       |
451                           FT_LOAD_IGNORE_TRANSFORM,
452                           &advance ) )
453        continue;
454
455      if ( started )
456      {
457        if ( advance != old_advance )
458        {
459          same_width = 0;
460          break;
461        }
462      }
463      else
464      {
465        old_advance = advance;
466        started     = 1;
467      }
468    }
469
470    metrics->root.digits_have_same_width = same_width;
471  }
472
473
474  FT_LOCAL_DEF( FT_Error )
475  af_latin2_metrics_init( AF_LatinMetrics  metrics,
476                          FT_Face          face )
477  {
478    FT_Error    error  = FT_Err_Ok;
479    FT_CharMap  oldmap = face->charmap;
480    FT_UInt     ee;
481
482    static const FT_Encoding  latin_encodings[] =
483    {
484      FT_ENCODING_UNICODE,
485      FT_ENCODING_APPLE_ROMAN,
486      FT_ENCODING_ADOBE_STANDARD,
487      FT_ENCODING_ADOBE_LATIN_1,
488      FT_ENCODING_NONE  /* end of list */
489    };
490
491
492    metrics->units_per_em = face->units_per_EM;
493
494    /* do we have a latin charmap in there? */
495    for ( ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++ )
496    {
497      error = FT_Select_Charmap( face, latin_encodings[ee] );
498      if ( !error )
499        break;
500    }
501
502    if ( !error )
503    {
504      af_latin2_metrics_init_widths( metrics, face );
505      af_latin2_metrics_init_blues( metrics, face );
506      af_latin2_metrics_check_digits( metrics, face );
507    }
508
509    FT_Set_Charmap( face, oldmap );
510    return FT_Err_Ok;
511  }
512
513
514  static void
515  af_latin2_metrics_scale_dim( AF_LatinMetrics  metrics,
516                               AF_Scaler        scaler,
517                               AF_Dimension     dim )
518  {
519    FT_Fixed      scale;
520    FT_Pos        delta;
521    AF_LatinAxis  axis;
522    FT_UInt       nn;
523
524
525    if ( dim == AF_DIMENSION_HORZ )
526    {
527      scale = scaler->x_scale;
528      delta = scaler->x_delta;
529    }
530    else
531    {
532      scale = scaler->y_scale;
533      delta = scaler->y_delta;
534    }
535
536    axis = &metrics->axis[dim];
537
538    if ( axis->org_scale == scale && axis->org_delta == delta )
539      return;
540
541    axis->org_scale = scale;
542    axis->org_delta = delta;
543
544    /*
545     * correct Y scale to optimize the alignment of the top of small
546     * letters to the pixel grid
547     */
548    if ( dim == AF_DIMENSION_VERT )
549    {
550      AF_LatinAxis  vaxis = &metrics->axis[AF_DIMENSION_VERT];
551      AF_LatinBlue  blue = NULL;
552
553
554      for ( nn = 0; nn < vaxis->blue_count; nn++ )
555      {
556        if ( vaxis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT )
557        {
558          blue = &vaxis->blues[nn];
559          break;
560        }
561      }
562
563      if ( blue )
564      {
565        FT_Pos   scaled;
566        FT_Pos   threshold;
567        FT_Pos   fitted;
568        FT_UInt  limit;
569        FT_UInt  ppem;
570
571
572        scaled    = FT_MulFix( blue->shoot.org, scaler->y_scale );
573        ppem      = metrics->root.scaler.face->size->metrics.x_ppem;
574        limit     = metrics->root.globals->increase_x_height;
575        threshold = 40;
576
577        /* if the `increase-x-height' property is active, */
578        /* we round up much more often                    */
579        if ( limit                                 &&
580             ppem <= limit                         &&
581             ppem >= AF_PROP_INCREASE_X_HEIGHT_MIN )
582          threshold = 52;
583
584        fitted = ( scaled + threshold ) & ~63;
585
586#if 1
587        if ( scaled != fitted )
588        {
589          scale = FT_MulDiv( scale, fitted, scaled );
590          FT_TRACE5(( "== scaled x-top = %.2g"
591                      "  fitted = %.2g, scaling = %.4g\n",
592                      scaled / 64.0, fitted / 64.0,
593                      ( fitted * 1.0 ) / scaled ));
594        }
595#endif
596      }
597    }
598
599    axis->scale = scale;
600    axis->delta = delta;
601
602    if ( dim == AF_DIMENSION_HORZ )
603    {
604      metrics->root.scaler.x_scale = scale;
605      metrics->root.scaler.x_delta = delta;
606    }
607    else
608    {
609      metrics->root.scaler.y_scale = scale;
610      metrics->root.scaler.y_delta = delta;
611    }
612
613    /* scale the standard widths */
614    for ( nn = 0; nn < axis->width_count; nn++ )
615    {
616      AF_Width  width = axis->widths + nn;
617
618
619      width->cur = FT_MulFix( width->org, scale );
620      width->fit = width->cur;
621    }
622
623    /* an extra-light axis corresponds to a standard width that is */
624    /* smaller than 5/8 pixels                                     */
625    axis->extra_light =
626      (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
627
628    if ( dim == AF_DIMENSION_VERT )
629    {
630      /* scale the blue zones */
631      for ( nn = 0; nn < axis->blue_count; nn++ )
632      {
633        AF_LatinBlue  blue = &axis->blues[nn];
634        FT_Pos        dist;
635
636
637        blue->ref.cur   = FT_MulFix( blue->ref.org, scale ) + delta;
638        blue->ref.fit   = blue->ref.cur;
639        blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
640        blue->shoot.fit = blue->shoot.cur;
641        blue->flags    &= ~AF_LATIN_BLUE_ACTIVE;
642
643        /* a blue zone is only active if it is less than 3/4 pixels tall */
644        dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
645        if ( dist <= 48 && dist >= -48 )
646        {
647          FT_Pos  delta1, delta2;
648
649          delta1 = blue->shoot.org - blue->ref.org;
650          delta2 = delta1;
651          if ( delta1 < 0 )
652            delta2 = -delta2;
653
654          delta2 = FT_MulFix( delta2, scale );
655
656          if ( delta2 < 32 )
657            delta2 = 0;
658          else if ( delta2 < 64 )
659            delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
660          else
661            delta2 = FT_PIX_ROUND( delta2 );
662
663          if ( delta1 < 0 )
664            delta2 = -delta2;
665
666          blue->ref.fit   = FT_PIX_ROUND( blue->ref.cur );
667          blue->shoot.fit = blue->ref.fit + delta2;
668
669          FT_TRACE5(( ">> activating blue zone %d:"
670                      "  ref.cur=%.2g ref.fit=%.2g"
671                      "  shoot.cur=%.2g shoot.fit=%.2g\n",
672                      nn, blue->ref.cur / 64.0, blue->ref.fit / 64.0,
673                      blue->shoot.cur / 64.0, blue->shoot.fit / 64.0 ));
674
675          blue->flags |= AF_LATIN_BLUE_ACTIVE;
676        }
677      }
678    }
679  }
680
681
682  FT_LOCAL_DEF( void )
683  af_latin2_metrics_scale( AF_LatinMetrics  metrics,
684                           AF_Scaler        scaler )
685  {
686    metrics->root.scaler.render_mode = scaler->render_mode;
687    metrics->root.scaler.face        = scaler->face;
688    metrics->root.scaler.flags       = scaler->flags;
689
690    af_latin2_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
691    af_latin2_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
692  }
693
694
695  /*************************************************************************/
696  /*************************************************************************/
697  /*****                                                               *****/
698  /*****           L A T I N   G L Y P H   A N A L Y S I S             *****/
699  /*****                                                               *****/
700  /*************************************************************************/
701  /*************************************************************************/
702
703#define  SORT_SEGMENTS
704
705  FT_LOCAL_DEF( FT_Error )
706  af_latin2_hints_compute_segments( AF_GlyphHints  hints,
707                                    AF_Dimension   dim )
708  {
709    AF_AxisHints  axis          = &hints->axis[dim];
710    FT_Memory     memory        = hints->memory;
711    FT_Error      error         = FT_Err_Ok;
712    AF_Segment    segment       = NULL;
713    AF_SegmentRec seg0;
714    AF_Point*     contour       = hints->contours;
715    AF_Point*     contour_limit = contour + hints->num_contours;
716    AF_Direction  major_dir, segment_dir;
717
718
719    FT_ZERO( &seg0 );
720    seg0.score = 32000;
721    seg0.flags = AF_EDGE_NORMAL;
722
723    major_dir   = (AF_Direction)FT_ABS( axis->major_dir );
724    segment_dir = major_dir;
725
726    axis->num_segments = 0;
727
728    /* set up (u,v) in each point */
729    if ( dim == AF_DIMENSION_HORZ )
730    {
731      AF_Point  point = hints->points;
732      AF_Point  limit = point + hints->num_points;
733
734
735      for ( ; point < limit; point++ )
736      {
737        point->u = point->fx;
738        point->v = point->fy;
739      }
740    }
741    else
742    {
743      AF_Point  point = hints->points;
744      AF_Point  limit = point + hints->num_points;
745
746
747      for ( ; point < limit; point++ )
748      {
749        point->u = point->fy;
750        point->v = point->fx;
751      }
752    }
753
754    /* do each contour separately */
755    for ( ; contour < contour_limit; contour++ )
756    {
757      AF_Point  point   =  contour[0];
758      AF_Point  start   =  point;
759      AF_Point  last    =  point->prev;
760
761
762      if ( point == last )  /* skip singletons -- just in case */
763        continue;
764
765      /* already on an edge ?, backtrack to find its start */
766      if ( FT_ABS( point->in_dir ) == major_dir )
767      {
768        point = point->prev;
769
770        while ( point->in_dir == start->in_dir )
771          point = point->prev;
772      }
773      else  /* otherwise, find first segment start, if any */
774      {
775        while ( FT_ABS( point->out_dir ) != major_dir )
776        {
777          point = point->next;
778
779          if ( point == start )
780            goto NextContour;
781        }
782      }
783
784      start = point;
785
786      for  (;;)
787      {
788        AF_Point  first;
789        FT_Pos    min_u, min_v, max_u, max_v;
790
791        /* we're at the start of a new segment */
792        FT_ASSERT( FT_ABS( point->out_dir ) == major_dir &&
793                           point->in_dir != point->out_dir );
794        first = point;
795
796        min_u = max_u = point->u;
797        min_v = max_v = point->v;
798
799        point = point->next;
800
801        while ( point->out_dir == first->out_dir )
802        {
803          point = point->next;
804
805          if ( point->u < min_u )
806            min_u = point->u;
807
808          if ( point->u > max_u )
809            max_u = point->u;
810        }
811
812        if ( point->v < min_v )
813          min_v = point->v;
814
815        if ( point->v > max_v )
816          max_v = point->v;
817
818        /* record new segment */
819        error = af_axis_hints_new_segment( axis, memory, &segment );
820        if ( error )
821          goto Exit;
822
823        segment[0]         = seg0;
824        segment->dir       = first->out_dir;
825        segment->first     = first;
826        segment->last      = point;
827        segment->pos       = (FT_Short)( ( min_u + max_u ) >> 1 );
828        segment->min_coord = (FT_Short) min_v;
829        segment->max_coord = (FT_Short) max_v;
830        segment->height    = (FT_Short)( max_v - min_v );
831
832        /* a segment is round if it doesn't have successive */
833        /* on-curve points.                                 */
834        {
835          AF_Point  pt   = first;
836          AF_Point  last = point;
837          AF_Flags  f0   = (AF_Flags)( pt->flags & AF_FLAG_CONTROL );
838          AF_Flags  f1;
839
840
841          segment->flags &= ~AF_EDGE_ROUND;
842
843          for ( ; pt != last; f0 = f1 )
844          {
845            pt = pt->next;
846            f1 = (AF_Flags)( pt->flags & AF_FLAG_CONTROL );
847
848            if ( !f0 && !f1 )
849              break;
850
851            if ( pt == last )
852              segment->flags |= AF_EDGE_ROUND;
853          }
854        }
855
856       /* this can happen in the case of a degenerate contour
857        * e.g. a 2-point vertical contour
858        */
859        if ( point == start )
860          break;
861
862        /* jump to the start of the next segment, if any */
863        while ( FT_ABS( point->out_dir ) != major_dir )
864        {
865          point = point->next;
866
867          if ( point == start )
868            goto NextContour;
869        }
870      }
871
872    NextContour:
873      ;
874    } /* contours */
875
876    /* now slightly increase the height of segments when this makes */
877    /* sense -- this is used to better detect and ignore serifs     */
878    {
879      AF_Segment  segments     = axis->segments;
880      AF_Segment  segments_end = segments + axis->num_segments;
881
882
883      for ( segment = segments; segment < segments_end; segment++ )
884      {
885        AF_Point  first   = segment->first;
886        AF_Point  last    = segment->last;
887        AF_Point  p;
888        FT_Pos    first_v = first->v;
889        FT_Pos    last_v  = last->v;
890
891
892        if ( first == last )
893          continue;
894
895        if ( first_v < last_v )
896        {
897          p = first->prev;
898          if ( p->v < first_v )
899            segment->height = (FT_Short)( segment->height +
900                                          ( ( first_v - p->v ) >> 1 ) );
901
902          p = last->next;
903          if ( p->v > last_v )
904            segment->height = (FT_Short)( segment->height +
905                                          ( ( p->v - last_v ) >> 1 ) );
906        }
907        else
908        {
909          p = first->prev;
910          if ( p->v > first_v )
911            segment->height = (FT_Short)( segment->height +
912                                          ( ( p->v - first_v ) >> 1 ) );
913
914          p = last->next;
915          if ( p->v < last_v )
916            segment->height = (FT_Short)( segment->height +
917                                          ( ( last_v - p->v ) >> 1 ) );
918        }
919      }
920    }
921
922#ifdef AF_SORT_SEGMENTS
923   /* place all segments with a negative direction to the start
924    * of the array, used to speed up segment linking later...
925    */
926    {
927      AF_Segment  segments = axis->segments;
928      FT_UInt     count    = axis->num_segments;
929      FT_UInt     ii, jj;
930
931      for ( ii = 0; ii < count; ii++ )
932      {
933        if ( segments[ii].dir > 0 )
934        {
935          for ( jj = ii + 1; jj < count; jj++ )
936          {
937            if ( segments[jj].dir < 0 )
938            {
939              AF_SegmentRec  tmp;
940
941
942              tmp          = segments[ii];
943              segments[ii] = segments[jj];
944              segments[jj] = tmp;
945
946              break;
947            }
948          }
949
950          if ( jj == count )
951            break;
952        }
953      }
954      axis->mid_segments = ii;
955    }
956#endif
957
958  Exit:
959    return error;
960  }
961
962
963  FT_LOCAL_DEF( void )
964  af_latin2_hints_link_segments( AF_GlyphHints  hints,
965                                 AF_Dimension   dim )
966  {
967    AF_AxisHints  axis          = &hints->axis[dim];
968    AF_Segment    segments      = axis->segments;
969    AF_Segment    segment_limit = segments + axis->num_segments;
970#ifdef AF_SORT_SEGMENTS
971    AF_Segment    segment_mid   = segments + axis->mid_segments;
972#endif
973    FT_Pos        len_threshold, len_score;
974    AF_Segment    seg1, seg2;
975
976
977    len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
978    if ( len_threshold == 0 )
979      len_threshold = 1;
980
981    len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 );
982
983#ifdef AF_SORT_SEGMENTS
984    for ( seg1 = segments; seg1 < segment_mid; seg1++ )
985    {
986      if ( seg1->dir != axis->major_dir || seg1->first == seg1->last )
987        continue;
988
989      for ( seg2 = segment_mid; seg2 < segment_limit; seg2++ )
990#else
991    /* now compare each segment to the others */
992    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
993    {
994      /* the fake segments are introduced to hint the metrics -- */
995      /* we must never link them to anything                     */
996      if ( seg1->dir != axis->major_dir || seg1->first == seg1->last )
997        continue;
998
999      for ( seg2 = segments; seg2 < segment_limit; seg2++ )
1000        if ( seg1->dir + seg2->dir == 0 && seg2->pos > seg1->pos )
1001#endif
1002        {
1003          FT_Pos  pos1 = seg1->pos;
1004          FT_Pos  pos2 = seg2->pos;
1005          FT_Pos  dist = pos2 - pos1;
1006
1007
1008          if ( dist < 0 )
1009            continue;
1010
1011          {
1012            FT_Pos  min = seg1->min_coord;
1013            FT_Pos  max = seg1->max_coord;
1014            FT_Pos  len, score;
1015
1016
1017            if ( min < seg2->min_coord )
1018              min = seg2->min_coord;
1019
1020            if ( max > seg2->max_coord )
1021              max = seg2->max_coord;
1022
1023            len = max - min;
1024            if ( len >= len_threshold )
1025            {
1026              score = dist + len_score / len;
1027              if ( score < seg1->score )
1028              {
1029                seg1->score = score;
1030                seg1->link  = seg2;
1031              }
1032
1033              if ( score < seg2->score )
1034              {
1035                seg2->score = score;
1036                seg2->link  = seg1;
1037              }
1038            }
1039          }
1040        }
1041    }
1042#if 0
1043    }
1044#endif
1045
1046    /* now, compute the `serif' segments */
1047    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
1048    {
1049      seg2 = seg1->link;
1050
1051      if ( seg2 )
1052      {
1053        if ( seg2->link != seg1 )
1054        {
1055          seg1->link  = 0;
1056          seg1->serif = seg2->link;
1057        }
1058      }
1059    }
1060  }
1061
1062
1063  FT_LOCAL_DEF( FT_Error )
1064  af_latin2_hints_compute_edges( AF_GlyphHints  hints,
1065                                 AF_Dimension   dim )
1066  {
1067    AF_AxisHints  axis   = &hints->axis[dim];
1068    FT_Error      error  = FT_Err_Ok;
1069    FT_Memory     memory = hints->memory;
1070    AF_LatinAxis  laxis  = &((AF_LatinMetrics)hints->metrics)->axis[dim];
1071
1072    AF_Segment    segments      = axis->segments;
1073    AF_Segment    segment_limit = segments + axis->num_segments;
1074    AF_Segment    seg;
1075
1076    AF_Direction  up_dir;
1077    FT_Fixed      scale;
1078    FT_Pos        edge_distance_threshold;
1079    FT_Pos        segment_length_threshold;
1080
1081
1082    axis->num_edges = 0;
1083
1084    scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
1085                                         : hints->y_scale;
1086
1087    up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
1088                                          : AF_DIR_RIGHT;
1089
1090    /*
1091     *  We want to ignore very small (mostly serif) segments, we do that
1092     *  by ignoring those that whose length is less than a given fraction
1093     *  of the standard width. If there is no standard width, we ignore
1094     *  those that are less than a given size in pixels
1095     *
1096     *  also, unlink serif segments that are linked to segments farther
1097     *  than 50% of the standard width
1098     */
1099    if ( dim == AF_DIMENSION_HORZ )
1100    {
1101      if ( laxis->width_count > 0 )
1102        segment_length_threshold = ( laxis->standard_width * 10 ) >> 4;
1103      else
1104        segment_length_threshold = FT_DivFix( 64, hints->y_scale );
1105    }
1106    else
1107      segment_length_threshold = 0;
1108
1109    /*********************************************************************/
1110    /*                                                                   */
1111    /* We will begin by generating a sorted table of edges for the       */
1112    /* current direction.  To do so, we simply scan each segment and try */
1113    /* to find an edge in our table that corresponds to its position.    */
1114    /*                                                                   */
1115    /* If no edge is found, we create and insert a new edge in the       */
1116    /* sorted table.  Otherwise, we simply add the segment to the edge's */
1117    /* list which will be processed in the second step to compute the    */
1118    /* edge's properties.                                                */
1119    /*                                                                   */
1120    /* Note that the edges table is sorted along the segment/edge        */
1121    /* position.                                                         */
1122    /*                                                                   */
1123    /*********************************************************************/
1124
1125    edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
1126                                         scale );
1127    if ( edge_distance_threshold > 64 / 4 )
1128      edge_distance_threshold = 64 / 4;
1129
1130    edge_distance_threshold = FT_DivFix( edge_distance_threshold,
1131                                         scale );
1132
1133    for ( seg = segments; seg < segment_limit; seg++ )
1134    {
1135      AF_Edge  found = 0;
1136      FT_Int   ee;
1137
1138
1139      if ( seg->height < segment_length_threshold )
1140        continue;
1141
1142      /* A special case for serif edges: If they are smaller than */
1143      /* 1.5 pixels we ignore them.                               */
1144      if ( seg->serif )
1145      {
1146        FT_Pos  dist = seg->serif->pos - seg->pos;
1147
1148
1149        if ( dist < 0 )
1150          dist = -dist;
1151
1152        if ( dist >= laxis->standard_width >> 1 )
1153        {
1154          /* unlink this serif, it is too distant from its reference stem */
1155          seg->serif = NULL;
1156        }
1157        else if ( 2*seg->height < 3 * segment_length_threshold )
1158          continue;
1159      }
1160
1161      /* look for an edge corresponding to the segment */
1162      for ( ee = 0; ee < axis->num_edges; ee++ )
1163      {
1164        AF_Edge  edge = axis->edges + ee;
1165        FT_Pos   dist;
1166
1167
1168        dist = seg->pos - edge->fpos;
1169        if ( dist < 0 )
1170          dist = -dist;
1171
1172        if ( dist < edge_distance_threshold && edge->dir == seg->dir )
1173        {
1174          found = edge;
1175          break;
1176        }
1177      }
1178
1179      if ( !found )
1180      {
1181        AF_Edge   edge;
1182
1183
1184        /* insert a new edge in the list and */
1185        /* sort according to the position    */
1186        error = af_axis_hints_new_edge( axis, seg->pos, seg->dir,
1187                                        memory, &edge );
1188        if ( error )
1189          goto Exit;
1190
1191        /* add the segment to the new edge's list */
1192        FT_ZERO( edge );
1193
1194        edge->first    = seg;
1195        edge->last     = seg;
1196        edge->fpos     = seg->pos;
1197        edge->dir      = seg->dir;
1198        edge->opos     = edge->pos = FT_MulFix( seg->pos, scale );
1199        seg->edge_next = seg;
1200      }
1201      else
1202      {
1203        /* if an edge was found, simply add the segment to the edge's */
1204        /* list                                                       */
1205        seg->edge_next         = found->first;
1206        found->last->edge_next = seg;
1207        found->last            = seg;
1208      }
1209    }
1210
1211
1212    /*********************************************************************/
1213    /*                                                                   */
1214    /* Good, we will now compute each edge's properties according to     */
1215    /* segments found on its position.  Basically, these are:            */
1216    /*                                                                   */
1217    /*  - edge's main direction                                          */
1218    /*  - stem edge, serif edge or both (which defaults to stem then)    */
1219    /*  - rounded edge, straight or both (which defaults to straight)    */
1220    /*  - link for edge                                                  */
1221    /*                                                                   */
1222    /*********************************************************************/
1223
1224    /* first of all, set the `edge' field in each segment -- this is */
1225    /* required in order to compute edge links                       */
1226
1227    /*
1228     * Note that removing this loop and setting the `edge' field of each
1229     * segment directly in the code above slows down execution speed for
1230     * some reasons on platforms like the Sun.
1231     */
1232    {
1233      AF_Edge  edges      = axis->edges;
1234      AF_Edge  edge_limit = edges + axis->num_edges;
1235      AF_Edge  edge;
1236
1237
1238      for ( edge = edges; edge < edge_limit; edge++ )
1239      {
1240        seg = edge->first;
1241        if ( seg )
1242          do
1243          {
1244            seg->edge = edge;
1245            seg       = seg->edge_next;
1246
1247          } while ( seg != edge->first );
1248      }
1249
1250      /* now, compute each edge properties */
1251      for ( edge = edges; edge < edge_limit; edge++ )
1252      {
1253        FT_Int  is_round    = 0;  /* does it contain round segments?    */
1254        FT_Int  is_straight = 0;  /* does it contain straight segments? */
1255#if 0
1256        FT_Pos  ups         = 0;  /* number of upwards segments         */
1257        FT_Pos  downs       = 0;  /* number of downwards segments       */
1258#endif
1259
1260
1261        seg = edge->first;
1262
1263        do
1264        {
1265          FT_Bool  is_serif;
1266
1267
1268          /* check for roundness of segment */
1269          if ( seg->flags & AF_EDGE_ROUND )
1270            is_round++;
1271          else
1272            is_straight++;
1273
1274#if 0
1275          /* check for segment direction */
1276          if ( seg->dir == up_dir )
1277            ups   += seg->max_coord-seg->min_coord;
1278          else
1279            downs += seg->max_coord-seg->min_coord;
1280#endif
1281
1282          /* check for links -- if seg->serif is set, then seg->link must */
1283          /* be ignored                                                   */
1284          is_serif = (FT_Bool)( seg->serif               &&
1285                                seg->serif->edge         &&
1286                                seg->serif->edge != edge );
1287
1288          if ( ( seg->link && seg->link->edge != NULL ) || is_serif )
1289          {
1290            AF_Edge     edge2;
1291            AF_Segment  seg2;
1292
1293
1294            edge2 = edge->link;
1295            seg2  = seg->link;
1296
1297            if ( is_serif )
1298            {
1299              seg2  = seg->serif;
1300              edge2 = edge->serif;
1301            }
1302
1303            if ( edge2 )
1304            {
1305              FT_Pos  edge_delta;
1306              FT_Pos  seg_delta;
1307
1308
1309              edge_delta = edge->fpos - edge2->fpos;
1310              if ( edge_delta < 0 )
1311                edge_delta = -edge_delta;
1312
1313              seg_delta = seg->pos - seg2->pos;
1314              if ( seg_delta < 0 )
1315                seg_delta = -seg_delta;
1316
1317              if ( seg_delta < edge_delta )
1318                edge2 = seg2->edge;
1319            }
1320            else
1321              edge2 = seg2->edge;
1322
1323            if ( is_serif )
1324            {
1325              edge->serif   = edge2;
1326              edge2->flags |= AF_EDGE_SERIF;
1327            }
1328            else
1329              edge->link  = edge2;
1330          }
1331
1332          seg = seg->edge_next;
1333
1334        } while ( seg != edge->first );
1335
1336        /* set the round/straight flags */
1337        edge->flags = AF_EDGE_NORMAL;
1338
1339        if ( is_round > 0 && is_round >= is_straight )
1340          edge->flags |= AF_EDGE_ROUND;
1341
1342#if 0
1343        /* set the edge's main direction */
1344        edge->dir = AF_DIR_NONE;
1345
1346        if ( ups > downs )
1347          edge->dir = (FT_Char)up_dir;
1348
1349        else if ( ups < downs )
1350          edge->dir = (FT_Char)-up_dir;
1351
1352        else if ( ups == downs )
1353          edge->dir = 0;  /* both up and down! */
1354#endif
1355
1356        /* gets rid of serifs if link is set                */
1357        /* XXX: This gets rid of many unpleasant artefacts! */
1358        /*      Example: the `c' in cour.pfa at size 13     */
1359
1360        if ( edge->serif && edge->link )
1361          edge->serif = 0;
1362      }
1363    }
1364
1365  Exit:
1366    return error;
1367  }
1368
1369
1370  FT_LOCAL_DEF( FT_Error )
1371  af_latin2_hints_detect_features( AF_GlyphHints  hints,
1372                                   AF_Dimension   dim )
1373  {
1374    FT_Error  error;
1375
1376
1377    error = af_latin2_hints_compute_segments( hints, dim );
1378    if ( !error )
1379    {
1380      af_latin2_hints_link_segments( hints, dim );
1381
1382      error = af_latin2_hints_compute_edges( hints, dim );
1383    }
1384    return error;
1385  }
1386
1387
1388  FT_LOCAL_DEF( void )
1389  af_latin2_hints_compute_blue_edges( AF_GlyphHints    hints,
1390                                      AF_LatinMetrics  metrics )
1391  {
1392    AF_AxisHints  axis       = &hints->axis[AF_DIMENSION_VERT];
1393    AF_Edge       edge       = axis->edges;
1394    AF_Edge       edge_limit = edge + axis->num_edges;
1395    AF_LatinAxis  latin      = &metrics->axis[AF_DIMENSION_VERT];
1396    FT_Fixed      scale      = latin->scale;
1397    FT_Pos        best_dist0;  /* initial threshold */
1398
1399
1400    /* compute the initial threshold as a fraction of the EM size */
1401    best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale );
1402
1403    if ( best_dist0 > 64 / 2 )
1404      best_dist0 = 64 / 2;
1405
1406    /* compute which blue zones are active, i.e. have their scaled */
1407    /* size < 3/4 pixels                                           */
1408
1409    /* for each horizontal edge search the blue zone which is closest */
1410    for ( ; edge < edge_limit; edge++ )
1411    {
1412      FT_Int    bb;
1413      AF_Width  best_blue = NULL;
1414      FT_Pos    best_dist = best_dist0;
1415
1416      for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
1417      {
1418        AF_LatinBlue  blue = latin->blues + bb;
1419        FT_Bool       is_top_blue, is_major_dir;
1420
1421
1422        /* skip inactive blue zones (i.e., those that are too small) */
1423        if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
1424          continue;
1425
1426        /* if it is a top zone, check for right edges -- if it is a bottom */
1427        /* zone, check for left edges                                      */
1428        /*                                                                 */
1429        /* of course, that's for TrueType                                  */
1430        is_top_blue  = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 );
1431        is_major_dir = FT_BOOL( edge->dir == axis->major_dir );
1432
1433        /* if it is a top zone, the edge must be against the major    */
1434        /* direction; if it is a bottom zone, it must be in the major */
1435        /* direction                                                  */
1436        if ( is_top_blue ^ is_major_dir )
1437        {
1438          FT_Pos     dist;
1439          AF_Width   compare;
1440
1441
1442          /* if it's a rounded edge, compare it to the overshoot position */
1443          /* if it's a flat edge, compare it to the reference position    */
1444          if ( edge->flags & AF_EDGE_ROUND )
1445            compare = &blue->shoot;
1446          else
1447            compare = &blue->ref;
1448
1449          dist = edge->fpos - compare->org;
1450          if ( dist < 0 )
1451            dist = -dist;
1452
1453          dist = FT_MulFix( dist, scale );
1454          if ( dist < best_dist )
1455          {
1456            best_dist = dist;
1457            best_blue = compare;
1458          }
1459
1460#if 0
1461          /* now, compare it to the overshoot position if the edge is     */
1462          /* rounded, and if the edge is over the reference position of a */
1463          /* top zone, or under the reference position of a bottom zone   */
1464          if ( edge->flags & AF_EDGE_ROUND && dist != 0 )
1465          {
1466            FT_Bool  is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
1467
1468
1469            if ( is_top_blue ^ is_under_ref )
1470            {
1471              blue = latin->blues + bb;
1472              dist = edge->fpos - blue->shoot.org;
1473              if ( dist < 0 )
1474                dist = -dist;
1475
1476              dist = FT_MulFix( dist, scale );
1477              if ( dist < best_dist )
1478              {
1479                best_dist = dist;
1480                best_blue = & blue->shoot;
1481              }
1482            }
1483          }
1484#endif
1485        }
1486      }
1487
1488      if ( best_blue )
1489        edge->blue_edge = best_blue;
1490    }
1491  }
1492
1493
1494  static FT_Error
1495  af_latin2_hints_init( AF_GlyphHints    hints,
1496                        AF_LatinMetrics  metrics )
1497  {
1498    FT_Render_Mode  mode;
1499    FT_UInt32       scaler_flags, other_flags;
1500    FT_Face         face = metrics->root.scaler.face;
1501
1502
1503    af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics );
1504
1505    /*
1506     *  correct x_scale and y_scale if needed, since they may have
1507     *  been modified `af_latin2_metrics_scale_dim' above
1508     */
1509    hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
1510    hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
1511    hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
1512    hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
1513
1514    /* compute flags depending on render mode, etc. */
1515    mode = metrics->root.scaler.render_mode;
1516
1517#if 0 /* #ifdef AF_CONFIG_OPTION_USE_WARPER */
1518    if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
1519    {
1520      metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
1521    }
1522#endif
1523
1524    scaler_flags = hints->scaler_flags;
1525    other_flags  = 0;
1526
1527    /*
1528     *  We snap the width of vertical stems for the monochrome and
1529     *  horizontal LCD rendering targets only.
1530     */
1531    if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
1532      other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
1533
1534    /*
1535     *  We snap the width of horizontal stems for the monochrome and
1536     *  vertical LCD rendering targets only.
1537     */
1538    if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
1539      other_flags |= AF_LATIN_HINTS_VERT_SNAP;
1540
1541    /*
1542     *  We adjust stems to full pixels only if we don't use the `light' mode.
1543     */
1544    if ( mode != FT_RENDER_MODE_LIGHT )
1545      other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
1546
1547    if ( mode == FT_RENDER_MODE_MONO )
1548      other_flags |= AF_LATIN_HINTS_MONO;
1549
1550    /*
1551     *  In `light' hinting mode we disable horizontal hinting completely.
1552     *  We also do it if the face is italic.
1553     */
1554    if ( mode == FT_RENDER_MODE_LIGHT                      ||
1555         ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 )
1556      scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
1557
1558    hints->scaler_flags = scaler_flags;
1559    hints->other_flags  = other_flags;
1560
1561    return 0;
1562  }
1563
1564
1565  /*************************************************************************/
1566  /*************************************************************************/
1567  /*****                                                               *****/
1568  /*****        L A T I N   G L Y P H   G R I D - F I T T I N G        *****/
1569  /*****                                                               *****/
1570  /*************************************************************************/
1571  /*************************************************************************/
1572
1573  /* snap a given width in scaled coordinates to one of the */
1574  /* current standard widths                                */
1575
1576  static FT_Pos
1577  af_latin2_snap_width( AF_Width  widths,
1578                        FT_Int    count,
1579                        FT_Pos    width )
1580  {
1581    int     n;
1582    FT_Pos  best      = 64 + 32 + 2;
1583    FT_Pos  reference = width;
1584    FT_Pos  scaled;
1585
1586
1587    for ( n = 0; n < count; n++ )
1588    {
1589      FT_Pos  w;
1590      FT_Pos  dist;
1591
1592
1593      w = widths[n].cur;
1594      dist = width - w;
1595      if ( dist < 0 )
1596        dist = -dist;
1597      if ( dist < best )
1598      {
1599        best      = dist;
1600        reference = w;
1601      }
1602    }
1603
1604    scaled = FT_PIX_ROUND( reference );
1605
1606    if ( width >= reference )
1607    {
1608      if ( width < scaled + 48 )
1609        width = reference;
1610    }
1611    else
1612    {
1613      if ( width > scaled - 48 )
1614        width = reference;
1615    }
1616
1617    return width;
1618  }
1619
1620
1621  /* compute the snapped width of a given stem */
1622
1623  static FT_Pos
1624  af_latin2_compute_stem_width( AF_GlyphHints  hints,
1625                                AF_Dimension   dim,
1626                                FT_Pos         width,
1627                                AF_Edge_Flags  base_flags,
1628                                AF_Edge_Flags  stem_flags )
1629  {
1630    AF_LatinMetrics  metrics  = (AF_LatinMetrics) hints->metrics;
1631    AF_LatinAxis     axis     = & metrics->axis[dim];
1632    FT_Pos           dist     = width;
1633    FT_Int           sign     = 0;
1634    FT_Int           vertical = ( dim == AF_DIMENSION_VERT );
1635
1636    FT_UNUSED( base_flags );
1637
1638
1639    if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ||
1640          axis->extra_light                      )
1641      return width;
1642
1643    if ( dist < 0 )
1644    {
1645      dist = -width;
1646      sign = 1;
1647    }
1648
1649    if ( (  vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
1650         ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
1651    {
1652      /* smooth hinting process: very lightly quantize the stem width */
1653
1654      /* leave the widths of serifs alone */
1655
1656      if ( ( stem_flags & AF_EDGE_SERIF ) && vertical && ( dist < 3 * 64 ) )
1657        goto Done_Width;
1658
1659#if 0
1660      else if ( ( base_flags & AF_EDGE_ROUND ) )
1661      {
1662        if ( dist < 80 )
1663          dist = 64;
1664      }
1665      else if ( dist < 56 )
1666        dist = 56;
1667#endif
1668      if ( axis->width_count > 0 )
1669      {
1670        FT_Pos  delta;
1671
1672
1673        /* compare to standard width */
1674        if ( axis->width_count > 0 )
1675        {
1676          delta = dist - axis->widths[0].cur;
1677
1678          if ( delta < 0 )
1679            delta = -delta;
1680
1681          if ( delta < 40 )
1682          {
1683            dist = axis->widths[0].cur;
1684            if ( dist < 48 )
1685              dist = 48;
1686
1687            goto Done_Width;
1688          }
1689        }
1690
1691        if ( dist < 3 * 64 )
1692        {
1693          delta  = dist & 63;
1694          dist  &= -64;
1695
1696          if ( delta < 10 )
1697            dist += delta;
1698
1699          else if ( delta < 32 )
1700            dist += 10;
1701
1702          else if ( delta < 54 )
1703            dist += 54;
1704
1705          else
1706            dist += delta;
1707        }
1708        else
1709          dist = ( dist + 32 ) & ~63;
1710      }
1711    }
1712    else
1713    {
1714      /* strong hinting process: snap the stem width to integer pixels */
1715      FT_Pos  org_dist = dist;
1716
1717
1718      dist = af_latin2_snap_width( axis->widths, axis->width_count, dist );
1719
1720      if ( vertical )
1721      {
1722        /* in the case of vertical hinting, always round */
1723        /* the stem heights to integer pixels            */
1724
1725        if ( dist >= 64 )
1726          dist = ( dist + 16 ) & ~63;
1727        else
1728          dist = 64;
1729      }
1730      else
1731      {
1732        if ( AF_LATIN_HINTS_DO_MONO( hints ) )
1733        {
1734          /* monochrome horizontal hinting: snap widths to integer pixels */
1735          /* with a different threshold                                   */
1736
1737          if ( dist < 64 )
1738            dist = 64;
1739          else
1740            dist = ( dist + 32 ) & ~63;
1741        }
1742        else
1743        {
1744          /* for horizontal anti-aliased hinting, we adopt a more subtle */
1745          /* approach: we strengthen small stems, round stems whose size */
1746          /* is between 1 and 2 pixels to an integer, otherwise nothing  */
1747
1748          if ( dist < 48 )
1749            dist = ( dist + 64 ) >> 1;
1750
1751          else if ( dist < 128 )
1752          {
1753            /* We only round to an integer width if the corresponding */
1754            /* distortion is less than 1/4 pixel.  Otherwise this     */
1755            /* makes everything worse since the diagonals, which are  */
1756            /* not hinted, appear a lot bolder or thinner than the    */
1757            /* vertical stems.                                        */
1758
1759            FT_Int  delta;
1760
1761
1762            dist = ( dist + 22 ) & ~63;
1763            delta = dist - org_dist;
1764            if ( delta < 0 )
1765              delta = -delta;
1766
1767            if ( delta >= 16 )
1768            {
1769              dist = org_dist;
1770              if ( dist < 48 )
1771                dist = ( dist + 64 ) >> 1;
1772            }
1773          }
1774          else
1775            /* round otherwise to prevent color fringes in LCD mode */
1776            dist = ( dist + 32 ) & ~63;
1777        }
1778      }
1779    }
1780
1781  Done_Width:
1782    if ( sign )
1783      dist = -dist;
1784
1785    return dist;
1786  }
1787
1788
1789  /* align one stem edge relative to the previous stem edge */
1790
1791  static void
1792  af_latin2_align_linked_edge( AF_GlyphHints  hints,
1793                               AF_Dimension   dim,
1794                               AF_Edge        base_edge,
1795                               AF_Edge        stem_edge )
1796  {
1797    FT_Pos  dist = stem_edge->opos - base_edge->opos;
1798
1799    FT_Pos  fitted_width = af_latin2_compute_stem_width(
1800                             hints, dim, dist,
1801                             (AF_Edge_Flags)base_edge->flags,
1802                             (AF_Edge_Flags)stem_edge->flags );
1803
1804
1805    stem_edge->pos = base_edge->pos + fitted_width;
1806
1807    FT_TRACE5(( "LINK: edge %d (opos=%.2f) linked to (%.2f), "
1808                "dist was %.2f, now %.2f\n",
1809                stem_edge-hints->axis[dim].edges, stem_edge->opos / 64.0,
1810                stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 ));
1811  }
1812
1813
1814  static void
1815  af_latin2_align_serif_edge( AF_GlyphHints  hints,
1816                              AF_Edge        base,
1817                              AF_Edge        serif )
1818  {
1819    FT_UNUSED( hints );
1820
1821    serif->pos = base->pos + ( serif->opos - base->opos );
1822  }
1823
1824
1825  /*************************************************************************/
1826  /*************************************************************************/
1827  /*************************************************************************/
1828  /****                                                                 ****/
1829  /****                    E D G E   H I N T I N G                      ****/
1830  /****                                                                 ****/
1831  /*************************************************************************/
1832  /*************************************************************************/
1833  /*************************************************************************/
1834
1835
1836  FT_LOCAL_DEF( void )
1837  af_latin2_hint_edges( AF_GlyphHints  hints,
1838                        AF_Dimension   dim )
1839  {
1840    AF_AxisHints  axis       = &hints->axis[dim];
1841    AF_Edge       edges      = axis->edges;
1842    AF_Edge       edge_limit = edges + axis->num_edges;
1843    AF_Edge       edge;
1844    AF_Edge       anchor     = 0;
1845    FT_Int        has_serifs = 0;
1846    FT_Pos        anchor_drift = 0;
1847
1848
1849
1850    FT_TRACE5(( "==== hinting %s edges =====\n",
1851                dim == AF_DIMENSION_HORZ ? "vertical" : "horizontal" ));
1852
1853    /* we begin by aligning all stems relative to the blue zone */
1854    /* if needed -- that's only for horizontal edges            */
1855
1856    if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
1857    {
1858      for ( edge = edges; edge < edge_limit; edge++ )
1859      {
1860        AF_Width  blue;
1861        AF_Edge   edge1, edge2;
1862
1863
1864        if ( edge->flags & AF_EDGE_DONE )
1865          continue;
1866
1867        blue  = edge->blue_edge;
1868        edge1 = NULL;
1869        edge2 = edge->link;
1870
1871        if ( blue )
1872        {
1873          edge1 = edge;
1874        }
1875        else if ( edge2 && edge2->blue_edge )
1876        {
1877          blue  = edge2->blue_edge;
1878          edge1 = edge2;
1879          edge2 = edge;
1880        }
1881
1882        if ( !edge1 )
1883          continue;
1884
1885        FT_TRACE5(( "BLUE: edge %d (opos=%.2f) snapped to (%.2f), "
1886                    "was (%.2f)\n",
1887                    edge1-edges, edge1->opos / 64.0, blue->fit / 64.0,
1888                    edge1->pos / 64.0 ));
1889
1890        edge1->pos    = blue->fit;
1891        edge1->flags |= AF_EDGE_DONE;
1892
1893        if ( edge2 && !edge2->blue_edge )
1894        {
1895          af_latin2_align_linked_edge( hints, dim, edge1, edge2 );
1896          edge2->flags |= AF_EDGE_DONE;
1897        }
1898
1899        if ( !anchor )
1900        {
1901          anchor = edge;
1902
1903          anchor_drift = ( anchor->pos - anchor->opos );
1904          if ( edge2 )
1905            anchor_drift = ( anchor_drift +
1906                             ( edge2->pos - edge2->opos ) ) >> 1;
1907        }
1908      }
1909    }
1910
1911    /* now we will align all stem edges, trying to maintain the */
1912    /* relative order of stems in the glyph                     */
1913    for ( edge = edges; edge < edge_limit; edge++ )
1914    {
1915      AF_Edge  edge2;
1916
1917
1918      if ( edge->flags & AF_EDGE_DONE )
1919        continue;
1920
1921      /* skip all non-stem edges */
1922      edge2 = edge->link;
1923      if ( !edge2 )
1924      {
1925        has_serifs++;
1926        continue;
1927      }
1928
1929      /* now align the stem */
1930
1931      /* this should not happen, but it's better to be safe */
1932      if ( edge2->blue_edge )
1933      {
1934        FT_TRACE5(( "ASSERTION FAILED for edge %d\n", edge2-edges ));
1935
1936        af_latin2_align_linked_edge( hints, dim, edge2, edge );
1937        edge->flags |= AF_EDGE_DONE;
1938        continue;
1939      }
1940
1941      if ( !anchor )
1942      {
1943        FT_Pos  org_len, org_center, cur_len;
1944        FT_Pos  cur_pos1, error1, error2, u_off, d_off;
1945
1946
1947        org_len = edge2->opos - edge->opos;
1948        cur_len = af_latin2_compute_stem_width(
1949                    hints, dim, org_len,
1950                    (AF_Edge_Flags)edge->flags,
1951                    (AF_Edge_Flags)edge2->flags );
1952        if ( cur_len <= 64 )
1953          u_off = d_off = 32;
1954        else
1955        {
1956          u_off = 38;
1957          d_off = 26;
1958        }
1959
1960        if ( cur_len < 96 )
1961        {
1962          org_center = edge->opos + ( org_len >> 1 );
1963
1964          cur_pos1   = FT_PIX_ROUND( org_center );
1965
1966          error1 = org_center - ( cur_pos1 - u_off );
1967          if ( error1 < 0 )
1968            error1 = -error1;
1969
1970          error2 = org_center - ( cur_pos1 + d_off );
1971          if ( error2 < 0 )
1972            error2 = -error2;
1973
1974          if ( error1 < error2 )
1975            cur_pos1 -= u_off;
1976          else
1977            cur_pos1 += d_off;
1978
1979          edge->pos  = cur_pos1 - cur_len / 2;
1980          edge2->pos = edge->pos + cur_len;
1981        }
1982        else
1983          edge->pos = FT_PIX_ROUND( edge->opos );
1984
1985        FT_TRACE5(( "ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
1986                    " snapped to (%.2f) (%.2f)\n",
1987                    edge-edges, edge->opos / 64.0,
1988                    edge2-edges, edge2->opos / 64.0,
1989                    edge->pos / 64.0, edge2->pos / 64.0 ));
1990        anchor = edge;
1991
1992        edge->flags |= AF_EDGE_DONE;
1993
1994        af_latin2_align_linked_edge( hints, dim, edge, edge2 );
1995
1996        edge2->flags |= AF_EDGE_DONE;
1997
1998        anchor_drift = ( ( anchor->pos - anchor->opos ) +
1999                         ( edge2->pos - edge2->opos ) ) >> 1;
2000
2001        FT_TRACE5(( "DRIFT: %.2f\n", anchor_drift/64.0 ));
2002      }
2003      else
2004      {
2005        FT_Pos   org_pos, org_len, org_center, cur_center, cur_len;
2006        FT_Pos   org_left, org_right;
2007
2008
2009        org_pos    = edge->opos + anchor_drift;
2010        org_len    = edge2->opos - edge->opos;
2011        org_center = org_pos + ( org_len >> 1 );
2012
2013        cur_len = af_latin2_compute_stem_width(
2014                   hints, dim, org_len,
2015                   (AF_Edge_Flags)edge->flags,
2016                   (AF_Edge_Flags)edge2->flags );
2017
2018        org_left  = org_pos + ( ( org_len - cur_len ) >> 1 );
2019        org_right = org_pos + ( ( org_len + cur_len ) >> 1 );
2020
2021        FT_TRACE5(( "ALIGN: left=%.2f right=%.2f ",
2022                    org_left / 64.0, org_right / 64.0 ));
2023        cur_center = org_center;
2024
2025        if ( edge2->flags & AF_EDGE_DONE )
2026        {
2027          FT_TRACE5(( "\n" ));
2028          edge->pos = edge2->pos - cur_len;
2029        }
2030        else
2031        {
2032         /* we want to compare several displacement, and choose
2033          * the one that increases fitness while minimizing
2034          * distortion as well
2035          */
2036          FT_Pos   displacements[6], scores[6], org, fit, delta;
2037          FT_UInt  count = 0;
2038
2039          /* note: don't even try to fit tiny stems */
2040          if ( cur_len < 32 )
2041          {
2042            FT_TRACE5(( "tiny stem\n" ));
2043            goto AlignStem;
2044          }
2045
2046          /* if the span is within a single pixel, don't touch it */
2047          if ( FT_PIX_FLOOR( org_left ) == FT_PIX_CEIL( org_right ) )
2048          {
2049            FT_TRACE5(( "single pixel stem\n" ));
2050            goto AlignStem;
2051          }
2052
2053          if ( cur_len <= 96 )
2054          {
2055           /* we want to avoid the absolute worst case which is
2056            * when the left and right edges of the span each represent
2057            * about 50% of the gray. we'd better want to change this
2058            * to 25/75%, since this is much more pleasant to the eye with
2059            * very acceptable distortion
2060            */
2061            FT_Pos  frac_left  = org_left  & 63;
2062            FT_Pos  frac_right = org_right & 63;
2063
2064            if ( frac_left  >= 22 && frac_left  <= 42 &&
2065                 frac_right >= 22 && frac_right <= 42 )
2066            {
2067              org = frac_left;
2068              fit = ( org <= 32 ) ? 16 : 48;
2069              delta = FT_ABS( fit - org );
2070              displacements[count] = fit - org;
2071              scores[count++]      = delta;
2072              FT_TRACE5(( "dispA=%.2f (%d) ", ( fit - org ) / 64.0, delta ));
2073
2074              org = frac_right;
2075              fit = ( org <= 32 ) ? 16 : 48;
2076              delta = FT_ABS( fit - org );
2077              displacements[count] = fit - org;
2078              scores[count++]     = delta;
2079              FT_TRACE5(( "dispB=%.2f (%d) ", ( fit - org ) / 64.0, delta ));
2080            }
2081          }
2082
2083          /* snapping the left edge to the grid */
2084          org   = org_left;
2085          fit   = FT_PIX_ROUND( org );
2086          delta = FT_ABS( fit - org );
2087          displacements[count] = fit - org;
2088          scores[count++]      = delta;
2089          FT_TRACE5(( "dispC=%.2f (%d) ", ( fit - org ) / 64.0, delta ));
2090
2091          /* snapping the right edge to the grid */
2092          org   = org_right;
2093          fit   = FT_PIX_ROUND( org );
2094          delta = FT_ABS( fit - org );
2095          displacements[count] = fit - org;
2096          scores[count++]      = delta;
2097          FT_TRACE5(( "dispD=%.2f (%d) ", ( fit - org ) / 64.0, delta ));
2098
2099          /* now find the best displacement */
2100          {
2101            FT_Pos  best_score = scores[0];
2102            FT_Pos  best_disp  = displacements[0];
2103            FT_UInt nn;
2104
2105            for ( nn = 1; nn < count; nn++ )
2106            {
2107              if ( scores[nn] < best_score )
2108              {
2109                best_score = scores[nn];
2110                best_disp  = displacements[nn];
2111              }
2112            }
2113
2114            cur_center = org_center + best_disp;
2115          }
2116          FT_TRACE5(( "\n" ));
2117        }
2118
2119      AlignStem:
2120        edge->pos  = cur_center - ( cur_len >> 1 );
2121        edge2->pos = edge->pos + cur_len;
2122
2123        FT_TRACE5(( "STEM1: %d (opos=%.2f) to %d (opos=%.2f)"
2124                    " snapped to (%.2f) and (%.2f),"
2125                    " org_len=%.2f cur_len=%.2f\n",
2126                    edge-edges, edge->opos / 64.0,
2127                    edge2-edges, edge2->opos / 64.0,
2128                    edge->pos / 64.0, edge2->pos / 64.0,
2129                    org_len / 64.0, cur_len / 64.0 ));
2130
2131        edge->flags  |= AF_EDGE_DONE;
2132        edge2->flags |= AF_EDGE_DONE;
2133
2134        if ( edge > edges && edge->pos < edge[-1].pos )
2135        {
2136          FT_TRACE5(( "BOUND: %d (pos=%.2f) to (%.2f)\n",
2137                      edge-edges, edge->pos / 64.0, edge[-1].pos / 64.0 ));
2138          edge->pos = edge[-1].pos;
2139        }
2140      }
2141    }
2142
2143    /* make sure that lowercase m's maintain their symmetry */
2144
2145    /* In general, lowercase m's have six vertical edges if they are sans */
2146    /* serif, or twelve if they are with serifs.  This implementation is  */
2147    /* based on that assumption, and seems to work very well with most    */
2148    /* faces.  However, if for a certain face this assumption is not      */
2149    /* true, the m is just rendered like before.  In addition, any stem   */
2150    /* correction will only be applied to symmetrical glyphs (even if the */
2151    /* glyph is not an m), so the potential for unwanted distortion is    */
2152    /* relatively low.                                                    */
2153
2154    /* We don't handle horizontal edges since we can't easily assure that */
2155    /* the third (lowest) stem aligns with the base line; it might end up */
2156    /* one pixel higher or lower.                                         */
2157
2158#if 0
2159    {
2160      FT_Int  n_edges = edge_limit - edges;
2161
2162
2163      if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
2164      {
2165        AF_Edge  edge1, edge2, edge3;
2166        FT_Pos   dist1, dist2, span, delta;
2167
2168
2169        if ( n_edges == 6 )
2170        {
2171          edge1 = edges;
2172          edge2 = edges + 2;
2173          edge3 = edges + 4;
2174        }
2175        else
2176        {
2177          edge1 = edges + 1;
2178          edge2 = edges + 5;
2179          edge3 = edges + 9;
2180        }
2181
2182        dist1 = edge2->opos - edge1->opos;
2183        dist2 = edge3->opos - edge2->opos;
2184
2185        span = dist1 - dist2;
2186        if ( span < 0 )
2187          span = -span;
2188
2189        if ( span < 8 )
2190        {
2191          delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
2192          edge3->pos -= delta;
2193          if ( edge3->link )
2194            edge3->link->pos -= delta;
2195
2196          /* move the serifs along with the stem */
2197          if ( n_edges == 12 )
2198          {
2199            ( edges + 8 )->pos -= delta;
2200            ( edges + 11 )->pos -= delta;
2201          }
2202
2203          edge3->flags |= AF_EDGE_DONE;
2204          if ( edge3->link )
2205            edge3->link->flags |= AF_EDGE_DONE;
2206        }
2207      }
2208    }
2209#endif
2210
2211    if ( has_serifs || !anchor )
2212    {
2213      /*
2214       *  now hint the remaining edges (serifs and single) in order
2215       *  to complete our processing
2216       */
2217      for ( edge = edges; edge < edge_limit; edge++ )
2218      {
2219        FT_Pos  delta;
2220
2221
2222        if ( edge->flags & AF_EDGE_DONE )
2223          continue;
2224
2225        delta = 1000;
2226
2227        if ( edge->serif )
2228        {
2229          delta = edge->serif->opos - edge->opos;
2230          if ( delta < 0 )
2231            delta = -delta;
2232        }
2233
2234        if ( delta < 64 + 16 )
2235        {
2236          af_latin2_align_serif_edge( hints, edge->serif, edge );
2237          FT_TRACE5(( "SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2238                      " aligned to (%.2f)\n",
2239                      edge-edges, edge->opos / 64.0,
2240                      edge->serif - edges, edge->serif->opos / 64.0,
2241                      edge->pos / 64.0 ));
2242        }
2243        else if ( !anchor )
2244        {
2245          FT_TRACE5(( "SERIF_ANCHOR: edge %d (opos=%.2f)"
2246                      " snapped to (%.2f)\n",
2247                      edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
2248          edge->pos = FT_PIX_ROUND( edge->opos );
2249          anchor    = edge;
2250        }
2251        else
2252        {
2253          AF_Edge  before, after;
2254
2255
2256          for ( before = edge - 1; before >= edges; before-- )
2257            if ( before->flags & AF_EDGE_DONE )
2258              break;
2259
2260          for ( after = edge + 1; after < edge_limit; after++ )
2261            if ( after->flags & AF_EDGE_DONE )
2262              break;
2263
2264          if ( before >= edges && before < edge   &&
2265               after < edge_limit && after > edge )
2266          {
2267            if ( after->opos == before->opos )
2268              edge->pos = before->pos;
2269            else
2270              edge->pos = before->pos +
2271                          FT_MulDiv( edge->opos - before->opos,
2272                                     after->pos - before->pos,
2273                                     after->opos - before->opos );
2274            FT_TRACE5(( "SERIF_LINK1: edge %d (opos=%.2f) snapped to (%.2f)"
2275                        " from %d (opos=%.2f)\n",
2276                        edge-edges, edge->opos / 64.0, edge->pos / 64.0,
2277                        before - edges, before->opos / 64.0 ));
2278          }
2279          else
2280          {
2281            edge->pos = anchor->pos +
2282                        ( ( edge->opos - anchor->opos + 16 ) & ~31 );
2283
2284            FT_TRACE5(( "SERIF_LINK2: edge %d (opos=%.2f)"
2285                        " snapped to (%.2f)\n",
2286                        edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
2287          }
2288        }
2289
2290        edge->flags |= AF_EDGE_DONE;
2291
2292        if ( edge > edges && edge->pos < edge[-1].pos )
2293          edge->pos = edge[-1].pos;
2294
2295        if ( edge + 1 < edge_limit        &&
2296             edge[1].flags & AF_EDGE_DONE &&
2297             edge->pos > edge[1].pos      )
2298          edge->pos = edge[1].pos;
2299      }
2300    }
2301  }
2302
2303
2304  static FT_Error
2305  af_latin2_hints_apply( AF_GlyphHints    hints,
2306                         FT_Outline*      outline,
2307                         AF_LatinMetrics  metrics )
2308  {
2309    FT_Error  error;
2310    int       dim;
2311
2312
2313    error = af_glyph_hints_reload( hints, outline );
2314    if ( error )
2315      goto Exit;
2316
2317    /* analyze glyph outline */
2318#ifdef AF_CONFIG_OPTION_USE_WARPER
2319    if ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ||
2320         AF_HINTS_DO_HORIZONTAL( hints ) )
2321#else
2322    if ( AF_HINTS_DO_HORIZONTAL( hints ) )
2323#endif
2324    {
2325      error = af_latin2_hints_detect_features( hints, AF_DIMENSION_HORZ );
2326      if ( error )
2327        goto Exit;
2328    }
2329
2330    if ( AF_HINTS_DO_VERTICAL( hints ) )
2331    {
2332      error = af_latin2_hints_detect_features( hints, AF_DIMENSION_VERT );
2333      if ( error )
2334        goto Exit;
2335
2336      af_latin2_hints_compute_blue_edges( hints, metrics );
2337    }
2338
2339    /* grid-fit the outline */
2340    for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
2341    {
2342#ifdef AF_CONFIG_OPTION_USE_WARPER
2343      if ( ( dim == AF_DIMENSION_HORZ &&
2344             metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ) )
2345      {
2346        AF_WarperRec  warper;
2347        FT_Fixed      scale;
2348        FT_Pos        delta;
2349
2350
2351        af_warper_compute( &warper, hints, dim, &scale, &delta );
2352        af_glyph_hints_scale_dim( hints, dim, scale, delta );
2353        continue;
2354      }
2355#endif
2356
2357      if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
2358           ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) )   )
2359      {
2360        af_latin2_hint_edges( hints, (AF_Dimension)dim );
2361        af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
2362        af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
2363        af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
2364      }
2365    }
2366    af_glyph_hints_save( hints, outline );
2367
2368  Exit:
2369    return error;
2370  }
2371
2372
2373  /*************************************************************************/
2374  /*************************************************************************/
2375  /*****                                                               *****/
2376  /*****              L A T I N   S C R I P T   C L A S S              *****/
2377  /*****                                                               *****/
2378  /*************************************************************************/
2379  /*************************************************************************/
2380
2381
2382  static const AF_Script_UniRangeRec  af_latin2_uniranges[] =
2383  {
2384    AF_UNIRANGE_REC( 32UL,  127UL ),    /* TODO: Add new Unicode ranges here! */
2385    AF_UNIRANGE_REC( 160UL, 255UL ),
2386    AF_UNIRANGE_REC( 0UL,   0UL )
2387  };
2388
2389
2390  AF_DEFINE_SCRIPT_CLASS( af_latin2_script_class,
2391    AF_SCRIPT_LATIN2,
2392    af_latin2_uniranges,
2393    'o',
2394
2395    sizeof ( AF_LatinMetricsRec ),
2396
2397    (AF_Script_InitMetricsFunc) af_latin2_metrics_init,
2398    (AF_Script_ScaleMetricsFunc)af_latin2_metrics_scale,
2399    (AF_Script_DoneMetricsFunc) NULL,
2400
2401    (AF_Script_InitHintsFunc)   af_latin2_hints_init,
2402    (AF_Script_ApplyHintsFunc)  af_latin2_hints_apply
2403  )
2404
2405
2406/* END */
2407