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