~reviczky/luatex/texlive-bin-git

« back to all changes in this revision

Viewing changes to libs/freetype2/freetype-2.5.3/src/autofit/aflatin.c

  • Committer: Adam Reviczky
  • Date: 2015-04-26 22:40:47 UTC
  • Revision ID: adam.reviczky@kclalumni.net-20150426224047-i2p26n3wqphupq6z
TeX Live 2015 import (rev. 37052)

Show diffs side-by-side

added added

removed removed

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