1 /***************************************************************************/
5 /* Auto-fitter hinting routines for CJK script (body). */
7 /* Copyright 2006, 2007, 2008, 2009 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 /***************************************************************************/
19 * The algorithm is based on akito's autohint patch, available here:
21 * http://www.kde.gr.jp/~akito/patch/freetype2/
29 #ifdef AF_CONFIG_OPTION_CJK
40 /*************************************************************************/
41 /*************************************************************************/
43 /***** C J K G L O B A L M E T R I C S *****/
45 /*************************************************************************/
46 /*************************************************************************/
48 FT_LOCAL_DEF( FT_Error
)
49 af_cjk_metrics_init( AF_LatinMetrics metrics
,
52 FT_CharMap oldmap
= face
->charmap
;
55 metrics
->units_per_em
= face
->units_per_EM
;
57 /* TODO are there blues? */
59 if ( FT_Select_Charmap( face
, FT_ENCODING_UNICODE
) )
63 /* latin's version would suffice */
64 af_latin_metrics_init_widths( metrics
, face
, 0x7530 );
65 af_latin_metrics_check_digits( metrics
, face
);
68 FT_Set_Charmap( face
, oldmap
);
75 af_cjk_metrics_scale_dim( AF_LatinMetrics metrics
,
82 axis
= &metrics
->axis
[dim
];
84 if ( dim
== AF_DIMENSION_HORZ
)
86 axis
->scale
= scaler
->x_scale
;
87 axis
->delta
= scaler
->x_delta
;
91 axis
->scale
= scaler
->y_scale
;
92 axis
->delta
= scaler
->y_delta
;
98 af_cjk_metrics_scale( AF_LatinMetrics metrics
,
101 metrics
->root
.scaler
= *scaler
;
103 af_cjk_metrics_scale_dim( metrics
, scaler
, AF_DIMENSION_HORZ
);
104 af_cjk_metrics_scale_dim( metrics
, scaler
, AF_DIMENSION_VERT
);
108 /*************************************************************************/
109 /*************************************************************************/
111 /***** C J K G L Y P H A N A L Y S I S *****/
113 /*************************************************************************/
114 /*************************************************************************/
117 af_cjk_hints_compute_segments( AF_GlyphHints hints
,
120 AF_AxisHints axis
= &hints
->axis
[dim
];
121 AF_Segment segments
= axis
->segments
;
122 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
127 error
= af_latin_hints_compute_segments( hints
, dim
);
131 /* a segment is round if it doesn't have successive */
132 /* on-curve points. */
133 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
135 AF_Point pt
= seg
->first
;
136 AF_Point last
= seg
->last
;
137 AF_Flags f0
= (AF_Flags
)(pt
->flags
& AF_FLAG_CONTROL
);
141 seg
->flags
&= ~AF_EDGE_ROUND
;
143 for ( ; pt
!= last
; f0
= f1
)
146 f1
= (AF_Flags
)(pt
->flags
& AF_FLAG_CONTROL
);
152 seg
->flags
|= AF_EDGE_ROUND
;
161 af_cjk_hints_link_segments( AF_GlyphHints hints
,
164 AF_AxisHints axis
= &hints
->axis
[dim
];
165 AF_Segment segments
= axis
->segments
;
166 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
167 AF_Direction major_dir
= axis
->major_dir
;
168 AF_Segment seg1
, seg2
;
169 FT_Pos len_threshold
;
170 FT_Pos dist_threshold
;
173 len_threshold
= AF_LATIN_CONSTANT( hints
->metrics
, 8 );
175 dist_threshold
= ( dim
== AF_DIMENSION_HORZ
) ? hints
->x_scale
177 dist_threshold
= FT_DivFix( 64 * 3, dist_threshold
);
179 /* now compare each segment to the others */
180 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
182 /* the fake segments are for metrics hinting only */
183 if ( seg1
->first
== seg1
->last
)
186 if ( seg1
->dir
!= major_dir
)
189 for ( seg2
= segments
; seg2
< segment_limit
; seg2
++ )
190 if ( seg2
!= seg1
&& seg1
->dir
+ seg2
->dir
== 0 )
192 FT_Pos dist
= seg2
->pos
- seg1
->pos
;
199 FT_Pos min
= seg1
->min_coord
;
200 FT_Pos max
= seg1
->max_coord
;
204 if ( min
< seg2
->min_coord
)
205 min
= seg2
->min_coord
;
207 if ( max
> seg2
->max_coord
)
208 max
= seg2
->max_coord
;
211 if ( len
>= len_threshold
)
213 if ( dist
* 8 < seg1
->score
* 9 &&
214 ( dist
* 8 < seg1
->score
* 7 || seg1
->len
< len
) )
221 if ( dist
* 8 < seg2
->score
* 9 &&
222 ( dist
* 8 < seg2
->score
* 7 || seg2
->len
< len
) )
234 * now compute the `serif' segments
236 * In Hanzi, some strokes are wider on one or both of the ends.
237 * We either identify the stems on the ends as serifs or remove
238 * the linkage, depending on the length of the stems.
243 AF_Segment link1
, link2
;
246 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
249 if ( !link1
|| link1
->link
!= seg1
|| link1
->pos
<= seg1
->pos
)
252 if ( seg1
->score
>= dist_threshold
)
255 for ( seg2
= segments
; seg2
< segment_limit
; seg2
++ )
257 if ( seg2
->pos
> seg1
->pos
|| seg1
== seg2
)
261 if ( !link2
|| link2
->link
!= seg2
|| link2
->pos
< link1
->pos
)
264 if ( seg1
->pos
== seg2
->pos
&& link1
->pos
== link2
->pos
)
267 if ( seg2
->score
<= seg1
->score
|| seg1
->score
* 4 <= seg2
->score
)
270 /* seg2 < seg1 < link1 < link2 */
272 if ( seg1
->len
>= seg2
->len
* 3 )
277 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
279 AF_Segment link
= seg
->link
;
287 else if ( link
== link2
)
296 seg1
->link
= link1
->link
= 0;
304 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
311 if ( seg2
->link
!= seg1
)
315 if ( seg2
->score
< dist_threshold
|| seg1
->score
< seg2
->score
* 4 )
316 seg1
->serif
= seg2
->link
;
326 af_cjk_hints_compute_edges( AF_GlyphHints hints
,
329 AF_AxisHints axis
= &hints
->axis
[dim
];
330 FT_Error error
= AF_Err_Ok
;
331 FT_Memory memory
= hints
->memory
;
332 AF_LatinAxis laxis
= &((AF_LatinMetrics
)hints
->metrics
)->axis
[dim
];
334 AF_Segment segments
= axis
->segments
;
335 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
339 FT_Pos edge_distance_threshold
;
344 scale
= ( dim
== AF_DIMENSION_HORZ
) ? hints
->x_scale
347 /*********************************************************************/
349 /* We begin by generating a sorted table of edges for the current */
350 /* direction. To do so, we simply scan each segment and try to find */
351 /* an edge in our table that corresponds to its position. */
353 /* If no edge is found, we create and insert a new edge in the */
354 /* sorted table. Otherwise, we simply add the segment to the edge's */
355 /* list which is then processed in the second step to compute the */
356 /* edge's properties. */
358 /* Note that the edges table is sorted along the segment/edge */
361 /*********************************************************************/
363 edge_distance_threshold
= FT_MulFix( laxis
->edge_distance_threshold
,
365 if ( edge_distance_threshold
> 64 / 4 )
366 edge_distance_threshold
= FT_DivFix( 64 / 4, scale
);
368 edge_distance_threshold
= laxis
->edge_distance_threshold
;
370 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
373 FT_Pos best
= 0xFFFFU
;
377 /* look for an edge corresponding to the segment */
378 for ( ee
= 0; ee
< axis
->num_edges
; ee
++ )
380 AF_Edge edge
= axis
->edges
+ ee
;
384 if ( edge
->dir
!= seg
->dir
)
387 dist
= seg
->pos
- edge
->fpos
;
391 if ( dist
< edge_distance_threshold
&& dist
< best
)
393 AF_Segment link
= seg
->link
;
396 /* check whether all linked segments of the candidate edge */
397 /* can make a single edge. */
400 AF_Segment seg1
= edge
->first
;
410 dist2
= AF_SEGMENT_DIST( link
, link1
);
411 if ( dist2
>= edge_distance_threshold
)
415 } while ( ( seg1
= seg1
->edge_next
) != edge
->first
);
417 if ( dist2
>= edge_distance_threshold
)
431 /* insert a new edge in the list and */
432 /* sort according to the position */
433 error
= af_axis_hints_new_edge( axis
, seg
->pos
,
434 (AF_Direction
)seg
->dir
,
439 /* add the segment to the new edge's list */
444 edge
->fpos
= seg
->pos
;
445 edge
->opos
= edge
->pos
= FT_MulFix( seg
->pos
, scale
);
446 seg
->edge_next
= seg
;
447 edge
->dir
= seg
->dir
;
451 /* if an edge was found, simply add the segment to the edge's */
453 seg
->edge_next
= found
->first
;
454 found
->last
->edge_next
= seg
;
459 /*********************************************************************/
461 /* Good, we now compute each edge's properties according to segments */
462 /* found on its position. Basically, these are as follows. */
464 /* - edge's main direction */
465 /* - stem edge, serif edge or both (which defaults to stem then) */
466 /* - rounded edge, straight or both (which defaults to straight) */
467 /* - link for edge */
469 /*********************************************************************/
471 /* first of all, set the `edge' field in each segment -- this is */
472 /* required in order to compute edge links */
474 /* Note that removing this loop and setting the `edge' field of each */
475 /* segment directly in the code above slows down execution speed for */
476 /* some reasons on platforms like the Sun. */
479 AF_Edge edges
= axis
->edges
;
480 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
484 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
491 seg
= seg
->edge_next
;
493 } while ( seg
!= edge
->first
);
496 /* now compute each edge properties */
497 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
499 FT_Int is_round
= 0; /* does it contain round segments? */
500 FT_Int is_straight
= 0; /* does it contain straight segments? */
510 /* check for roundness of segment */
511 if ( seg
->flags
& AF_EDGE_ROUND
)
516 /* check for links -- if seg->serif is set, then seg->link must */
518 is_serif
= (FT_Bool
)( seg
->serif
&& seg
->serif
->edge
!= edge
);
520 if ( seg
->link
|| is_serif
)
541 edge_delta
= edge
->fpos
- edge2
->fpos
;
542 if ( edge_delta
< 0 )
543 edge_delta
= -edge_delta
;
545 seg_delta
= AF_SEGMENT_DIST( seg
, seg2
);
547 if ( seg_delta
< edge_delta
)
556 edge2
->flags
|= AF_EDGE_SERIF
;
562 seg
= seg
->edge_next
;
564 } while ( seg
!= edge
->first
);
566 /* set the round/straight flags */
567 edge
->flags
= AF_EDGE_NORMAL
;
569 if ( is_round
> 0 && is_round
>= is_straight
)
570 edge
->flags
|= AF_EDGE_ROUND
;
572 /* get rid of serifs if link is set */
573 /* XXX: This gets rid of many unpleasant artefacts! */
574 /* Example: the `c' in cour.pfa at size 13 */
576 if ( edge
->serif
&& edge
->link
)
587 af_cjk_hints_detect_features( AF_GlyphHints hints
,
593 error
= af_cjk_hints_compute_segments( hints
, dim
);
596 af_cjk_hints_link_segments( hints
, dim
);
598 error
= af_cjk_hints_compute_edges( hints
, dim
);
604 FT_LOCAL_DEF( FT_Error
)
605 af_cjk_hints_init( AF_GlyphHints hints
,
606 AF_LatinMetrics metrics
)
609 FT_UInt32 scaler_flags
, other_flags
;
612 af_glyph_hints_rescale( hints
, (AF_ScriptMetrics
)metrics
);
615 * correct x_scale and y_scale when needed, since they may have
616 * been modified af_cjk_scale_dim above
618 hints
->x_scale
= metrics
->axis
[AF_DIMENSION_HORZ
].scale
;
619 hints
->x_delta
= metrics
->axis
[AF_DIMENSION_HORZ
].delta
;
620 hints
->y_scale
= metrics
->axis
[AF_DIMENSION_VERT
].scale
;
621 hints
->y_delta
= metrics
->axis
[AF_DIMENSION_VERT
].delta
;
623 /* compute flags depending on render mode, etc. */
624 mode
= metrics
->root
.scaler
.render_mode
;
627 if ( mode
== FT_RENDER_MODE_LCD
|| mode
== FT_RENDER_MODE_LCD_V
)
628 metrics
->root
.scaler
.render_mode
= mode
= FT_RENDER_MODE_NORMAL
;
631 scaler_flags
= hints
->scaler_flags
;
635 * We snap the width of vertical stems for the monochrome and
636 * horizontal LCD rendering targets only.
638 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD
)
639 other_flags
|= AF_LATIN_HINTS_HORZ_SNAP
;
642 * We snap the width of horizontal stems for the monochrome and
643 * vertical LCD rendering targets only.
645 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD_V
)
646 other_flags
|= AF_LATIN_HINTS_VERT_SNAP
;
649 * We adjust stems to full pixels only if we don't use the `light' mode.
651 if ( mode
!= FT_RENDER_MODE_LIGHT
)
652 other_flags
|= AF_LATIN_HINTS_STEM_ADJUST
;
654 if ( mode
== FT_RENDER_MODE_MONO
)
655 other_flags
|= AF_LATIN_HINTS_MONO
;
657 scaler_flags
|= AF_SCALER_FLAG_NO_ADVANCE
;
659 hints
->scaler_flags
= scaler_flags
;
660 hints
->other_flags
= other_flags
;
666 /*************************************************************************/
667 /*************************************************************************/
669 /***** C J K G L Y P H G R I D - F I T T I N G *****/
671 /*************************************************************************/
672 /*************************************************************************/
674 /* snap a given width in scaled coordinates to one of the */
675 /* current standard widths */
678 af_cjk_snap_width( AF_Width widths
,
683 FT_Pos best
= 64 + 32 + 2;
684 FT_Pos reference
= width
;
688 for ( n
= 0; n
< count
; n
++ )
705 scaled
= FT_PIX_ROUND( reference
);
707 if ( width
>= reference
)
709 if ( width
< scaled
+ 48 )
714 if ( width
> scaled
- 48 )
722 /* compute the snapped width of a given stem */
725 af_cjk_compute_stem_width( AF_GlyphHints hints
,
728 AF_Edge_Flags base_flags
,
729 AF_Edge_Flags stem_flags
)
731 AF_LatinMetrics metrics
= (AF_LatinMetrics
) hints
->metrics
;
732 AF_LatinAxis axis
= & metrics
->axis
[dim
];
735 FT_Int vertical
= ( dim
== AF_DIMENSION_VERT
);
737 FT_UNUSED( base_flags
);
738 FT_UNUSED( stem_flags
);
741 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
750 if ( ( vertical
&& !AF_LATIN_HINTS_DO_VERT_SNAP( hints
) ) ||
751 ( !vertical
&& !AF_LATIN_HINTS_DO_HORZ_SNAP( hints
) ) )
753 /* smooth hinting process: very lightly quantize the stem width */
755 if ( axis
->width_count
> 0 )
757 if ( FT_ABS( dist
- axis
->widths
[0].cur
) < 40 )
759 dist
= axis
->widths
[0].cur
;
768 dist
+= ( 54 - dist
) / 2 ;
769 else if ( dist
< 3 * 64 )
779 else if ( delta
< 22 )
781 else if ( delta
< 42 )
783 else if ( delta
< 54 )
791 /* strong hinting process: snap the stem width to integer pixels */
793 dist
= af_cjk_snap_width( axis
->widths
, axis
->width_count
, dist
);
797 /* in the case of vertical hinting, always round */
798 /* the stem heights to integer pixels */
801 dist
= ( dist
+ 16 ) & ~63;
807 if ( AF_LATIN_HINTS_DO_MONO( hints
) )
809 /* monochrome horizontal hinting: snap widths to integer pixels */
810 /* with a different threshold */
815 dist
= ( dist
+ 32 ) & ~63;
819 /* for horizontal anti-aliased hinting, we adopt a more subtle */
820 /* approach: we strengthen small stems, round stems whose size */
821 /* is between 1 and 2 pixels to an integer, otherwise nothing */
824 dist
= ( dist
+ 64 ) >> 1;
826 else if ( dist
< 128 )
827 dist
= ( dist
+ 22 ) & ~63;
829 /* round otherwise to prevent color fringes in LCD mode */
830 dist
= ( dist
+ 32 ) & ~63;
843 /* align one stem edge relative to the previous stem edge */
846 af_cjk_align_linked_edge( AF_GlyphHints hints
,
851 FT_Pos dist
= stem_edge
->opos
- base_edge
->opos
;
853 FT_Pos fitted_width
= af_cjk_compute_stem_width(
855 (AF_Edge_Flags
)base_edge
->flags
,
856 (AF_Edge_Flags
)stem_edge
->flags
);
859 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
864 af_cjk_align_serif_edge( AF_GlyphHints hints
,
870 serif
->pos
= base
->pos
+ ( serif
->opos
- base
->opos
);
874 /*************************************************************************/
875 /*************************************************************************/
876 /*************************************************************************/
878 /**** E D G E H I N T I N G ****/
880 /*************************************************************************/
881 /*************************************************************************/
882 /*************************************************************************/
885 #define AF_LIGHT_MODE_MAX_HORZ_GAP 9
886 #define AF_LIGHT_MODE_MAX_VERT_GAP 15
887 #define AF_LIGHT_MODE_MAX_DELTA_ABS 14
891 af_hint_normal_stem( AF_GlyphHints hints
,
897 FT_Pos org_len
, cur_len
, org_center
;
898 FT_Pos cur_pos1
, cur_pos2
;
899 FT_Pos d_off1
, u_off1
, d_off2
, u_off2
, delta
;
901 FT_Pos threshold
= 64;
904 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
906 if ( ( edge
->flags
& AF_EDGE_ROUND
) &&
907 ( edge2
->flags
& AF_EDGE_ROUND
) )
909 if ( dim
== AF_DIMENSION_VERT
)
910 threshold
= 64 - AF_LIGHT_MODE_MAX_HORZ_GAP
;
912 threshold
= 64 - AF_LIGHT_MODE_MAX_VERT_GAP
;
916 if ( dim
== AF_DIMENSION_VERT
)
917 threshold
= 64 - AF_LIGHT_MODE_MAX_HORZ_GAP
/ 3;
919 threshold
= 64 - AF_LIGHT_MODE_MAX_VERT_GAP
/ 3;
923 org_len
= edge2
->opos
- edge
->opos
;
924 cur_len
= af_cjk_compute_stem_width( hints
, dim
, org_len
,
925 (AF_Edge_Flags
)edge
->flags
,
926 (AF_Edge_Flags
)edge2
->flags
);
928 org_center
= ( edge
->opos
+ edge2
->opos
) / 2 + anchor
;
929 cur_pos1
= org_center
- cur_len
/ 2;
930 cur_pos2
= cur_pos1
+ cur_len
;
931 d_off1
= cur_pos1
- FT_PIX_FLOOR( cur_pos1
);
932 d_off2
= cur_pos2
- FT_PIX_FLOOR( cur_pos2
);
933 u_off1
= 64 - d_off1
;
934 u_off2
= 64 - d_off2
;
938 if ( d_off1
== 0 || d_off2
== 0 )
941 if ( cur_len
<= threshold
)
943 if ( d_off2
< cur_len
)
945 if ( u_off1
<= d_off2
)
954 if ( threshold
< 64 )
956 if ( d_off1
>= threshold
|| u_off1
>= threshold
||
957 d_off2
>= threshold
|| u_off2
>= threshold
)
961 offset
= cur_len
% 64;
965 if ( u_off1
<= offset
|| d_off2
<= offset
)
969 offset
= 64 - threshold
;
971 d_off1
= threshold
- u_off1
;
972 u_off1
= u_off1
- offset
;
973 u_off2
= threshold
- d_off2
;
974 d_off2
= d_off2
- offset
;
976 if ( d_off1
<= u_off1
)
979 if ( d_off2
<= u_off2
)
982 if ( FT_ABS( u_off1
) <= FT_ABS( u_off2
) )
990 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
992 if ( delta
> AF_LIGHT_MODE_MAX_DELTA_ABS
)
993 delta
= AF_LIGHT_MODE_MAX_DELTA_ABS
;
994 else if ( delta
< -AF_LIGHT_MODE_MAX_DELTA_ABS
)
995 delta
= -AF_LIGHT_MODE_MAX_DELTA_ABS
;
1001 if ( edge
->opos
< edge2
->opos
)
1003 edge
->pos
= cur_pos1
;
1004 edge2
->pos
= cur_pos1
+ cur_len
;
1008 edge
->pos
= cur_pos1
+ cur_len
;
1009 edge2
->pos
= cur_pos1
;
1017 af_cjk_hint_edges( AF_GlyphHints hints
,
1020 AF_AxisHints axis
= &hints
->axis
[dim
];
1021 AF_Edge edges
= axis
->edges
;
1022 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1030 /* now we align all stem edges. */
1031 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1036 if ( edge
->flags
& AF_EDGE_DONE
)
1039 /* skip all non-stem edges */
1047 /* now align the stem */
1051 af_cjk_align_linked_edge( hints
, dim
, edge2
, edge
);
1052 edge
->flags
|= AF_EDGE_DONE
;
1056 if ( dim
!= AF_DIMENSION_VERT
&& !anchor
)
1062 AF_Edge left
= edge
;
1063 AF_Edge right
= edge_limit
- 1;
1064 AF_EdgeRec left1
, left2
, right1
, right2
;
1065 FT_Pos target
, center1
, center2
;
1066 FT_Pos delta1
, delta2
, d1
, d2
;
1069 while ( right
> left
&& !right
->link
)
1073 left2
= *left
->link
;
1074 right1
= *right
->link
;
1077 delta
= ( ( ( hinter
->pp2
.x
+ 32 ) & -64 ) - hinter
->pp2
.x
) / 2;
1078 target
= left
->opos
+ ( right
->opos
- left
->opos
) / 2 + delta
- 16;
1081 delta1
+= af_hint_normal_stem( hints
, left
, left
->link
,
1084 if ( left
->link
!= right
)
1085 af_hint_normal_stem( hints
, right
->link
, right
, delta1
, 0 );
1087 center1
= left
->pos
+ ( right
->pos
- left
->pos
) / 2;
1089 if ( center1
>= target
)
1090 delta2
= delta
- 32;
1092 delta2
= delta
+ 32;
1094 delta2
+= af_hint_normal_stem( hints
, &left1
, &left2
, delta2
, 0 );
1096 if ( delta1
!= delta2
)
1098 if ( left
->link
!= right
)
1099 af_hint_normal_stem( hints
, &right1
, &right2
, delta2
, 0 );
1101 center2
= left1
.pos
+ ( right2
.pos
- left1
.pos
) / 2;
1103 d1
= center1
- target
;
1104 d2
= center2
- target
;
1106 if ( FT_ABS( d2
) < FT_ABS( d1
) )
1108 left
->pos
= left1
.pos
;
1109 left
->link
->pos
= left2
.pos
;
1111 if ( left
->link
!= right
)
1113 right
->link
->pos
= right1
.pos
;
1114 right
->pos
= right2
.pos
;
1122 right
->link
->flags
|= AF_EDGE_DONE
;
1123 right
->flags
|= AF_EDGE_DONE
;
1129 delta
= af_hint_normal_stem( hints
, edge
, edge2
, 0,
1130 AF_DIMENSION_HORZ
);
1133 af_hint_normal_stem( hints
, edge
, edge2
, delta
, dim
);
1136 printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
1137 edge
- edges
, edge2
- edges
,
1138 ( edge
->pos
- edge
->opos
) / 64.0,
1139 ( edge2
->pos
- edge2
->opos
) / 64.0 );
1143 edge
->flags
|= AF_EDGE_DONE
;
1144 edge2
->flags
|= AF_EDGE_DONE
;
1147 /* make sure that lowercase m's maintain their symmetry */
1149 /* In general, lowercase m's have six vertical edges if they are sans */
1150 /* serif, or twelve if they are with serifs. This implementation is */
1151 /* based on that assumption, and seems to work very well with most */
1152 /* faces. However, if for a certain face this assumption is not */
1153 /* true, the m is just rendered like before. In addition, any stem */
1154 /* correction will only be applied to symmetrical glyphs (even if the */
1155 /* glyph is not an m), so the potential for unwanted distortion is */
1156 /* relatively low. */
1158 /* We don't handle horizontal edges since we can't easily assure that */
1159 /* the third (lowest) stem aligns with the base line; it might end up */
1160 /* one pixel higher or lower. */
1162 n_edges
= edge_limit
- edges
;
1163 if ( dim
== AF_DIMENSION_HORZ
&& ( n_edges
== 6 || n_edges
== 12 ) )
1165 AF_Edge edge1
, edge2
, edge3
;
1166 FT_Pos dist1
, dist2
, span
;
1182 dist1
= edge2
->opos
- edge1
->opos
;
1183 dist2
= edge3
->opos
- edge2
->opos
;
1185 span
= dist1
- dist2
;
1189 if ( edge1
->link
== edge1
+ 1 &&
1190 edge2
->link
== edge2
+ 1 &&
1191 edge3
->link
== edge3
+ 1 && span
< 8 )
1193 delta
= edge3
->pos
- ( 2 * edge2
->pos
- edge1
->pos
);
1194 edge3
->pos
-= delta
;
1196 edge3
->link
->pos
-= delta
;
1198 /* move the serifs along with the stem */
1199 if ( n_edges
== 12 )
1201 ( edges
+ 8 )->pos
-= delta
;
1202 ( edges
+ 11 )->pos
-= delta
;
1205 edge3
->flags
|= AF_EDGE_DONE
;
1207 edge3
->link
->flags
|= AF_EDGE_DONE
;
1215 * now hint the remaining edges (serifs and single) in order
1216 * to complete our processing
1218 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1220 if ( edge
->flags
& AF_EDGE_DONE
)
1225 af_cjk_align_serif_edge( hints
, edge
->serif
, edge
);
1226 edge
->flags
|= AF_EDGE_DONE
;
1234 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1236 AF_Edge before
, after
;
1239 if ( edge
->flags
& AF_EDGE_DONE
)
1242 before
= after
= edge
;
1244 while ( --before
>= edges
)
1245 if ( before
->flags
& AF_EDGE_DONE
)
1248 while ( ++after
< edge_limit
)
1249 if ( after
->flags
& AF_EDGE_DONE
)
1252 if ( before
>= edges
|| after
< edge_limit
)
1254 if ( before
< edges
)
1255 af_cjk_align_serif_edge( hints
, after
, edge
);
1256 else if ( after
>= edge_limit
)
1257 af_cjk_align_serif_edge( hints
, before
, edge
);
1260 if ( after
->fpos
== before
->fpos
)
1261 edge
->pos
= before
->pos
;
1263 edge
->pos
= before
->pos
+
1264 FT_MulDiv( edge
->fpos
- before
->fpos
,
1265 after
->pos
- before
->pos
,
1266 after
->fpos
- before
->fpos
);
1274 af_cjk_align_edge_points( AF_GlyphHints hints
,
1277 AF_AxisHints axis
= & hints
->axis
[dim
];
1278 AF_Edge edges
= axis
->edges
;
1279 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1284 snapping
= FT_BOOL( ( dim
== AF_DIMENSION_HORZ
&&
1285 AF_LATIN_HINTS_DO_HORZ_SNAP( hints
) ) ||
1286 ( dim
== AF_DIMENSION_VERT
&&
1287 AF_LATIN_HINTS_DO_VERT_SNAP( hints
) ) );
1289 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1291 /* move the points of each segment */
1292 /* in each edge to the edge's position */
1293 AF_Segment seg
= edge
->first
;
1300 AF_Point point
= seg
->first
;
1305 if ( dim
== AF_DIMENSION_HORZ
)
1307 point
->x
= edge
->pos
;
1308 point
->flags
|= AF_FLAG_TOUCH_X
;
1312 point
->y
= edge
->pos
;
1313 point
->flags
|= AF_FLAG_TOUCH_Y
;
1316 if ( point
== seg
->last
)
1319 point
= point
->next
;
1322 seg
= seg
->edge_next
;
1324 } while ( seg
!= edge
->first
);
1328 FT_Pos delta
= edge
->pos
- edge
->opos
;
1333 AF_Point point
= seg
->first
;
1338 if ( dim
== AF_DIMENSION_HORZ
)
1341 point
->flags
|= AF_FLAG_TOUCH_X
;
1346 point
->flags
|= AF_FLAG_TOUCH_Y
;
1349 if ( point
== seg
->last
)
1352 point
= point
->next
;
1355 seg
= seg
->edge_next
;
1357 } while ( seg
!= edge
->first
);
1363 FT_LOCAL_DEF( FT_Error
)
1364 af_cjk_hints_apply( AF_GlyphHints hints
,
1365 FT_Outline
* outline
,
1366 AF_LatinMetrics metrics
)
1371 FT_UNUSED( metrics
);
1374 error
= af_glyph_hints_reload( hints
, outline
, 0 );
1378 /* analyze glyph outline */
1379 if ( AF_HINTS_DO_HORIZONTAL( hints
) )
1381 error
= af_cjk_hints_detect_features( hints
, AF_DIMENSION_HORZ
);
1386 if ( AF_HINTS_DO_VERTICAL( hints
) )
1388 error
= af_cjk_hints_detect_features( hints
, AF_DIMENSION_VERT
);
1393 /* grid-fit the outline */
1394 for ( dim
= 0; dim
< AF_DIMENSION_MAX
; dim
++ )
1396 if ( ( dim
== AF_DIMENSION_HORZ
&& AF_HINTS_DO_HORIZONTAL( hints
) ) ||
1397 ( dim
== AF_DIMENSION_VERT
&& AF_HINTS_DO_VERTICAL( hints
) ) )
1400 #ifdef AF_USE_WARPER
1401 if ( dim
== AF_DIMENSION_HORZ
&&
1402 metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_NORMAL
)
1404 AF_WarperRec warper
;
1409 af_warper_compute( &warper
, hints
, dim
, &scale
, &delta
);
1410 af_glyph_hints_scale_dim( hints
, dim
, scale
, delta
);
1413 #endif /* AF_USE_WARPER */
1415 af_cjk_hint_edges( hints
, (AF_Dimension
)dim
);
1416 af_cjk_align_edge_points( hints
, (AF_Dimension
)dim
);
1417 af_glyph_hints_align_strong_points( hints
, (AF_Dimension
)dim
);
1418 af_glyph_hints_align_weak_points( hints
, (AF_Dimension
)dim
);
1423 af_glyph_hints_dump_points( hints
);
1424 af_glyph_hints_dump_segments( hints
);
1425 af_glyph_hints_dump_edges( hints
);
1428 af_glyph_hints_save( hints
, outline
);
1435 /*************************************************************************/
1436 /*************************************************************************/
1438 /***** C J K S C R I P T C L A S S *****/
1440 /*************************************************************************/
1441 /*************************************************************************/
1444 static const AF_Script_UniRangeRec af_cjk_uniranges
[] =
1447 AF_UNIRANGE_REC( 0x0100UL
, 0xFFFFUL
), /* why this? */
1449 AF_UNIRANGE_REC( 0x2E80UL
, 0x2EFFUL
), /* CJK Radicals Supplement */
1450 AF_UNIRANGE_REC( 0x2F00UL
, 0x2FDFUL
), /* Kangxi Radicals */
1451 AF_UNIRANGE_REC( 0x3000UL
, 0x303FUL
), /* CJK Symbols and Punctuation */
1452 AF_UNIRANGE_REC( 0x3040UL
, 0x309FUL
), /* Hiragana */
1453 AF_UNIRANGE_REC( 0x30A0UL
, 0x30FFUL
), /* Katakana */
1454 AF_UNIRANGE_REC( 0x3100UL
, 0x312FUL
), /* Bopomofo */
1455 AF_UNIRANGE_REC( 0x3130UL
, 0x318FUL
), /* Hangul Compatibility Jamo */
1456 AF_UNIRANGE_REC( 0x31A0UL
, 0x31BFUL
), /* Bopomofo Extended */
1457 AF_UNIRANGE_REC( 0x31C0UL
, 0x31EFUL
), /* CJK Strokes */
1458 AF_UNIRANGE_REC( 0x31F0UL
, 0x31FFUL
), /* Katakana Phonetic Extensions */
1459 AF_UNIRANGE_REC( 0x3200UL
, 0x32FFUL
), /* Enclosed CJK Letters and Months */
1460 AF_UNIRANGE_REC( 0x3300UL
, 0x33FFUL
), /* CJK Compatibility */
1461 AF_UNIRANGE_REC( 0x3400UL
, 0x4DBFUL
), /* CJK Unified Ideographs Extension A */
1462 AF_UNIRANGE_REC( 0x4DC0UL
, 0x4DFFUL
), /* Yijing Hexagram Symbols */
1463 AF_UNIRANGE_REC( 0x4E00UL
, 0x9FFFUL
), /* CJK Unified Ideographs */
1464 AF_UNIRANGE_REC( 0xF900UL
, 0xFAFFUL
), /* CJK Compatibility Ideographs */
1465 AF_UNIRANGE_REC( 0xFE30UL
, 0xFE4FUL
), /* CJK Compatibility Forms */
1466 AF_UNIRANGE_REC( 0xFF00UL
, 0xFFEFUL
), /* Halfwidth and Fullwidth Forms */
1467 AF_UNIRANGE_REC( 0x20000UL
, 0x2A6DFUL
), /* CJK Unified Ideographs Extension B */
1468 AF_UNIRANGE_REC( 0x2F800UL
, 0x2FA1FUL
), /* CJK Compatibility Ideographs Supplement */
1469 AF_UNIRANGE_REC( 0UL, 0UL )
1473 AF_DEFINE_SCRIPT_CLASS(af_cjk_script_class
,
1477 sizeof( AF_LatinMetricsRec
),
1479 (AF_Script_InitMetricsFunc
) af_cjk_metrics_init
,
1480 (AF_Script_ScaleMetricsFunc
)af_cjk_metrics_scale
,
1481 (AF_Script_DoneMetricsFunc
) NULL
,
1483 (AF_Script_InitHintsFunc
) af_cjk_hints_init
,
1484 (AF_Script_ApplyHintsFunc
) af_cjk_hints_apply
1487 #else /* !AF_CONFIG_OPTION_CJK */
1489 static const AF_Script_UniRangeRec af_cjk_uniranges
[] =
1491 AF_UNIRANGE_REC( 0UL, 0UL )
1495 AF_DEFINE_SCRIPT_CLASS(af_cjk_script_class
,
1499 sizeof( AF_LatinMetricsRec
),
1501 (AF_Script_InitMetricsFunc
) NULL
,
1502 (AF_Script_ScaleMetricsFunc
)NULL
,
1503 (AF_Script_DoneMetricsFunc
) NULL
,
1505 (AF_Script_InitHintsFunc
) NULL
,
1506 (AF_Script_ApplyHintsFunc
) NULL
1509 #endif /* !AF_CONFIG_OPTION_CJK */