1 /***************************************************************************/
5 /* Auto-fitter hinting routines for latin script (body). */
7 /* Copyright 2003-2013 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
20 #include FT_ADVANCES_H
21 #include FT_INTERNAL_DEBUG_H
28 #ifdef AF_CONFIG_OPTION_USE_WARPER
33 /*************************************************************************/
35 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
36 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
37 /* messages during execution. */
40 #define FT_COMPONENT trace_aflatin
43 /*************************************************************************/
44 /*************************************************************************/
46 /***** L A T I N G L O B A L M E T R I C S *****/
48 /*************************************************************************/
49 /*************************************************************************/
52 /* Find segments and links, compute all stem widths, and initialize */
53 /* standard width and height for the glyph with given charcode. */
56 af_latin_metrics_init_widths( AF_LatinMetrics metrics
,
59 /* scan the array of segments in each direction */
60 AF_GlyphHintsRec hints
[1];
64 "latin standard widths computation (script `%s')\n"
65 "=================================================\n"
67 af_script_names
[metrics
->root
.script_class
->script
] ));
69 af_glyph_hints_init( hints
, face
->memory
);
71 metrics
->axis
[AF_DIMENSION_HORZ
].width_count
= 0;
72 metrics
->axis
[AF_DIMENSION_VERT
].width_count
= 0;
78 AF_LatinMetricsRec dummy
[1];
79 AF_Scaler scaler
= &dummy
->root
.scaler
;
82 glyph_index
= FT_Get_Char_Index(
84 metrics
->root
.script_class
->standard_char
);
85 if ( glyph_index
== 0 )
88 FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n",
89 metrics
->root
.script_class
->standard_char
, glyph_index
));
91 error
= FT_Load_Glyph( face
, glyph_index
, FT_LOAD_NO_SCALE
);
92 if ( error
|| face
->glyph
->outline
.n_points
<= 0 )
97 dummy
->units_per_em
= metrics
->units_per_em
;
99 scaler
->x_scale
= 0x10000L
;
100 scaler
->y_scale
= 0x10000L
;
105 scaler
->render_mode
= FT_RENDER_MODE_NORMAL
;
108 af_glyph_hints_rescale( hints
, (AF_ScriptMetrics
)dummy
);
110 error
= af_glyph_hints_reload( hints
, &face
->glyph
->outline
);
114 for ( dim
= 0; dim
< AF_DIMENSION_MAX
; dim
++ )
116 AF_LatinAxis axis
= &metrics
->axis
[dim
];
117 AF_AxisHints axhints
= &hints
->axis
[dim
];
118 AF_Segment seg
, limit
, link
;
119 FT_UInt num_widths
= 0;
122 error
= af_latin_hints_compute_segments( hints
,
127 af_latin_hints_link_segments( hints
,
130 seg
= axhints
->segments
;
131 limit
= seg
+ axhints
->num_segments
;
133 for ( ; seg
< limit
; seg
++ )
137 /* we only consider stem segments there! */
138 if ( link
&& link
->link
== seg
&& link
> seg
)
143 dist
= seg
->pos
- link
->pos
;
147 if ( num_widths
< AF_LATIN_MAX_WIDTHS
)
148 axis
->widths
[num_widths
++].org
= dist
;
152 /* this also replaces multiple almost identical stem widths */
153 /* with a single one (the value 100 is heuristic) */
154 af_sort_and_quantize_widths( &num_widths
, axis
->widths
,
155 dummy
->units_per_em
/ 100 );
156 axis
->width_count
= num_widths
;
160 for ( dim
= 0; dim
< AF_DIMENSION_MAX
; dim
++ )
162 AF_LatinAxis axis
= &metrics
->axis
[dim
];
166 stdw
= ( axis
->width_count
> 0 ) ? axis
->widths
[0].org
167 : AF_LATIN_CONSTANT( metrics
, 50 );
169 /* let's try 20% of the smallest width */
170 axis
->edge_distance_threshold
= stdw
/ 5;
171 axis
->standard_width
= stdw
;
172 axis
->extra_light
= 0;
174 #ifdef FT_DEBUG_LEVEL_TRACE
179 FT_TRACE5(( "%s widths:\n",
180 dim
== AF_DIMENSION_VERT
? "horizontal"
183 FT_TRACE5(( " %d (standard)", axis
->standard_width
));
184 for ( i
= 1; i
< axis
->width_count
; i
++ )
185 FT_TRACE5(( " %d", axis
->widths
[i
].org
));
195 af_glyph_hints_done( hints
);
199 /* Find all blue zones. Flat segments give the reference points, */
200 /* round segments the overshoot positions. */
203 af_latin_metrics_init_blues( AF_LatinMetrics metrics
,
206 FT_Pos flats
[AF_BLUE_STRING_MAX_LEN
];
207 FT_Pos rounds
[AF_BLUE_STRING_MAX_LEN
];
214 AF_LatinAxis axis
= &metrics
->axis
[AF_DIMENSION_VERT
];
217 AF_Blue_Stringset bss
= metrics
->root
.script_class
->blue_stringset
;
218 const AF_Blue_StringRec
* bs
= &af_blue_stringsets
[bss
];
221 /* we walk over the blue character strings as specified in the */
222 /* script's entry in the `af_blue_stringset' array */
224 FT_TRACE5(( "latin blue zones computation\n"
225 "============================\n"
228 for ( ; bs
->string
!= AF_BLUE_STRING_MAX
; bs
++ )
230 const char* p
= &af_blue_strings
[bs
->string
];
235 #ifdef FT_DEBUG_LEVEL_TRACE
237 FT_Bool have_flag
= 0;
240 FT_TRACE5(( "blue zone %d", axis
->blue_count
));
242 if ( bs
->properties
)
246 if ( AF_LATIN_IS_TOP_BLUE( bs
) )
248 FT_TRACE5(( "top" ));
252 if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs
) )
256 FT_TRACE5(( "small top" ));
260 if ( AF_LATIN_IS_LONG_BLUE( bs
) )
264 FT_TRACE5(( "long" ));
270 FT_TRACE5(( ":\n" ));
272 #endif /* FT_DEBUG_LEVEL_TRACE */
281 FT_Pos best_y
; /* same as points.y */
282 FT_Int best_point
, best_contour_first
, best_contour_last
;
287 GET_UTF8_CHAR( ch
, p
);
289 /* load the character in the face -- skip unknown or empty ones */
290 glyph_index
= FT_Get_Char_Index( face
, ch
);
291 if ( glyph_index
== 0 )
293 FT_TRACE5(( " U+%04lX unavailable\n", ch
));
297 error
= FT_Load_Glyph( face
, glyph_index
, FT_LOAD_NO_SCALE
);
298 outline
= face
->glyph
->outline
;
299 if ( error
|| outline
.n_points
<= 0 )
301 FT_TRACE5(( " U+%04lX contains no outlines\n", ch
));
305 /* now compute min or max point indices and coordinates */
306 points
= outline
.points
;
308 best_y
= 0; /* make compiler happy */
309 best_contour_first
= 0; /* ditto */
310 best_contour_last
= 0; /* ditto */
318 for ( nn
= 0; nn
< outline
.n_contours
; first
= last
+ 1, nn
++ )
320 FT_Int old_best_point
= best_point
;
324 last
= outline
.contours
[nn
];
326 /* Avoid single-point contours since they are never rasterized. */
327 /* In some fonts, they correspond to mark attachment points */
328 /* that are way outside of the glyph's real outline. */
332 if ( AF_LATIN_IS_TOP_BLUE( bs
) )
334 for ( pp
= first
; pp
<= last
; pp
++ )
335 if ( best_point
< 0 || points
[pp
].y
> best_y
)
338 best_y
= points
[pp
].y
;
343 for ( pp
= first
; pp
<= last
; pp
++ )
344 if ( best_point
< 0 || points
[pp
].y
< best_y
)
347 best_y
= points
[pp
].y
;
351 if ( best_point
!= old_best_point
)
353 best_contour_first
= first
;
354 best_contour_last
= last
;
359 /* now check whether the point belongs to a straight or round */
360 /* segment; we first need to find in which contour the extremum */
361 /* lies, then inspect its previous and next points */
362 if ( best_point
>= 0 )
364 FT_Pos best_x
= points
[best_point
].x
;
366 FT_Int best_segment_first
, best_segment_last
;
367 FT_Int best_on_point_first
, best_on_point_last
;
371 best_segment_first
= best_point
;
372 best_segment_last
= best_point
;
374 if ( FT_CURVE_TAG( outline
.tags
[best_point
] ) == FT_CURVE_TAG_ON
)
376 best_on_point_first
= best_point
;
377 best_on_point_last
= best_point
;
381 best_on_point_first
= -1;
382 best_on_point_last
= -1;
385 /* look for the previous and next points on the contour */
386 /* that are not on the same Y coordinate, then threshold */
387 /* the `closeness'... */
393 if ( prev
> best_contour_first
)
396 prev
= best_contour_last
;
398 dist
= FT_ABS( points
[prev
].y
- best_y
);
399 /* accept a small distance or a small angle (both values are */
400 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
402 if ( FT_ABS( points
[prev
].x
- best_x
) <= 20 * dist
)
405 best_segment_first
= prev
;
407 if ( FT_CURVE_TAG( outline
.tags
[prev
] ) == FT_CURVE_TAG_ON
)
409 best_on_point_first
= prev
;
410 if ( best_on_point_last
< 0 )
411 best_on_point_last
= prev
;
414 } while ( prev
!= best_point
);
418 if ( next
< best_contour_last
)
421 next
= best_contour_first
;
423 dist
= FT_ABS( points
[next
].y
- best_y
);
425 if ( FT_ABS( points
[next
].x
- best_x
) <= 20 * dist
)
428 best_segment_last
= next
;
430 if ( FT_CURVE_TAG( outline
.tags
[next
] ) == FT_CURVE_TAG_ON
)
432 best_on_point_last
= next
;
433 if ( best_on_point_first
< 0 )
434 best_on_point_first
= next
;
437 } while ( next
!= best_point
);
439 if ( AF_LATIN_IS_LONG_BLUE( bs
) )
441 /* If this flag is set, we have an additional constraint to */
442 /* get the blue zone distance: Find a segment of the topmost */
443 /* (or bottommost) contour that is longer than a heuristic */
444 /* threshold. This ensures that small bumps in the outline */
445 /* are ignored (for example, the `vertical serifs' found in */
446 /* many Hebrew glyph designs). */
448 /* If this segment is long enough, we are done. Otherwise, */
449 /* search the segment next to the extremum that is long */
450 /* enough, has the same direction, and a not too large */
451 /* vertical distance from the extremum. Note that the */
452 /* algorithm doesn't check whether the found segment is */
453 /* actually the one (vertically) nearest to the extremum. */
455 /* heuristic threshold value */
456 FT_Pos length_threshold
= metrics
->units_per_em
/ 25;
459 dist
= FT_ABS( points
[best_segment_last
].x
-
460 points
[best_segment_first
].x
);
462 if ( dist
< length_threshold
&&
463 best_segment_last
- best_segment_first
+ 2 <=
464 best_contour_last
- best_contour_first
)
466 /* heuristic threshold value */
467 FT_Pos height_threshold
= metrics
->units_per_em
/ 4;
476 /* compute direction */
481 if ( prev
> best_contour_first
)
484 prev
= best_contour_last
;
486 if ( points
[prev
].x
!= best_x
)
489 } while ( prev
!= best_point
);
491 /* skip glyph for the degenerate case */
492 if ( prev
== best_point
)
495 left2right
= FT_BOOL( points
[prev
].x
< points
[best_point
].x
);
497 first
= best_segment_last
;
505 FT_Int p_first
, p_last
;
510 /* no hit; adjust first point */
513 /* also adjust first and last on point */
514 if ( FT_CURVE_TAG( outline
.tags
[first
] ) ==
529 if ( last
< best_contour_last
)
532 last
= best_contour_first
;
534 if ( FT_ABS( best_y
- points
[first
].y
) > height_threshold
)
536 /* vertical distance too large */
541 /* same test as above */
542 dist
= FT_ABS( points
[last
].y
- points
[first
].y
);
544 if ( FT_ABS( points
[last
].x
- points
[first
].x
) <=
551 if ( FT_CURVE_TAG( outline
.tags
[last
] ) == FT_CURVE_TAG_ON
)
558 l2r
= FT_BOOL( points
[first
].x
< points
[last
].x
);
559 d
= FT_ABS( points
[last
].x
- points
[first
].x
);
561 if ( l2r
== left2right
&&
562 d
>= length_threshold
)
564 /* all constraints are met; update segment after finding */
568 if ( last
< best_contour_last
)
571 last
= best_contour_first
;
573 d
= FT_ABS( points
[last
].y
- points
[first
].y
);
575 if ( FT_ABS( points
[next
].x
- points
[first
].x
) <=
584 if ( FT_CURVE_TAG( outline
.tags
[last
] ) ==
592 } while ( last
!= best_segment_first
);
594 best_y
= points
[first
].y
;
596 best_segment_first
= first
;
597 best_segment_last
= last
;
599 best_on_point_first
= p_first
;
600 best_on_point_last
= p_last
;
605 } while ( last
!= best_segment_first
);
609 FT_TRACE5(( " U+%04lX: best_y = %5ld", ch
, best_y
));
611 /* now set the `round' flag depending on the segment's kind: */
613 /* - if the horizontal distance between the first and last */
614 /* `on' point is larger than upem/8 (value 8 is heuristic) */
615 /* we have a flat segment */
616 /* - if either the first or the last point of the segment is */
617 /* an `off' point, the segment is round, otherwise it is */
619 if ( best_on_point_first
>= 0 &&
620 best_on_point_last
>= 0 &&
621 (FT_UInt
)( FT_ABS( points
[best_on_point_last
].x
-
622 points
[best_on_point_first
].x
) ) >
623 metrics
->units_per_em
/ 8 )
627 FT_CURVE_TAG( outline
.tags
[best_segment_first
] ) !=
629 FT_CURVE_TAG( outline
.tags
[best_segment_last
] ) !=
632 FT_TRACE5(( " (%s)\n", round
? "round" : "flat" ));
636 rounds
[num_rounds
++] = best_y
;
638 flats
[num_flats
++] = best_y
;
641 if ( num_flats
== 0 && num_rounds
== 0 )
644 * we couldn't find a single glyph to compute this blue zone,
645 * we will simply ignore it then
647 FT_TRACE5(( " empty\n" ));
651 /* we have computed the contents of the `rounds' and `flats' tables, */
652 /* now determine the reference and overshoot position of the blue -- */
653 /* we simply take the median value after a simple sort */
654 af_sort_pos( num_rounds
, rounds
);
655 af_sort_pos( num_flats
, flats
);
657 blue
= &axis
->blues
[axis
->blue_count
];
658 blue_ref
= &blue
->ref
.org
;
659 blue_shoot
= &blue
->shoot
.org
;
663 if ( num_flats
== 0 )
666 *blue_shoot
= rounds
[num_rounds
/ 2];
668 else if ( num_rounds
== 0 )
671 *blue_shoot
= flats
[num_flats
/ 2];
675 *blue_ref
= flats
[num_flats
/ 2];
676 *blue_shoot
= rounds
[num_rounds
/ 2];
679 /* there are sometimes problems: if the overshoot position of top */
680 /* zones is under its reference position, or the opposite for bottom */
681 /* zones. We must thus check everything there and correct the errors */
682 if ( *blue_shoot
!= *blue_ref
)
684 FT_Pos ref
= *blue_ref
;
685 FT_Pos shoot
= *blue_shoot
;
686 FT_Bool over_ref
= FT_BOOL( shoot
> ref
);
689 if ( AF_LATIN_IS_TOP_BLUE( bs
) ^ over_ref
)
692 *blue_shoot
= ( shoot
+ ref
) / 2;
694 FT_TRACE5(( " [overshoot smaller than reference,"
695 " taking mean value]\n" ));
700 if ( AF_LATIN_IS_TOP_BLUE( bs
) )
701 blue
->flags
|= AF_LATIN_BLUE_TOP
;
704 * The following flag is used later to adjust the y and x scales
705 * in order to optimize the pixel grid alignment of the top of small
708 if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs
) )
709 blue
->flags
|= AF_LATIN_BLUE_ADJUSTMENT
;
711 FT_TRACE5(( " -> reference = %ld\n"
712 " overshoot = %ld\n",
713 *blue_ref
, *blue_shoot
));
722 /* Check whether all ASCII digits have the same advance width. */
725 af_latin_metrics_check_digits( AF_LatinMetrics metrics
,
729 FT_Bool started
= 0, same_width
= 1;
730 FT_Fixed advance
, old_advance
= 0;
733 /* digit `0' is 0x30 in all supported charmaps */
734 for ( i
= 0x30; i
<= 0x39; i
++ )
739 glyph_index
= FT_Get_Char_Index( face
, i
);
740 if ( glyph_index
== 0 )
743 if ( FT_Get_Advance( face
, glyph_index
,
746 FT_LOAD_IGNORE_TRANSFORM
,
752 if ( advance
!= old_advance
)
760 old_advance
= advance
;
765 metrics
->root
.digits_have_same_width
= same_width
;
769 /* Initialize global metrics. */
771 FT_LOCAL_DEF( FT_Error
)
772 af_latin_metrics_init( AF_LatinMetrics metrics
,
775 FT_CharMap oldmap
= face
->charmap
;
778 metrics
->units_per_em
= face
->units_per_EM
;
780 if ( !FT_Select_Charmap( face
, FT_ENCODING_UNICODE
) )
782 af_latin_metrics_init_widths( metrics
, face
);
783 af_latin_metrics_init_blues( metrics
, face
);
784 af_latin_metrics_check_digits( metrics
, face
);
787 FT_Set_Charmap( face
, oldmap
);
792 /* Adjust scaling value, then scale and shift widths */
793 /* and blue zones (if applicable) for given dimension. */
796 af_latin_metrics_scale_dim( AF_LatinMetrics metrics
,
806 if ( dim
== AF_DIMENSION_HORZ
)
808 scale
= scaler
->x_scale
;
809 delta
= scaler
->x_delta
;
813 scale
= scaler
->y_scale
;
814 delta
= scaler
->y_delta
;
817 axis
= &metrics
->axis
[dim
];
819 if ( axis
->org_scale
== scale
&& axis
->org_delta
== delta
)
822 axis
->org_scale
= scale
;
823 axis
->org_delta
= delta
;
826 * correct X and Y scale to optimize the alignment of the top of small
827 * letters to the pixel grid
830 AF_LatinAxis Axis
= &metrics
->axis
[AF_DIMENSION_VERT
];
831 AF_LatinBlue blue
= NULL
;
834 for ( nn
= 0; nn
< Axis
->blue_count
; nn
++ )
836 if ( Axis
->blues
[nn
].flags
& AF_LATIN_BLUE_ADJUSTMENT
)
838 blue
= &Axis
->blues
[nn
];
852 scaled
= FT_MulFix( blue
->shoot
.org
, scaler
->y_scale
);
853 ppem
= metrics
->root
.scaler
.face
->size
->metrics
.x_ppem
;
854 limit
= metrics
->root
.globals
->increase_x_height
;
857 /* if the `increase-x-height' property is active, */
858 /* we round up much more often */
861 ppem
>= AF_PROP_INCREASE_X_HEIGHT_MIN
)
864 fitted
= ( scaled
+ threshold
) & ~63;
866 if ( scaled
!= fitted
)
869 if ( dim
== AF_DIMENSION_HORZ
)
871 if ( fitted
< scaled
)
872 scale
-= scale
/ 50; /* scale *= 0.98 */
876 if ( dim
== AF_DIMENSION_VERT
)
878 scale
= FT_MulDiv( scale
, fitted
, scaled
);
881 "af_latin_metrics_scale_dim:"
882 " x height alignment (script `%s'):\n"
884 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
886 af_script_names
[metrics
->root
.script_class
->script
],
887 axis
->org_scale
/ 65536.0,
889 ( fitted
- scaled
) * 100 / scaled
));
898 if ( dim
== AF_DIMENSION_HORZ
)
900 metrics
->root
.scaler
.x_scale
= scale
;
901 metrics
->root
.scaler
.x_delta
= delta
;
905 metrics
->root
.scaler
.y_scale
= scale
;
906 metrics
->root
.scaler
.y_delta
= delta
;
909 FT_TRACE5(( "%s widths (script `%s')\n",
910 dim
== AF_DIMENSION_HORZ
? "horizontal" : "vertical",
911 af_script_names
[metrics
->root
.script_class
->script
] ));
913 /* scale the widths */
914 for ( nn
= 0; nn
< axis
->width_count
; nn
++ )
916 AF_Width width
= axis
->widths
+ nn
;
919 width
->cur
= FT_MulFix( width
->org
, scale
);
920 width
->fit
= width
->cur
;
922 FT_TRACE5(( " %d scaled to %.2f\n",
924 width
->cur
/ 64.0 ));
929 /* an extra-light axis corresponds to a standard width that is */
930 /* smaller than 5/8 pixels */
932 (FT_Bool
)( FT_MulFix( axis
->standard_width
, scale
) < 32 + 8 );
934 #ifdef FT_DEBUG_LEVEL_TRACE
935 if ( axis
->extra_light
)
936 FT_TRACE5(( "`%s' script is extra light (at current resolution)\n"
938 af_script_names
[metrics
->root
.script_class
->script
] ));
941 if ( dim
== AF_DIMENSION_VERT
)
943 FT_TRACE5(( "blue zones (script `%s')\n",
944 af_script_names
[metrics
->root
.script_class
->script
] ));
946 /* scale the blue zones */
947 for ( nn
= 0; nn
< axis
->blue_count
; nn
++ )
949 AF_LatinBlue blue
= &axis
->blues
[nn
];
953 blue
->ref
.cur
= FT_MulFix( blue
->ref
.org
, scale
) + delta
;
954 blue
->ref
.fit
= blue
->ref
.cur
;
955 blue
->shoot
.cur
= FT_MulFix( blue
->shoot
.org
, scale
) + delta
;
956 blue
->shoot
.fit
= blue
->shoot
.cur
;
957 blue
->flags
&= ~AF_LATIN_BLUE_ACTIVE
;
959 /* a blue zone is only active if it is less than 3/4 pixels tall */
960 dist
= FT_MulFix( blue
->ref
.org
- blue
->shoot
.org
, scale
);
961 if ( dist
<= 48 && dist
>= -48 )
969 /* use discrete values for blue zone widths */
973 /* generic, original code */
974 delta1
= blue
->shoot
.org
- blue
->ref
.org
;
979 delta2
= FT_MulFix( delta2
, scale
);
983 else if ( delta2
< 64 )
984 delta2
= 32 + ( ( ( delta2
- 32 ) + 16 ) & ~31 );
986 delta2
= FT_PIX_ROUND( delta2
);
991 blue
->ref
.fit
= FT_PIX_ROUND( blue
->ref
.cur
);
992 blue
->shoot
.fit
= blue
->ref
.fit
+ delta2
;
996 /* simplified version due to abs(dist) <= 48 */
1003 else if ( delta2
< 48 )
1011 blue
->ref
.fit
= FT_PIX_ROUND( blue
->ref
.cur
);
1012 blue
->shoot
.fit
= blue
->ref
.fit
- delta2
;
1016 blue
->flags
|= AF_LATIN_BLUE_ACTIVE
;
1018 FT_TRACE5(( " reference %d: %d scaled to %.2f%s\n"
1019 " overshoot %d: %d scaled to %.2f%s\n",
1022 blue
->ref
.fit
/ 64.0,
1023 blue
->flags
& AF_LATIN_BLUE_ACTIVE
? ""
1027 blue
->shoot
.fit
/ 64.0,
1028 blue
->flags
& AF_LATIN_BLUE_ACTIVE
? ""
1036 /* Scale global values in both directions. */
1038 FT_LOCAL_DEF( void )
1039 af_latin_metrics_scale( AF_LatinMetrics metrics
,
1042 metrics
->root
.scaler
.render_mode
= scaler
->render_mode
;
1043 metrics
->root
.scaler
.face
= scaler
->face
;
1044 metrics
->root
.scaler
.flags
= scaler
->flags
;
1046 af_latin_metrics_scale_dim( metrics
, scaler
, AF_DIMENSION_HORZ
);
1047 af_latin_metrics_scale_dim( metrics
, scaler
, AF_DIMENSION_VERT
);
1051 /*************************************************************************/
1052 /*************************************************************************/
1054 /***** L A T I N G L Y P H A N A L Y S I S *****/
1056 /*************************************************************************/
1057 /*************************************************************************/
1060 /* Walk over all contours and compute its segments. */
1062 FT_LOCAL_DEF( FT_Error
)
1063 af_latin_hints_compute_segments( AF_GlyphHints hints
,
1066 AF_AxisHints axis
= &hints
->axis
[dim
];
1067 FT_Memory memory
= hints
->memory
;
1068 FT_Error error
= FT_Err_Ok
;
1069 AF_Segment segment
= NULL
;
1071 AF_Point
* contour
= hints
->contours
;
1072 AF_Point
* contour_limit
= contour
+ hints
->num_contours
;
1073 AF_Direction major_dir
, segment_dir
;
1078 seg0
.flags
= AF_EDGE_NORMAL
;
1080 major_dir
= (AF_Direction
)FT_ABS( axis
->major_dir
);
1081 segment_dir
= major_dir
;
1083 axis
->num_segments
= 0;
1085 /* set up (u,v) in each point */
1086 if ( dim
== AF_DIMENSION_HORZ
)
1088 AF_Point point
= hints
->points
;
1089 AF_Point limit
= point
+ hints
->num_points
;
1092 for ( ; point
< limit
; point
++ )
1094 point
->u
= point
->fx
;
1095 point
->v
= point
->fy
;
1100 AF_Point point
= hints
->points
;
1101 AF_Point limit
= point
+ hints
->num_points
;
1104 for ( ; point
< limit
; point
++ )
1106 point
->u
= point
->fy
;
1107 point
->v
= point
->fx
;
1111 /* do each contour separately */
1112 for ( ; contour
< contour_limit
; contour
++ )
1114 AF_Point point
= contour
[0];
1115 AF_Point last
= point
->prev
;
1117 FT_Pos min_pos
= 32000; /* minimum segment pos != min_coord */
1118 FT_Pos max_pos
= -32000; /* maximum segment pos != max_coord */
1122 if ( point
== last
) /* skip singletons -- just in case */
1125 if ( FT_ABS( last
->out_dir
) == major_dir
&&
1126 FT_ABS( point
->out_dir
) == major_dir
)
1128 /* we are already on an edge, try to locate its start */
1133 point
= point
->prev
;
1134 if ( FT_ABS( point
->out_dir
) != major_dir
)
1136 point
= point
->next
;
1139 if ( point
== last
)
1160 if ( point
->out_dir
!= segment_dir
|| point
== last
)
1162 /* we are just leaving an edge; record a new segment! */
1163 segment
->last
= point
;
1164 segment
->pos
= (FT_Short
)( ( min_pos
+ max_pos
) >> 1 );
1166 /* a segment is round if either its first or last point */
1167 /* is a control point */
1168 if ( ( segment
->first
->flags
| point
->flags
) &
1170 segment
->flags
|= AF_EDGE_ROUND
;
1172 /* compute segment size */
1173 min_pos
= max_pos
= point
->v
;
1175 v
= segment
->first
->v
;
1181 segment
->min_coord
= (FT_Short
)min_pos
;
1182 segment
->max_coord
= (FT_Short
)max_pos
;
1183 segment
->height
= (FT_Short
)( segment
->max_coord
-
1184 segment
->min_coord
);
1192 /* now exit if we are at the start/end point */
1193 if ( point
== last
)
1200 if ( !on_edge
&& FT_ABS( point
->out_dir
) == major_dir
)
1202 /* this is the start of a new segment! */
1203 segment_dir
= (AF_Direction
)point
->out_dir
;
1205 /* clear all segment fields */
1206 error
= af_axis_hints_new_segment( axis
, memory
, &segment
);
1211 segment
->dir
= (FT_Char
)segment_dir
;
1212 min_pos
= max_pos
= point
->u
;
1213 segment
->first
= point
;
1214 segment
->last
= point
;
1218 point
= point
->next
;
1224 /* now slightly increase the height of segments if this makes */
1225 /* sense -- this is used to better detect and ignore serifs */
1227 AF_Segment segments
= axis
->segments
;
1228 AF_Segment segments_end
= segments
+ axis
->num_segments
;
1231 for ( segment
= segments
; segment
< segments_end
; segment
++ )
1233 AF_Point first
= segment
->first
;
1234 AF_Point last
= segment
->last
;
1235 FT_Pos first_v
= first
->v
;
1236 FT_Pos last_v
= last
->v
;
1239 if ( first
== last
)
1242 if ( first_v
< last_v
)
1248 if ( p
->v
< first_v
)
1249 segment
->height
= (FT_Short
)( segment
->height
+
1250 ( ( first_v
- p
->v
) >> 1 ) );
1253 if ( p
->v
> last_v
)
1254 segment
->height
= (FT_Short
)( segment
->height
+
1255 ( ( p
->v
- last_v
) >> 1 ) );
1263 if ( p
->v
> first_v
)
1264 segment
->height
= (FT_Short
)( segment
->height
+
1265 ( ( p
->v
- first_v
) >> 1 ) );
1268 if ( p
->v
< last_v
)
1269 segment
->height
= (FT_Short
)( segment
->height
+
1270 ( ( last_v
- p
->v
) >> 1 ) );
1280 /* Link segments to form stems and serifs. */
1282 FT_LOCAL_DEF( void )
1283 af_latin_hints_link_segments( AF_GlyphHints hints
,
1286 AF_AxisHints axis
= &hints
->axis
[dim
];
1287 AF_Segment segments
= axis
->segments
;
1288 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
1289 FT_Pos len_threshold
, len_score
;
1290 AF_Segment seg1
, seg2
;
1293 len_threshold
= AF_LATIN_CONSTANT( hints
->metrics
, 8 );
1294 if ( len_threshold
== 0 )
1297 len_score
= AF_LATIN_CONSTANT( hints
->metrics
, 6000 );
1299 /* now compare each segment to the others */
1300 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
1302 /* the fake segments are introduced to hint the metrics -- */
1303 /* we must never link them to anything */
1304 if ( seg1
->dir
!= axis
->major_dir
|| seg1
->first
== seg1
->last
)
1307 /* search for stems having opposite directions, */
1308 /* with seg1 to the `left' of seg2 */
1309 for ( seg2
= segments
; seg2
< segment_limit
; seg2
++ )
1311 FT_Pos pos1
= seg1
->pos
;
1312 FT_Pos pos2
= seg2
->pos
;
1315 if ( seg1
->dir
+ seg2
->dir
== 0 && pos2
> pos1
)
1317 /* compute distance between the two segments */
1318 FT_Pos dist
= pos2
- pos1
;
1319 FT_Pos min
= seg1
->min_coord
;
1320 FT_Pos max
= seg1
->max_coord
;
1324 if ( min
< seg2
->min_coord
)
1325 min
= seg2
->min_coord
;
1327 if ( max
> seg2
->max_coord
)
1328 max
= seg2
->max_coord
;
1330 /* compute maximum coordinate difference of the two segments */
1332 if ( len
>= len_threshold
)
1334 /* small coordinate differences cause a higher score, and */
1335 /* segments with a greater distance cause a higher score also */
1336 score
= dist
+ len_score
/ len
;
1338 /* and we search for the smallest score */
1339 /* of the sum of the two values */
1340 if ( score
< seg1
->score
)
1342 seg1
->score
= score
;
1346 if ( score
< seg2
->score
)
1348 seg2
->score
= score
;
1356 /* now compute the `serif' segments, cf. explanations in `afhints.h' */
1357 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
1363 if ( seg2
->link
!= seg1
)
1366 seg1
->serif
= seg2
->link
;
1373 /* Link segments to edges, using feature analysis for selection. */
1375 FT_LOCAL_DEF( FT_Error
)
1376 af_latin_hints_compute_edges( AF_GlyphHints hints
,
1379 AF_AxisHints axis
= &hints
->axis
[dim
];
1380 FT_Error error
= FT_Err_Ok
;
1381 FT_Memory memory
= hints
->memory
;
1382 AF_LatinAxis laxis
= &((AF_LatinMetrics
)hints
->metrics
)->axis
[dim
];
1384 AF_Segment segments
= axis
->segments
;
1385 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
1389 AF_Direction up_dir
;
1392 FT_Pos edge_distance_threshold
;
1393 FT_Pos segment_length_threshold
;
1396 axis
->num_edges
= 0;
1398 scale
= ( dim
== AF_DIMENSION_HORZ
) ? hints
->x_scale
1402 up_dir
= ( dim
== AF_DIMENSION_HORZ
) ? AF_DIR_UP
1407 * We ignore all segments that are less than 1 pixel in length
1408 * to avoid many problems with serif fonts. We compute the
1409 * corresponding threshold in font units.
1411 if ( dim
== AF_DIMENSION_HORZ
)
1412 segment_length_threshold
= FT_DivFix( 64, hints
->y_scale
);
1414 segment_length_threshold
= 0;
1416 /*********************************************************************/
1418 /* We begin by generating a sorted table of edges for the current */
1419 /* direction. To do so, we simply scan each segment and try to find */
1420 /* an edge in our table that corresponds to its position. */
1422 /* If no edge is found, we create and insert a new edge in the */
1423 /* sorted table. Otherwise, we simply add the segment to the edge's */
1424 /* list which gets processed in the second step to compute the */
1425 /* edge's properties. */
1427 /* Note that the table of edges is sorted along the segment/edge */
1430 /*********************************************************************/
1432 /* assure that edge distance threshold is at most 0.25px */
1433 edge_distance_threshold
= FT_MulFix( laxis
->edge_distance_threshold
,
1435 if ( edge_distance_threshold
> 64 / 4 )
1436 edge_distance_threshold
= 64 / 4;
1438 edge_distance_threshold
= FT_DivFix( edge_distance_threshold
,
1441 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
1443 AF_Edge found
= NULL
;
1447 if ( seg
->height
< segment_length_threshold
)
1450 /* A special case for serif edges: If they are smaller than */
1451 /* 1.5 pixels we ignore them. */
1453 2 * seg
->height
< 3 * segment_length_threshold
)
1456 /* look for an edge corresponding to the segment */
1457 for ( ee
= 0; ee
< axis
->num_edges
; ee
++ )
1459 AF_Edge edge
= axis
->edges
+ ee
;
1463 dist
= seg
->pos
- edge
->fpos
;
1467 if ( dist
< edge_distance_threshold
&& edge
->dir
== seg
->dir
)
1479 /* insert a new edge in the list and */
1480 /* sort according to the position */
1481 error
= af_axis_hints_new_edge( axis
, seg
->pos
,
1482 (AF_Direction
)seg
->dir
,
1487 /* add the segment to the new edge's list */
1492 edge
->dir
= seg
->dir
;
1493 edge
->fpos
= seg
->pos
;
1494 edge
->opos
= FT_MulFix( seg
->pos
, scale
);
1495 edge
->pos
= edge
->opos
;
1496 seg
->edge_next
= seg
;
1500 /* if an edge was found, simply add the segment to the edge's */
1502 seg
->edge_next
= found
->first
;
1503 found
->last
->edge_next
= seg
;
1509 /******************************************************************/
1511 /* Good, we now compute each edge's properties according to the */
1512 /* segments found on its position. Basically, these are */
1514 /* - the edge's main direction */
1515 /* - stem edge, serif edge or both (which defaults to stem then) */
1516 /* - rounded edge, straight or both (which defaults to straight) */
1517 /* - link for edge */
1519 /******************************************************************/
1521 /* first of all, set the `edge' field in each segment -- this is */
1522 /* required in order to compute edge links */
1525 * Note that removing this loop and setting the `edge' field of each
1526 * segment directly in the code above slows down execution speed for
1527 * some reasons on platforms like the Sun.
1530 AF_Edge edges
= axis
->edges
;
1531 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1535 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1542 seg
= seg
->edge_next
;
1544 } while ( seg
!= edge
->first
);
1547 /* now compute each edge properties */
1548 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1550 FT_Int is_round
= 0; /* does it contain round segments? */
1551 FT_Int is_straight
= 0; /* does it contain straight segments? */
1553 FT_Pos ups
= 0; /* number of upwards segments */
1554 FT_Pos downs
= 0; /* number of downwards segments */
1565 /* check for roundness of segment */
1566 if ( seg
->flags
& AF_EDGE_ROUND
)
1572 /* check for segment direction */
1573 if ( seg
->dir
== up_dir
)
1574 ups
+= seg
->max_coord
- seg
->min_coord
;
1576 downs
+= seg
->max_coord
- seg
->min_coord
;
1579 /* check for links -- if seg->serif is set, then seg->link must */
1581 is_serif
= (FT_Bool
)( seg
->serif
&&
1583 seg
->serif
->edge
!= edge
);
1585 if ( ( seg
->link
&& seg
->link
->edge
!= NULL
) || is_serif
)
1597 edge2
= edge
->serif
;
1606 edge_delta
= edge
->fpos
- edge2
->fpos
;
1607 if ( edge_delta
< 0 )
1608 edge_delta
= -edge_delta
;
1610 seg_delta
= seg
->pos
- seg2
->pos
;
1611 if ( seg_delta
< 0 )
1612 seg_delta
= -seg_delta
;
1614 if ( seg_delta
< edge_delta
)
1622 edge
->serif
= edge2
;
1623 edge2
->flags
|= AF_EDGE_SERIF
;
1629 seg
= seg
->edge_next
;
1631 } while ( seg
!= edge
->first
);
1633 /* set the round/straight flags */
1634 edge
->flags
= AF_EDGE_NORMAL
;
1636 if ( is_round
> 0 && is_round
>= is_straight
)
1637 edge
->flags
|= AF_EDGE_ROUND
;
1640 /* set the edge's main direction */
1641 edge
->dir
= AF_DIR_NONE
;
1644 edge
->dir
= (FT_Char
)up_dir
;
1646 else if ( ups
< downs
)
1647 edge
->dir
= (FT_Char
)-up_dir
;
1649 else if ( ups
== downs
)
1650 edge
->dir
= 0; /* both up and down! */
1653 /* get rid of serifs if link is set */
1654 /* XXX: This gets rid of many unpleasant artefacts! */
1655 /* Example: the `c' in cour.pfa at size 13 */
1657 if ( edge
->serif
&& edge
->link
)
1667 /* Detect segments and edges for given dimension. */
1669 FT_LOCAL_DEF( FT_Error
)
1670 af_latin_hints_detect_features( AF_GlyphHints hints
,
1676 error
= af_latin_hints_compute_segments( hints
, dim
);
1679 af_latin_hints_link_segments( hints
, dim
);
1681 error
= af_latin_hints_compute_edges( hints
, dim
);
1688 /* Compute all edges which lie within blue zones. */
1690 FT_LOCAL_DEF( void )
1691 af_latin_hints_compute_blue_edges( AF_GlyphHints hints
,
1692 AF_LatinMetrics metrics
)
1694 AF_AxisHints axis
= &hints
->axis
[AF_DIMENSION_VERT
];
1695 AF_Edge edge
= axis
->edges
;
1696 AF_Edge edge_limit
= edge
+ axis
->num_edges
;
1697 AF_LatinAxis latin
= &metrics
->axis
[AF_DIMENSION_VERT
];
1698 FT_Fixed scale
= latin
->scale
;
1701 /* compute which blue zones are active, i.e. have their scaled */
1702 /* size < 3/4 pixels */
1704 /* for each horizontal edge search the blue zone which is closest */
1705 for ( ; edge
< edge_limit
; edge
++ )
1708 AF_Width best_blue
= NULL
;
1709 FT_Pos best_dist
; /* initial threshold */
1712 /* compute the initial threshold as a fraction of the EM size */
1713 /* (the value 40 is heuristic) */
1714 best_dist
= FT_MulFix( metrics
->units_per_em
/ 40, scale
);
1716 /* assure a minimum distance of 0.5px */
1717 if ( best_dist
> 64 / 2 )
1720 for ( bb
= 0; bb
< latin
->blue_count
; bb
++ )
1722 AF_LatinBlue blue
= latin
->blues
+ bb
;
1723 FT_Bool is_top_blue
, is_major_dir
;
1726 /* skip inactive blue zones (i.e., those that are too large) */
1727 if ( !( blue
->flags
& AF_LATIN_BLUE_ACTIVE
) )
1730 /* if it is a top zone, check for right edges -- if it is a bottom */
1731 /* zone, check for left edges */
1733 /* of course, that's for TrueType */
1734 is_top_blue
= (FT_Byte
)( ( blue
->flags
& AF_LATIN_BLUE_TOP
) != 0 );
1735 is_major_dir
= FT_BOOL( edge
->dir
== axis
->major_dir
);
1737 /* if it is a top zone, the edge must be against the major */
1738 /* direction; if it is a bottom zone, it must be in the major */
1740 if ( is_top_blue
^ is_major_dir
)
1745 /* first of all, compare it to the reference position */
1746 dist
= edge
->fpos
- blue
->ref
.org
;
1750 dist
= FT_MulFix( dist
, scale
);
1751 if ( dist
< best_dist
)
1754 best_blue
= &blue
->ref
;
1757 /* now compare it to the overshoot position and check whether */
1758 /* the edge is rounded, and whether the edge is over the */
1759 /* reference position of a top zone, or under the reference */
1760 /* position of a bottom zone */
1761 if ( edge
->flags
& AF_EDGE_ROUND
&& dist
!= 0 )
1763 FT_Bool is_under_ref
= FT_BOOL( edge
->fpos
< blue
->ref
.org
);
1766 if ( is_top_blue
^ is_under_ref
)
1768 dist
= edge
->fpos
- blue
->shoot
.org
;
1772 dist
= FT_MulFix( dist
, scale
);
1773 if ( dist
< best_dist
)
1776 best_blue
= &blue
->shoot
;
1784 edge
->blue_edge
= best_blue
;
1789 /* Initalize hinting engine. */
1792 af_latin_hints_init( AF_GlyphHints hints
,
1793 AF_LatinMetrics metrics
)
1795 FT_Render_Mode mode
;
1796 FT_UInt32 scaler_flags
, other_flags
;
1797 FT_Face face
= metrics
->root
.scaler
.face
;
1800 af_glyph_hints_rescale( hints
, (AF_ScriptMetrics
)metrics
);
1803 * correct x_scale and y_scale if needed, since they may have
1804 * been modified by `af_latin_metrics_scale_dim' above
1806 hints
->x_scale
= metrics
->axis
[AF_DIMENSION_HORZ
].scale
;
1807 hints
->x_delta
= metrics
->axis
[AF_DIMENSION_HORZ
].delta
;
1808 hints
->y_scale
= metrics
->axis
[AF_DIMENSION_VERT
].scale
;
1809 hints
->y_delta
= metrics
->axis
[AF_DIMENSION_VERT
].delta
;
1811 /* compute flags depending on render mode, etc. */
1812 mode
= metrics
->root
.scaler
.render_mode
;
1814 #if 0 /* #ifdef AF_CONFIG_OPTION_USE_WARPER */
1815 if ( mode
== FT_RENDER_MODE_LCD
|| mode
== FT_RENDER_MODE_LCD_V
)
1816 metrics
->root
.scaler
.render_mode
= mode
= FT_RENDER_MODE_NORMAL
;
1819 scaler_flags
= hints
->scaler_flags
;
1823 * We snap the width of vertical stems for the monochrome and
1824 * horizontal LCD rendering targets only.
1826 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD
)
1827 other_flags
|= AF_LATIN_HINTS_HORZ_SNAP
;
1830 * We snap the width of horizontal stems for the monochrome and
1831 * vertical LCD rendering targets only.
1833 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD_V
)
1834 other_flags
|= AF_LATIN_HINTS_VERT_SNAP
;
1837 * We adjust stems to full pixels only if we don't use the `light' mode.
1839 if ( mode
!= FT_RENDER_MODE_LIGHT
)
1840 other_flags
|= AF_LATIN_HINTS_STEM_ADJUST
;
1842 if ( mode
== FT_RENDER_MODE_MONO
)
1843 other_flags
|= AF_LATIN_HINTS_MONO
;
1846 * In `light' hinting mode we disable horizontal hinting completely.
1847 * We also do it if the face is italic.
1849 if ( mode
== FT_RENDER_MODE_LIGHT
||
1850 ( face
->style_flags
& FT_STYLE_FLAG_ITALIC
) != 0 )
1851 scaler_flags
|= AF_SCALER_FLAG_NO_HORIZONTAL
;
1853 hints
->scaler_flags
= scaler_flags
;
1854 hints
->other_flags
= other_flags
;
1860 /*************************************************************************/
1861 /*************************************************************************/
1863 /***** L A T I N G L Y P H G R I D - F I T T I N G *****/
1865 /*************************************************************************/
1866 /*************************************************************************/
1868 /* Snap a given width in scaled coordinates to one of the */
1869 /* current standard widths. */
1872 af_latin_snap_width( AF_Width widths
,
1877 FT_Pos best
= 64 + 32 + 2;
1878 FT_Pos reference
= width
;
1882 for ( n
= 0; n
< count
; n
++ )
1899 scaled
= FT_PIX_ROUND( reference
);
1901 if ( width
>= reference
)
1903 if ( width
< scaled
+ 48 )
1908 if ( width
> scaled
- 48 )
1916 /* Compute the snapped width of a given stem, ignoring very thin ones. */
1917 /* There is a lot of voodoo in this function; changing the hard-coded */
1918 /* parameters influence the whole hinting process. */
1921 af_latin_compute_stem_width( AF_GlyphHints hints
,
1924 AF_Edge_Flags base_flags
,
1925 AF_Edge_Flags stem_flags
)
1927 AF_LatinMetrics metrics
= (AF_LatinMetrics
)hints
->metrics
;
1928 AF_LatinAxis axis
= &metrics
->axis
[dim
];
1929 FT_Pos dist
= width
;
1931 FT_Int vertical
= ( dim
== AF_DIMENSION_VERT
);
1934 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) ||
1944 if ( ( vertical
&& !AF_LATIN_HINTS_DO_VERT_SNAP( hints
) ) ||
1945 ( !vertical
&& !AF_LATIN_HINTS_DO_HORZ_SNAP( hints
) ) )
1947 /* smooth hinting process: very lightly quantize the stem width */
1949 /* leave the widths of serifs alone */
1950 if ( ( stem_flags
& AF_EDGE_SERIF
) &&
1955 else if ( base_flags
& AF_EDGE_ROUND
)
1960 else if ( dist
< 56 )
1963 if ( axis
->width_count
> 0 )
1968 /* compare to standard width */
1969 delta
= dist
- axis
->widths
[0].cur
;
1976 dist
= axis
->widths
[0].cur
;
1983 if ( dist
< 3 * 64 )
1991 else if ( delta
< 32 )
1994 else if ( delta
< 54 )
2001 dist
= ( dist
+ 32 ) & ~63;
2006 /* strong hinting process: snap the stem width to integer pixels */
2008 FT_Pos org_dist
= dist
;
2011 dist
= af_latin_snap_width( axis
->widths
, axis
->width_count
, dist
);
2015 /* in the case of vertical hinting, always round */
2016 /* the stem heights to integer pixels */
2019 dist
= ( dist
+ 16 ) & ~63;
2025 if ( AF_LATIN_HINTS_DO_MONO( hints
) )
2027 /* monochrome horizontal hinting: snap widths to integer pixels */
2028 /* with a different threshold */
2033 dist
= ( dist
+ 32 ) & ~63;
2037 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2038 /* approach: we strengthen small stems, round stems whose size */
2039 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2042 dist
= ( dist
+ 64 ) >> 1;
2044 else if ( dist
< 128 )
2046 /* We only round to an integer width if the corresponding */
2047 /* distortion is less than 1/4 pixel. Otherwise this */
2048 /* makes everything worse since the diagonals, which are */
2049 /* not hinted, appear a lot bolder or thinner than the */
2050 /* vertical stems. */
2055 dist
= ( dist
+ 22 ) & ~63;
2056 delta
= dist
- org_dist
;
2064 dist
= ( dist
+ 64 ) >> 1;
2068 /* round otherwise to prevent color fringes in LCD mode */
2069 dist
= ( dist
+ 32 ) & ~63;
2082 /* Align one stem edge relative to the previous stem edge. */
2085 af_latin_align_linked_edge( AF_GlyphHints hints
,
2090 FT_Pos dist
= stem_edge
->opos
- base_edge
->opos
;
2092 FT_Pos fitted_width
= af_latin_compute_stem_width(
2094 (AF_Edge_Flags
)base_edge
->flags
,
2095 (AF_Edge_Flags
)stem_edge
->flags
);
2098 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
2100 FT_TRACE5(( " LINK: edge %d (opos=%.2f) linked to %.2f,"
2101 " dist was %.2f, now %.2f\n",
2102 stem_edge
-hints
->axis
[dim
].edges
, stem_edge
->opos
/ 64.0,
2103 stem_edge
->pos
/ 64.0, dist
/ 64.0, fitted_width
/ 64.0 ));
2107 /* Shift the coordinates of the `serif' edge by the same amount */
2108 /* as the corresponding `base' edge has been moved already. */
2111 af_latin_align_serif_edge( AF_GlyphHints hints
,
2117 serif
->pos
= base
->pos
+ ( serif
->opos
- base
->opos
);
2121 /*************************************************************************/
2122 /*************************************************************************/
2123 /*************************************************************************/
2125 /**** E D G E H I N T I N G ****/
2127 /*************************************************************************/
2128 /*************************************************************************/
2129 /*************************************************************************/
2132 /* The main grid-fitting routine. */
2134 FT_LOCAL_DEF( void )
2135 af_latin_hint_edges( AF_GlyphHints hints
,
2138 AF_AxisHints axis
= &hints
->axis
[dim
];
2139 AF_Edge edges
= axis
->edges
;
2140 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
2143 AF_Edge anchor
= NULL
;
2144 FT_Int has_serifs
= 0;
2146 #ifdef FT_DEBUG_LEVEL_TRACE
2147 FT_UInt num_actions
= 0;
2151 FT_TRACE5(( "latin %s edge hinting (script `%s')\n",
2152 dim
== AF_DIMENSION_VERT
? "horizontal" : "vertical",
2153 af_script_names
[hints
->metrics
->script_class
->script
] ));
2155 /* we begin by aligning all stems relative to the blue zone */
2156 /* if needed -- that's only for horizontal edges */
2158 if ( dim
== AF_DIMENSION_VERT
&& AF_HINTS_DO_BLUES( hints
) )
2160 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
2163 AF_Edge edge1
, edge2
; /* these edges form the stem to check */
2166 if ( edge
->flags
& AF_EDGE_DONE
)
2169 blue
= edge
->blue_edge
;
2176 /* flip edges if the other stem is aligned to a blue zone */
2177 else if ( edge2
&& edge2
->blue_edge
)
2179 blue
= edge2
->blue_edge
;
2187 #ifdef FT_DEBUG_LEVEL_TRACE
2189 FT_TRACE5(( " BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2190 " was %.2f (anchor=edge %d)\n",
2191 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
2192 edge1
->pos
/ 64.0, edge
- edges
));
2194 FT_TRACE5(( " BLUE: edge %d (opos=%.2f) snapped to %.2f,"
2196 edge1
- edges
, edge1
->opos
/ 64.0, blue
->fit
/ 64.0,
2197 edge1
->pos
/ 64.0 ));
2202 edge1
->pos
= blue
->fit
;
2203 edge1
->flags
|= AF_EDGE_DONE
;
2205 if ( edge2
&& !edge2
->blue_edge
)
2207 af_latin_align_linked_edge( hints
, dim
, edge1
, edge2
);
2208 edge2
->flags
|= AF_EDGE_DONE
;
2210 #ifdef FT_DEBUG_LEVEL_TRACE
2220 /* now we align all other stem edges, trying to maintain the */
2221 /* relative order of stems in the glyph */
2222 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
2227 if ( edge
->flags
& AF_EDGE_DONE
)
2230 /* skip all non-stem edges */
2238 /* now align the stem */
2240 /* this should not happen, but it's better to be safe */
2241 if ( edge2
->blue_edge
)
2243 FT_TRACE5(( " ASSERTION FAILED for edge %d\n", edge2
-edges
));
2245 af_latin_align_linked_edge( hints
, dim
, edge2
, edge
);
2246 edge
->flags
|= AF_EDGE_DONE
;
2248 #ifdef FT_DEBUG_LEVEL_TRACE
2256 /* if we reach this if clause, no stem has been aligned yet */
2258 FT_Pos org_len
, org_center
, cur_len
;
2259 FT_Pos cur_pos1
, error1
, error2
, u_off
, d_off
;
2262 org_len
= edge2
->opos
- edge
->opos
;
2263 cur_len
= af_latin_compute_stem_width(
2264 hints
, dim
, org_len
,
2265 (AF_Edge_Flags
)edge
->flags
,
2266 (AF_Edge_Flags
)edge2
->flags
);
2268 /* some voodoo to specially round edges for small stem widths; */
2269 /* the idea is to align the center of a stem, then shifting */
2270 /* the stem edges to suitable positions */
2271 if ( cur_len
<= 64 )
2279 /* 1px < width < 1.5px */
2286 org_center
= edge
->opos
+ ( org_len
>> 1 );
2287 cur_pos1
= FT_PIX_ROUND( org_center
);
2289 error1
= org_center
- ( cur_pos1
- u_off
);
2293 error2
= org_center
- ( cur_pos1
+ d_off
);
2297 if ( error1
< error2
)
2302 edge
->pos
= cur_pos1
- cur_len
/ 2;
2303 edge2
->pos
= edge
->pos
+ cur_len
;
2306 edge
->pos
= FT_PIX_ROUND( edge
->opos
);
2309 edge
->flags
|= AF_EDGE_DONE
;
2311 FT_TRACE5(( " ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2312 " snapped to %.2f and %.2f\n",
2313 edge
- edges
, edge
->opos
/ 64.0,
2314 edge2
- edges
, edge2
->opos
/ 64.0,
2315 edge
->pos
/ 64.0, edge2
->pos
/ 64.0 ));
2317 af_latin_align_linked_edge( hints
, dim
, edge
, edge2
);
2319 #ifdef FT_DEBUG_LEVEL_TRACE
2325 FT_Pos org_pos
, org_len
, org_center
, cur_len
;
2326 FT_Pos cur_pos1
, cur_pos2
, delta1
, delta2
;
2329 org_pos
= anchor
->pos
+ ( edge
->opos
- anchor
->opos
);
2330 org_len
= edge2
->opos
- edge
->opos
;
2331 org_center
= org_pos
+ ( org_len
>> 1 );
2333 cur_len
= af_latin_compute_stem_width(
2334 hints
, dim
, org_len
,
2335 (AF_Edge_Flags
)edge
->flags
,
2336 (AF_Edge_Flags
)edge2
->flags
);
2338 if ( edge2
->flags
& AF_EDGE_DONE
)
2340 FT_TRACE5(( " ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2341 edge
- edges
, edge
->pos
/ 64.0,
2342 ( edge2
->pos
- cur_len
) / 64.0 ));
2344 edge
->pos
= edge2
->pos
- cur_len
;
2347 else if ( cur_len
< 96 )
2349 FT_Pos u_off
, d_off
;
2352 cur_pos1
= FT_PIX_ROUND( org_center
);
2354 if ( cur_len
<= 64 )
2365 delta1
= org_center
- ( cur_pos1
- u_off
);
2369 delta2
= org_center
- ( cur_pos1
+ d_off
);
2373 if ( delta1
< delta2
)
2378 edge
->pos
= cur_pos1
- cur_len
/ 2;
2379 edge2
->pos
= cur_pos1
+ cur_len
/ 2;
2381 FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2382 " snapped to %.2f and %.2f\n",
2383 edge
- edges
, edge
->opos
/ 64.0,
2384 edge2
- edges
, edge2
->opos
/ 64.0,
2385 edge
->pos
/ 64.0, edge2
->pos
/ 64.0 ));
2390 org_pos
= anchor
->pos
+ ( edge
->opos
- anchor
->opos
);
2391 org_len
= edge2
->opos
- edge
->opos
;
2392 org_center
= org_pos
+ ( org_len
>> 1 );
2394 cur_len
= af_latin_compute_stem_width(
2395 hints
, dim
, org_len
,
2396 (AF_Edge_Flags
)edge
->flags
,
2397 (AF_Edge_Flags
)edge2
->flags
);
2399 cur_pos1
= FT_PIX_ROUND( org_pos
);
2400 delta1
= cur_pos1
+ ( cur_len
>> 1 ) - org_center
;
2404 cur_pos2
= FT_PIX_ROUND( org_pos
+ org_len
) - cur_len
;
2405 delta2
= cur_pos2
+ ( cur_len
>> 1 ) - org_center
;
2409 edge
->pos
= ( delta1
< delta2
) ? cur_pos1
: cur_pos2
;
2410 edge2
->pos
= edge
->pos
+ cur_len
;
2412 FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2413 " snapped to %.2f and %.2f\n",
2414 edge
- edges
, edge
->opos
/ 64.0,
2415 edge2
- edges
, edge2
->opos
/ 64.0,
2416 edge
->pos
/ 64.0, edge2
->pos
/ 64.0 ));
2419 #ifdef FT_DEBUG_LEVEL_TRACE
2423 edge
->flags
|= AF_EDGE_DONE
;
2424 edge2
->flags
|= AF_EDGE_DONE
;
2426 if ( edge
> edges
&& edge
->pos
< edge
[-1].pos
)
2428 #ifdef FT_DEBUG_LEVEL_TRACE
2429 FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2430 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0 ));
2435 edge
->pos
= edge
[-1].pos
;
2440 /* make sure that lowercase m's maintain their symmetry */
2442 /* In general, lowercase m's have six vertical edges if they are sans */
2443 /* serif, or twelve if they are with serifs. This implementation is */
2444 /* based on that assumption, and seems to work very well with most */
2445 /* faces. However, if for a certain face this assumption is not */
2446 /* true, the m is just rendered like before. In addition, any stem */
2447 /* correction will only be applied to symmetrical glyphs (even if the */
2448 /* glyph is not an m), so the potential for unwanted distortion is */
2449 /* relatively low. */
2451 /* We don't handle horizontal edges since we can't easily assure that */
2452 /* the third (lowest) stem aligns with the base line; it might end up */
2453 /* one pixel higher or lower. */
2455 n_edges
= edge_limit
- edges
;
2456 if ( dim
== AF_DIMENSION_HORZ
&& ( n_edges
== 6 || n_edges
== 12 ) )
2458 AF_Edge edge1
, edge2
, edge3
;
2459 FT_Pos dist1
, dist2
, span
, delta
;
2475 dist1
= edge2
->opos
- edge1
->opos
;
2476 dist2
= edge3
->opos
- edge2
->opos
;
2478 span
= dist1
- dist2
;
2484 delta
= edge3
->pos
- ( 2 * edge2
->pos
- edge1
->pos
);
2485 edge3
->pos
-= delta
;
2487 edge3
->link
->pos
-= delta
;
2489 /* move the serifs along with the stem */
2490 if ( n_edges
== 12 )
2492 ( edges
+ 8 )->pos
-= delta
;
2493 ( edges
+ 11 )->pos
-= delta
;
2496 edge3
->flags
|= AF_EDGE_DONE
;
2498 edge3
->link
->flags
|= AF_EDGE_DONE
;
2502 if ( has_serifs
|| !anchor
)
2505 * now hint the remaining edges (serifs and single) in order
2506 * to complete our processing
2508 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
2513 if ( edge
->flags
& AF_EDGE_DONE
)
2520 delta
= edge
->serif
->opos
- edge
->opos
;
2525 if ( delta
< 64 + 16 )
2527 af_latin_align_serif_edge( hints
, edge
->serif
, edge
);
2528 FT_TRACE5(( " SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2529 " aligned to %.2f\n",
2530 edge
- edges
, edge
->opos
/ 64.0,
2531 edge
->serif
- edges
, edge
->serif
->opos
/ 64.0,
2532 edge
->pos
/ 64.0 ));
2536 edge
->pos
= FT_PIX_ROUND( edge
->opos
);
2538 FT_TRACE5(( " SERIF_ANCHOR: edge %d (opos=%.2f)"
2539 " snapped to %.2f\n",
2540 edge
-edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0 ));
2544 AF_Edge before
, after
;
2547 for ( before
= edge
- 1; before
>= edges
; before
-- )
2548 if ( before
->flags
& AF_EDGE_DONE
)
2551 for ( after
= edge
+ 1; after
< edge_limit
; after
++ )
2552 if ( after
->flags
& AF_EDGE_DONE
)
2555 if ( before
>= edges
&& before
< edge
&&
2556 after
< edge_limit
&& after
> edge
)
2558 if ( after
->opos
== before
->opos
)
2559 edge
->pos
= before
->pos
;
2561 edge
->pos
= before
->pos
+
2562 FT_MulDiv( edge
->opos
- before
->opos
,
2563 after
->pos
- before
->pos
,
2564 after
->opos
- before
->opos
);
2566 FT_TRACE5(( " SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2567 " from %d (opos=%.2f)\n",
2568 edge
- edges
, edge
->opos
/ 64.0,
2570 before
- edges
, before
->opos
/ 64.0 ));
2574 edge
->pos
= anchor
->pos
+
2575 ( ( edge
->opos
- anchor
->opos
+ 16 ) & ~31 );
2576 FT_TRACE5(( " SERIF_LINK2: edge %d (opos=%.2f)"
2577 " snapped to %.2f\n",
2578 edge
- edges
, edge
->opos
/ 64.0, edge
->pos
/ 64.0 ));
2582 #ifdef FT_DEBUG_LEVEL_TRACE
2585 edge
->flags
|= AF_EDGE_DONE
;
2587 if ( edge
> edges
&& edge
->pos
< edge
[-1].pos
)
2589 #ifdef FT_DEBUG_LEVEL_TRACE
2590 FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2591 edge
- edges
, edge
->pos
/ 64.0, edge
[-1].pos
/ 64.0 ));
2595 edge
->pos
= edge
[-1].pos
;
2598 if ( edge
+ 1 < edge_limit
&&
2599 edge
[1].flags
& AF_EDGE_DONE
&&
2600 edge
->pos
> edge
[1].pos
)
2602 #ifdef FT_DEBUG_LEVEL_TRACE
2603 FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2604 edge
- edges
, edge
->pos
/ 64.0, edge
[1].pos
/ 64.0 ));
2609 edge
->pos
= edge
[1].pos
;
2614 #ifdef FT_DEBUG_LEVEL_TRACE
2616 FT_TRACE5(( " (none)\n" ));
2617 FT_TRACE5(( "\n" ));
2622 /* Apply the complete hinting algorithm to a latin glyph. */
2625 af_latin_hints_apply( AF_GlyphHints hints
,
2626 FT_Outline
* outline
,
2627 AF_LatinMetrics metrics
)
2633 error
= af_glyph_hints_reload( hints
, outline
);
2637 /* analyze glyph outline */
2638 #ifdef AF_CONFIG_OPTION_USE_WARPER
2639 if ( metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
||
2640 AF_HINTS_DO_HORIZONTAL( hints
) )
2642 if ( AF_HINTS_DO_HORIZONTAL( hints
) )
2645 error
= af_latin_hints_detect_features( hints
, AF_DIMENSION_HORZ
);
2650 if ( AF_HINTS_DO_VERTICAL( hints
) )
2652 error
= af_latin_hints_detect_features( hints
, AF_DIMENSION_VERT
);
2656 af_latin_hints_compute_blue_edges( hints
, metrics
);
2659 /* grid-fit the outline */
2660 for ( dim
= 0; dim
< AF_DIMENSION_MAX
; dim
++ )
2662 #ifdef AF_CONFIG_OPTION_USE_WARPER
2663 if ( dim
== AF_DIMENSION_HORZ
&&
2664 metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_LIGHT
)
2666 AF_WarperRec warper
;
2671 af_warper_compute( &warper
, hints
, (AF_Dimension
)dim
,
2673 af_glyph_hints_scale_dim( hints
, (AF_Dimension
)dim
,
2679 if ( ( dim
== AF_DIMENSION_HORZ
&& AF_HINTS_DO_HORIZONTAL( hints
) ) ||
2680 ( dim
== AF_DIMENSION_VERT
&& AF_HINTS_DO_VERTICAL( hints
) ) )
2682 af_latin_hint_edges( hints
, (AF_Dimension
)dim
);
2683 af_glyph_hints_align_edge_points( hints
, (AF_Dimension
)dim
);
2684 af_glyph_hints_align_strong_points( hints
, (AF_Dimension
)dim
);
2685 af_glyph_hints_align_weak_points( hints
, (AF_Dimension
)dim
);
2689 af_glyph_hints_save( hints
, outline
);
2696 /*************************************************************************/
2697 /*************************************************************************/
2699 /***** L A T I N S C R I P T C L A S S *****/
2701 /*************************************************************************/
2702 /*************************************************************************/
2705 AF_DEFINE_WRITING_SYSTEM_CLASS(
2706 af_latin_writing_system_class
,
2708 AF_WRITING_SYSTEM_LATIN
,
2710 sizeof ( AF_LatinMetricsRec
),
2712 (AF_Script_InitMetricsFunc
) af_latin_metrics_init
,
2713 (AF_Script_ScaleMetricsFunc
)af_latin_metrics_scale
,
2714 (AF_Script_DoneMetricsFunc
) NULL
,
2716 (AF_Script_InitHintsFunc
) af_latin_hints_init
,
2717 (AF_Script_ApplyHintsFunc
) af_latin_hints_apply
2721 /* XXX: this should probably fine tuned to differentiate better between */
2724 static const AF_Script_UniRangeRec af_latn_uniranges
[] =
2726 AF_UNIRANGE_REC( 0x0020UL
, 0x007FUL
), /* Basic Latin (no control chars) */
2727 AF_UNIRANGE_REC( 0x00A0UL
, 0x00FFUL
), /* Latin-1 Supplement (no control chars) */
2728 AF_UNIRANGE_REC( 0x0100UL
, 0x017FUL
), /* Latin Extended-A */
2729 AF_UNIRANGE_REC( 0x0180UL
, 0x024FUL
), /* Latin Extended-B */
2730 AF_UNIRANGE_REC( 0x0250UL
, 0x02AFUL
), /* IPA Extensions */
2731 AF_UNIRANGE_REC( 0x02B0UL
, 0x02FFUL
), /* Spacing Modifier Letters */
2732 AF_UNIRANGE_REC( 0x0300UL
, 0x036FUL
), /* Combining Diacritical Marks */
2733 AF_UNIRANGE_REC( 0x1D00UL
, 0x1D7FUL
), /* Phonetic Extensions */
2734 AF_UNIRANGE_REC( 0x1D80UL
, 0x1DBFUL
), /* Phonetic Extensions Supplement */
2735 AF_UNIRANGE_REC( 0x1DC0UL
, 0x1DFFUL
), /* Combining Diacritical Marks Supplement */
2736 AF_UNIRANGE_REC( 0x1E00UL
, 0x1EFFUL
), /* Latin Extended Additional */
2737 AF_UNIRANGE_REC( 0x2000UL
, 0x206FUL
), /* General Punctuation */
2738 AF_UNIRANGE_REC( 0x2070UL
, 0x209FUL
), /* Superscripts and Subscripts */
2739 AF_UNIRANGE_REC( 0x20A0UL
, 0x20CFUL
), /* Currency Symbols */
2740 AF_UNIRANGE_REC( 0x2150UL
, 0x218FUL
), /* Number Forms */
2741 AF_UNIRANGE_REC( 0x2460UL
, 0x24FFUL
), /* Enclosed Alphanumerics */
2742 AF_UNIRANGE_REC( 0x2C60UL
, 0x2C7FUL
), /* Latin Extended-C */
2743 AF_UNIRANGE_REC( 0x2E00UL
, 0x2E7FUL
), /* Supplemental Punctuation */
2744 AF_UNIRANGE_REC( 0xA720UL
, 0xA7FFUL
), /* Latin Extended-D */
2745 AF_UNIRANGE_REC( 0xFB00UL
, 0xFB06UL
), /* Alphab. Present. Forms (Latin Ligs) */
2746 AF_UNIRANGE_REC( 0x1D400UL
, 0x1D7FFUL
), /* Mathematical Alphanumeric Symbols */
2747 AF_UNIRANGE_REC( 0x1F100UL
, 0x1F1FFUL
), /* Enclosed Alphanumeric Supplement */
2748 AF_UNIRANGE_REC( 0UL, 0UL )
2751 static const AF_Script_UniRangeRec af_grek_uniranges
[] =
2753 AF_UNIRANGE_REC( 0x0370UL
, 0x03FFUL
), /* Greek and Coptic */
2754 AF_UNIRANGE_REC( 0x1F00UL
, 0x1FFFUL
), /* Greek Extended */
2755 AF_UNIRANGE_REC( 0UL, 0UL )
2758 static const AF_Script_UniRangeRec af_cyrl_uniranges
[] =
2760 AF_UNIRANGE_REC( 0x0400UL
, 0x04FFUL
), /* Cyrillic */
2761 AF_UNIRANGE_REC( 0x0500UL
, 0x052FUL
), /* Cyrillic Supplement */
2762 AF_UNIRANGE_REC( 0x2DE0UL
, 0x2DFFUL
), /* Cyrillic Extended-A */
2763 AF_UNIRANGE_REC( 0xA640UL
, 0xA69FUL
), /* Cyrillic Extended-B */
2764 AF_UNIRANGE_REC( 0UL, 0UL )
2767 static const AF_Script_UniRangeRec af_hebr_uniranges
[] =
2769 AF_UNIRANGE_REC( 0x0590UL
, 0x05FFUL
), /* Hebrew */
2770 AF_UNIRANGE_REC( 0xFB1DUL
, 0xFB4FUL
), /* Alphab. Present. Forms (Hebrew) */
2771 AF_UNIRANGE_REC( 0UL, 0UL )
2775 AF_DEFINE_SCRIPT_CLASS(
2776 af_latn_script_class
,
2779 AF_BLUE_STRINGSET_LATN
,
2780 AF_WRITING_SYSTEM_LATIN
,
2786 AF_DEFINE_SCRIPT_CLASS(
2787 af_grek_script_class
,
2790 AF_BLUE_STRINGSET_GREK
,
2791 AF_WRITING_SYSTEM_LATIN
,
2797 AF_DEFINE_SCRIPT_CLASS(
2798 af_cyrl_script_class
,
2801 AF_BLUE_STRINGSET_CYRL
,
2802 AF_WRITING_SYSTEM_LATIN
,
2808 AF_DEFINE_SCRIPT_CLASS(
2809 af_hebr_script_class
,
2812 AF_BLUE_STRINGSET_HEBR
,
2813 AF_WRITING_SYSTEM_LATIN
,