1 /***************************************************************************/
5 /* Auto-fitter hinting routines for CJK script (body). */
7 /* Copyright 2006, 2007 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 /*************************************************************************/
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
) )
62 /* latin's version would suffice */
63 af_latin_metrics_init_widths( metrics
, face
, 0x7530 );
65 FT_Set_Charmap( face
, oldmap
);
72 af_cjk_metrics_scale_dim( AF_LatinMetrics metrics
,
79 axis
= &metrics
->axis
[dim
];
81 if ( dim
== AF_DIMENSION_HORZ
)
83 axis
->scale
= scaler
->x_scale
;
84 axis
->delta
= scaler
->x_delta
;
88 axis
->scale
= scaler
->y_scale
;
89 axis
->delta
= scaler
->y_delta
;
95 af_cjk_metrics_scale( AF_LatinMetrics metrics
,
98 metrics
->root
.scaler
= *scaler
;
100 af_cjk_metrics_scale_dim( metrics
, scaler
, AF_DIMENSION_HORZ
);
101 af_cjk_metrics_scale_dim( metrics
, scaler
, AF_DIMENSION_VERT
);
105 /*************************************************************************/
106 /*************************************************************************/
108 /***** C J K G L Y P H A N A L Y S I S *****/
110 /*************************************************************************/
111 /*************************************************************************/
114 af_cjk_hints_compute_segments( AF_GlyphHints hints
,
117 AF_AxisHints axis
= &hints
->axis
[dim
];
118 AF_Segment segments
= axis
->segments
;
119 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
124 error
= af_latin_hints_compute_segments( hints
, dim
);
128 /* a segment is round if it doesn't have successive */
129 /* on-curve points. */
130 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
132 AF_Point pt
= seg
->first
;
133 AF_Point last
= seg
->last
;
134 AF_Flags f0
= (AF_Flags
)(pt
->flags
& AF_FLAG_CONTROL
);
138 seg
->flags
&= ~AF_EDGE_ROUND
;
140 for ( ; pt
!= last
; f0
= f1
)
143 f1
= (AF_Flags
)(pt
->flags
& AF_FLAG_CONTROL
);
149 seg
->flags
|= AF_EDGE_ROUND
;
158 af_cjk_hints_link_segments( AF_GlyphHints hints
,
161 AF_AxisHints axis
= &hints
->axis
[dim
];
162 AF_Segment segments
= axis
->segments
;
163 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
164 AF_Direction major_dir
= axis
->major_dir
;
165 AF_Segment seg1
, seg2
;
166 FT_Pos len_threshold
;
167 FT_Pos dist_threshold
;
170 len_threshold
= AF_LATIN_CONSTANT( hints
->metrics
, 8 );
172 dist_threshold
= ( dim
== AF_DIMENSION_HORZ
) ? hints
->x_scale
174 dist_threshold
= FT_DivFix( 64 * 3, dist_threshold
);
176 /* now compare each segment to the others */
177 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
179 /* the fake segments are for metrics hinting only */
180 if ( seg1
->first
== seg1
->last
)
183 if ( seg1
->dir
!= major_dir
)
186 for ( seg2
= segments
; seg2
< segment_limit
; seg2
++ )
187 if ( seg2
!= seg1
&& seg1
->dir
+ seg2
->dir
== 0 )
189 FT_Pos dist
= seg2
->pos
- seg1
->pos
;
196 FT_Pos min
= seg1
->min_coord
;
197 FT_Pos max
= seg1
->max_coord
;
201 if ( min
< seg2
->min_coord
)
202 min
= seg2
->min_coord
;
204 if ( max
> seg2
->max_coord
)
205 max
= seg2
->max_coord
;
208 if ( len
>= len_threshold
)
210 if ( dist
* 8 < seg1
->score
* 9 &&
211 ( dist
* 8 < seg1
->score
* 7 || seg1
->len
< len
) )
218 if ( dist
* 8 < seg2
->score
* 9 &&
219 ( dist
* 8 < seg2
->score
* 7 || seg2
->len
< len
) )
231 * now compute the `serif' segments
233 * In Hanzi, some strokes are wider on one or both of the ends.
234 * We either identify the stems on the ends as serifs or remove
235 * the linkage, depending on the length of the stems.
240 AF_Segment link1
, link2
;
243 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
246 if ( !link1
|| link1
->link
!= seg1
|| link1
->pos
<= seg1
->pos
)
249 if ( seg1
->score
>= dist_threshold
)
252 for ( seg2
= segments
; seg2
< segment_limit
; seg2
++ )
254 if ( seg2
->pos
> seg1
->pos
|| seg1
== seg2
)
258 if ( !link2
|| link2
->link
!= seg2
|| link2
->pos
< link1
->pos
)
261 if ( seg1
->pos
== seg2
->pos
&& link1
->pos
== link2
->pos
)
264 if ( seg2
->score
<= seg1
->score
|| seg1
->score
* 4 <= seg2
->score
)
267 /* seg2 < seg1 < link1 < link2 */
269 if ( seg1
->len
>= seg2
->len
* 3 )
274 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
276 AF_Segment link
= seg
->link
;
284 else if ( link
== link2
)
293 seg1
->link
= link1
->link
= 0;
301 for ( seg1
= segments
; seg1
< segment_limit
; seg1
++ )
308 if ( seg2
->link
!= seg1
)
312 if ( seg2
->score
< dist_threshold
|| seg1
->score
< seg2
->score
* 4 )
313 seg1
->serif
= seg2
->link
;
323 af_cjk_hints_compute_edges( AF_GlyphHints hints
,
326 AF_AxisHints axis
= &hints
->axis
[dim
];
327 FT_Error error
= AF_Err_Ok
;
328 FT_Memory memory
= hints
->memory
;
329 AF_LatinAxis laxis
= &((AF_LatinMetrics
)hints
->metrics
)->axis
[dim
];
331 AF_Segment segments
= axis
->segments
;
332 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
336 FT_Pos edge_distance_threshold
;
341 scale
= ( dim
== AF_DIMENSION_HORZ
) ? hints
->x_scale
344 /*********************************************************************/
346 /* We begin by generating a sorted table of edges for the current */
347 /* direction. To do so, we simply scan each segment and try to find */
348 /* an edge in our table that corresponds to its position. */
350 /* If no edge is found, we create and insert a new edge in the */
351 /* sorted table. Otherwise, we simply add the segment to the edge's */
352 /* list which is then processed in the second step to compute the */
353 /* edge's properties. */
355 /* Note that the edges table is sorted along the segment/edge */
358 /*********************************************************************/
360 edge_distance_threshold
= FT_MulFix( laxis
->edge_distance_threshold
,
362 if ( edge_distance_threshold
> 64 / 4 )
363 edge_distance_threshold
= FT_DivFix( 64 / 4, scale
);
365 edge_distance_threshold
= laxis
->edge_distance_threshold
;
367 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
370 FT_Pos best
= 0xFFFFU
;
374 /* look for an edge corresponding to the segment */
375 for ( ee
= 0; ee
< axis
->num_edges
; ee
++ )
377 AF_Edge edge
= axis
->edges
+ ee
;
381 if ( edge
->dir
!= seg
->dir
)
384 dist
= seg
->pos
- edge
->fpos
;
388 if ( dist
< edge_distance_threshold
&& dist
< best
)
390 AF_Segment link
= seg
->link
;
393 /* check whether all linked segments of the candidate edge */
394 /* can make a single edge. */
397 AF_Segment seg1
= edge
->first
;
407 dist2
= AF_SEGMENT_DIST( link
, link1
);
408 if ( dist2
>= edge_distance_threshold
)
412 } while ( ( seg1
= seg1
->edge_next
) != edge
->first
);
414 if ( dist2
>= edge_distance_threshold
)
428 /* insert a new edge in the list and */
429 /* sort according to the position */
430 error
= af_axis_hints_new_edge( axis
, seg
->pos
, seg
->dir
, memory
, &edge
);
434 /* add the segment to the new edge's list */
439 edge
->fpos
= seg
->pos
;
440 edge
->opos
= edge
->pos
= FT_MulFix( seg
->pos
, scale
);
441 seg
->edge_next
= seg
;
442 edge
->dir
= seg
->dir
;
446 /* if an edge was found, simply add the segment to the edge's */
448 seg
->edge_next
= found
->first
;
449 found
->last
->edge_next
= seg
;
454 /*********************************************************************/
456 /* Good, we now compute each edge's properties according to segments */
457 /* found on its position. Basically, these are as follows. */
459 /* - edge's main direction */
460 /* - stem edge, serif edge or both (which defaults to stem then) */
461 /* - rounded edge, straight or both (which defaults to straight) */
462 /* - link for edge */
464 /*********************************************************************/
466 /* first of all, set the `edge' field in each segment -- this is */
467 /* required in order to compute edge links */
469 /* Note that removing this loop and setting the `edge' field of each */
470 /* segment directly in the code above slows down execution speed for */
471 /* some reasons on platforms like the Sun. */
474 AF_Edge edges
= axis
->edges
;
475 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
479 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
486 seg
= seg
->edge_next
;
488 } while ( seg
!= edge
->first
);
491 /* now compute each edge properties */
492 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
494 FT_Int is_round
= 0; /* does it contain round segments? */
495 FT_Int is_straight
= 0; /* does it contain straight segments? */
505 /* check for roundness of segment */
506 if ( seg
->flags
& AF_EDGE_ROUND
)
511 /* check for links -- if seg->serif is set, then seg->link must */
513 is_serif
= (FT_Bool
)( seg
->serif
&& seg
->serif
->edge
!= edge
);
515 if ( seg
->link
|| is_serif
)
536 edge_delta
= edge
->fpos
- edge2
->fpos
;
537 if ( edge_delta
< 0 )
538 edge_delta
= -edge_delta
;
540 seg_delta
= AF_SEGMENT_DIST( seg
, seg2
);
542 if ( seg_delta
< edge_delta
)
551 edge2
->flags
|= AF_EDGE_SERIF
;
557 seg
= seg
->edge_next
;
559 } while ( seg
!= edge
->first
);
561 /* set the round/straight flags */
562 edge
->flags
= AF_EDGE_NORMAL
;
564 if ( is_round
> 0 && is_round
>= is_straight
)
565 edge
->flags
|= AF_EDGE_ROUND
;
567 /* get rid of serifs if link is set */
568 /* XXX: This gets rid of many unpleasant artefacts! */
569 /* Example: the `c' in cour.pfa at size 13 */
571 if ( edge
->serif
&& edge
->link
)
582 af_cjk_hints_detect_features( AF_GlyphHints hints
,
588 error
= af_cjk_hints_compute_segments( hints
, dim
);
591 af_cjk_hints_link_segments( hints
, dim
);
593 error
= af_cjk_hints_compute_edges( hints
, dim
);
600 af_cjk_hints_init( AF_GlyphHints hints
,
601 AF_LatinMetrics metrics
)
604 FT_UInt32 scaler_flags
, other_flags
;
607 af_glyph_hints_rescale( hints
, (AF_ScriptMetrics
)metrics
);
610 * correct x_scale and y_scale when needed, since they may have
611 * been modified af_cjk_scale_dim above
613 hints
->x_scale
= metrics
->axis
[AF_DIMENSION_HORZ
].scale
;
614 hints
->x_delta
= metrics
->axis
[AF_DIMENSION_HORZ
].delta
;
615 hints
->y_scale
= metrics
->axis
[AF_DIMENSION_VERT
].scale
;
616 hints
->y_delta
= metrics
->axis
[AF_DIMENSION_VERT
].delta
;
618 /* compute flags depending on render mode, etc. */
619 mode
= metrics
->root
.scaler
.render_mode
;
622 if ( mode
== FT_RENDER_MODE_LCD
|| mode
== FT_RENDER_MODE_LCD_V
)
623 metrics
->root
.scaler
.render_mode
= mode
= FT_RENDER_MODE_NORMAL
;
626 scaler_flags
= hints
->scaler_flags
;
630 * We snap the width of vertical stems for the monochrome and
631 * horizontal LCD rendering targets only.
633 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD
)
634 other_flags
|= AF_LATIN_HINTS_HORZ_SNAP
;
637 * We snap the width of horizontal stems for the monochrome and
638 * vertical LCD rendering targets only.
640 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD_V
)
641 other_flags
|= AF_LATIN_HINTS_VERT_SNAP
;
644 * We adjust stems to full pixels only if we don't use the `light' mode.
646 if ( mode
!= FT_RENDER_MODE_LIGHT
)
647 other_flags
|= AF_LATIN_HINTS_STEM_ADJUST
;
649 if ( mode
== FT_RENDER_MODE_MONO
)
650 other_flags
|= AF_LATIN_HINTS_MONO
;
652 scaler_flags
|= AF_SCALER_FLAG_NO_ADVANCE
;
654 hints
->scaler_flags
= scaler_flags
;
655 hints
->other_flags
= other_flags
;
661 /*************************************************************************/
662 /*************************************************************************/
664 /***** C J K G L Y P H G R I D - F I T T I N G *****/
666 /*************************************************************************/
667 /*************************************************************************/
669 /* snap a given width in scaled coordinates to one of the */
670 /* current standard widths */
673 af_cjk_snap_width( AF_Width widths
,
678 FT_Pos best
= 64 + 32 + 2;
679 FT_Pos reference
= width
;
683 for ( n
= 0; n
< count
; n
++ )
700 scaled
= FT_PIX_ROUND( reference
);
702 if ( width
>= reference
)
704 if ( width
< scaled
+ 48 )
709 if ( width
> scaled
- 48 )
717 /* compute the snapped width of a given stem */
720 af_cjk_compute_stem_width( AF_GlyphHints hints
,
723 AF_Edge_Flags base_flags
,
724 AF_Edge_Flags stem_flags
)
726 AF_LatinMetrics metrics
= (AF_LatinMetrics
) hints
->metrics
;
727 AF_LatinAxis axis
= & metrics
->axis
[dim
];
730 FT_Int vertical
= ( dim
== AF_DIMENSION_VERT
);
732 FT_UNUSED( base_flags
);
733 FT_UNUSED( stem_flags
);
736 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
745 if ( ( vertical
&& !AF_LATIN_HINTS_DO_VERT_SNAP( hints
) ) ||
746 ( !vertical
&& !AF_LATIN_HINTS_DO_HORZ_SNAP( hints
) ) )
748 /* smooth hinting process: very lightly quantize the stem width */
750 if ( axis
->width_count
> 0 )
752 if ( FT_ABS( dist
- axis
->widths
[0].cur
) < 40 )
754 dist
= axis
->widths
[0].cur
;
763 dist
+= ( 54 - dist
) / 2 ;
764 else if ( dist
< 3 * 64 )
774 else if ( delta
< 22 )
776 else if ( delta
< 42 )
778 else if ( delta
< 54 )
786 /* strong hinting process: snap the stem width to integer pixels */
788 dist
= af_cjk_snap_width( axis
->widths
, axis
->width_count
, dist
);
792 /* in the case of vertical hinting, always round */
793 /* the stem heights to integer pixels */
796 dist
= ( dist
+ 16 ) & ~63;
802 if ( AF_LATIN_HINTS_DO_MONO( hints
) )
804 /* monochrome horizontal hinting: snap widths to integer pixels */
805 /* with a different threshold */
810 dist
= ( dist
+ 32 ) & ~63;
814 /* for horizontal anti-aliased hinting, we adopt a more subtle */
815 /* approach: we strengthen small stems, round stems whose size */
816 /* is between 1 and 2 pixels to an integer, otherwise nothing */
819 dist
= ( dist
+ 64 ) >> 1;
821 else if ( dist
< 128 )
822 dist
= ( dist
+ 22 ) & ~63;
824 /* round otherwise to prevent color fringes in LCD mode */
825 dist
= ( dist
+ 32 ) & ~63;
838 /* align one stem edge relative to the previous stem edge */
841 af_cjk_align_linked_edge( AF_GlyphHints hints
,
846 FT_Pos dist
= stem_edge
->opos
- base_edge
->opos
;
848 FT_Pos fitted_width
= af_cjk_compute_stem_width(
850 (AF_Edge_Flags
)base_edge
->flags
,
851 (AF_Edge_Flags
)stem_edge
->flags
);
854 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
859 af_cjk_align_serif_edge( AF_GlyphHints hints
,
865 serif
->pos
= base
->pos
+ ( serif
->opos
- base
->opos
);
869 /*************************************************************************/
870 /*************************************************************************/
871 /*************************************************************************/
873 /**** E D G E H I N T I N G ****/
875 /*************************************************************************/
876 /*************************************************************************/
877 /*************************************************************************/
880 #define AF_LIGHT_MODE_MAX_HORZ_GAP 9
881 #define AF_LIGHT_MODE_MAX_VERT_GAP 15
882 #define AF_LIGHT_MODE_MAX_DELTA_ABS 14
886 af_hint_normal_stem( AF_GlyphHints hints
,
892 FT_Pos org_len
, cur_len
, org_center
;
893 FT_Pos cur_pos1
, cur_pos2
;
894 FT_Pos d_off1
, u_off1
, d_off2
, u_off2
, delta
;
896 FT_Pos threshold
= 64;
899 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
901 if ( ( edge
->flags
& AF_EDGE_ROUND
) &&
902 ( edge2
->flags
& AF_EDGE_ROUND
) )
904 if ( dim
== AF_DIMENSION_VERT
)
905 threshold
= 64 - AF_LIGHT_MODE_MAX_HORZ_GAP
;
907 threshold
= 64 - AF_LIGHT_MODE_MAX_VERT_GAP
;
911 if ( dim
== AF_DIMENSION_VERT
)
912 threshold
= 64 - AF_LIGHT_MODE_MAX_HORZ_GAP
/ 3;
914 threshold
= 64 - AF_LIGHT_MODE_MAX_VERT_GAP
/ 3;
918 org_len
= edge2
->opos
- edge
->opos
;
919 cur_len
= af_cjk_compute_stem_width( hints
, dim
, org_len
,
920 (AF_Edge_Flags
)edge
->flags
,
921 (AF_Edge_Flags
)edge2
->flags
);
923 org_center
= ( edge
->opos
+ edge2
->opos
) / 2 + anchor
;
924 cur_pos1
= org_center
- cur_len
/ 2;
925 cur_pos2
= cur_pos1
+ cur_len
;
926 d_off1
= cur_pos1
- FT_PIX_FLOOR( cur_pos1
);
927 d_off2
= cur_pos2
- FT_PIX_FLOOR( cur_pos2
);
928 u_off1
= 64 - d_off1
;
929 u_off2
= 64 - d_off2
;
933 if ( d_off1
== 0 || d_off2
== 0 )
936 if ( cur_len
<= threshold
)
938 if ( d_off2
< cur_len
)
940 if ( u_off1
<= d_off2
)
949 if ( threshold
< 64 )
951 if ( d_off1
>= threshold
|| u_off1
>= threshold
||
952 d_off2
>= threshold
|| u_off2
>= threshold
)
956 offset
= cur_len
% 64;
960 if ( u_off1
<= offset
|| d_off2
<= offset
)
964 offset
= 64 - threshold
;
966 d_off1
= threshold
- u_off1
;
967 u_off1
= u_off1
- offset
;
968 u_off2
= threshold
- d_off2
;
969 d_off2
= d_off2
- offset
;
971 if ( d_off1
<= u_off1
)
974 if ( d_off2
<= u_off2
)
977 if ( FT_ABS( u_off1
) <= FT_ABS( u_off2
) )
985 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
987 if ( delta
> AF_LIGHT_MODE_MAX_DELTA_ABS
)
988 delta
= AF_LIGHT_MODE_MAX_DELTA_ABS
;
989 else if ( delta
< -AF_LIGHT_MODE_MAX_DELTA_ABS
)
990 delta
= -AF_LIGHT_MODE_MAX_DELTA_ABS
;
996 if ( edge
->opos
< edge2
->opos
)
998 edge
->pos
= cur_pos1
;
999 edge2
->pos
= cur_pos1
+ cur_len
;
1003 edge
->pos
= cur_pos1
+ cur_len
;
1004 edge2
->pos
= cur_pos1
;
1012 af_cjk_hint_edges( AF_GlyphHints hints
,
1015 AF_AxisHints axis
= &hints
->axis
[dim
];
1016 AF_Edge edges
= axis
->edges
;
1017 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1025 /* now we align all stem edges. */
1026 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1031 if ( edge
->flags
& AF_EDGE_DONE
)
1034 /* skip all non-stem edges */
1042 /* now align the stem */
1046 af_cjk_align_linked_edge( hints
, dim
, edge2
, edge
);
1047 edge
->flags
|= AF_EDGE_DONE
;
1051 if ( dim
!= AF_DIMENSION_VERT
&& !anchor
)
1057 AF_Edge left
= edge
;
1058 AF_Edge right
= edge_limit
- 1;
1059 AF_EdgeRec left1
, left2
, right1
, right2
;
1060 FT_Pos target
, center1
, center2
;
1061 FT_Pos delta1
, delta2
, d1
, d2
;
1064 while ( right
> left
&& !right
->link
)
1068 left2
= *left
->link
;
1069 right1
= *right
->link
;
1072 delta
= ( ( ( hinter
->pp2
.x
+ 32 ) & -64 ) - hinter
->pp2
.x
) / 2;
1073 target
= left
->opos
+ ( right
->opos
- left
->opos
) / 2 + delta
- 16;
1076 delta1
+= af_hint_normal_stem( hints
, left
, left
->link
,
1079 if ( left
->link
!= right
)
1080 af_hint_normal_stem( hints
, right
->link
, right
, delta1
, 0 );
1082 center1
= left
->pos
+ ( right
->pos
- left
->pos
) / 2;
1084 if ( center1
>= target
)
1085 delta2
= delta
- 32;
1087 delta2
= delta
+ 32;
1089 delta2
+= af_hint_normal_stem( hints
, &left1
, &left2
, delta2
, 0 );
1091 if ( delta1
!= delta2
)
1093 if ( left
->link
!= right
)
1094 af_hint_normal_stem( hints
, &right1
, &right2
, delta2
, 0 );
1096 center2
= left1
.pos
+ ( right2
.pos
- left1
.pos
) / 2;
1098 d1
= center1
- target
;
1099 d2
= center2
- target
;
1101 if ( FT_ABS( d2
) < FT_ABS( d1
) )
1103 left
->pos
= left1
.pos
;
1104 left
->link
->pos
= left2
.pos
;
1106 if ( left
->link
!= right
)
1108 right
->link
->pos
= right1
.pos
;
1109 right
->pos
= right2
.pos
;
1117 right
->link
->flags
|= AF_EDGE_DONE
;
1118 right
->flags
|= AF_EDGE_DONE
;
1124 delta
= af_hint_normal_stem( hints
, edge
, edge2
, 0,
1125 AF_DIMENSION_HORZ
);
1128 af_hint_normal_stem( hints
, edge
, edge2
, delta
, dim
);
1131 printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
1132 edge
- edges
, edge2
- edges
,
1133 ( edge
->pos
- edge
->opos
) / 64.0,
1134 ( edge2
->pos
- edge2
->opos
) / 64.0 );
1138 edge
->flags
|= AF_EDGE_DONE
;
1139 edge2
->flags
|= AF_EDGE_DONE
;
1142 /* make sure that lowercase m's maintain their symmetry */
1144 /* In general, lowercase m's have six vertical edges if they are sans */
1145 /* serif, or twelve if they are with serifs. This implementation is */
1146 /* based on that assumption, and seems to work very well with most */
1147 /* faces. However, if for a certain face this assumption is not */
1148 /* true, the m is just rendered like before. In addition, any stem */
1149 /* correction will only be applied to symmetrical glyphs (even if the */
1150 /* glyph is not an m), so the potential for unwanted distortion is */
1151 /* relatively low. */
1153 /* We don't handle horizontal edges since we can't easily assure that */
1154 /* the third (lowest) stem aligns with the base line; it might end up */
1155 /* one pixel higher or lower. */
1157 n_edges
= edge_limit
- edges
;
1158 if ( dim
== AF_DIMENSION_HORZ
&& ( n_edges
== 6 || n_edges
== 12 ) )
1160 AF_Edge edge1
, edge2
, edge3
;
1161 FT_Pos dist1
, dist2
, span
;
1177 dist1
= edge2
->opos
- edge1
->opos
;
1178 dist2
= edge3
->opos
- edge2
->opos
;
1180 span
= dist1
- dist2
;
1184 if ( edge1
->link
== edge1
+ 1 &&
1185 edge2
->link
== edge2
+ 1 &&
1186 edge3
->link
== edge3
+ 1 && span
< 8 )
1188 delta
= edge3
->pos
- ( 2 * edge2
->pos
- edge1
->pos
);
1189 edge3
->pos
-= delta
;
1191 edge3
->link
->pos
-= delta
;
1193 /* move the serifs along with the stem */
1194 if ( n_edges
== 12 )
1196 ( edges
+ 8 )->pos
-= delta
;
1197 ( edges
+ 11 )->pos
-= delta
;
1200 edge3
->flags
|= AF_EDGE_DONE
;
1202 edge3
->link
->flags
|= AF_EDGE_DONE
;
1210 * now hint the remaining edges (serifs and single) in order
1211 * to complete our processing
1213 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1215 if ( edge
->flags
& AF_EDGE_DONE
)
1220 af_cjk_align_serif_edge( hints
, edge
->serif
, edge
);
1221 edge
->flags
|= AF_EDGE_DONE
;
1229 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1231 AF_Edge before
, after
;
1234 if ( edge
->flags
& AF_EDGE_DONE
)
1237 before
= after
= edge
;
1239 while ( --before
>= edges
)
1240 if ( before
->flags
& AF_EDGE_DONE
)
1243 while ( ++after
< edge_limit
)
1244 if ( after
->flags
& AF_EDGE_DONE
)
1247 if ( before
>= edges
|| after
< edge_limit
)
1249 if ( before
< edges
)
1250 af_cjk_align_serif_edge( hints
, after
, edge
);
1251 else if ( after
>= edge_limit
)
1252 af_cjk_align_serif_edge( hints
, before
, edge
);
1254 edge
->pos
= before
->pos
+
1255 FT_MulDiv( edge
->fpos
- before
->fpos
,
1256 after
->pos
- before
->pos
,
1257 after
->fpos
- before
->fpos
);
1264 af_cjk_align_edge_points( AF_GlyphHints hints
,
1267 AF_AxisHints axis
= & hints
->axis
[dim
];
1268 AF_Edge edges
= axis
->edges
;
1269 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1274 snapping
= FT_BOOL( ( dim
== AF_DIMENSION_HORZ
&&
1275 AF_LATIN_HINTS_DO_HORZ_SNAP( hints
) ) ||
1276 ( dim
== AF_DIMENSION_VERT
&&
1277 AF_LATIN_HINTS_DO_VERT_SNAP( hints
) ) );
1279 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1281 /* move the points of each segment */
1282 /* in each edge to the edge's position */
1283 AF_Segment seg
= edge
->first
;
1290 AF_Point point
= seg
->first
;
1295 if ( dim
== AF_DIMENSION_HORZ
)
1297 point
->x
= edge
->pos
;
1298 point
->flags
|= AF_FLAG_TOUCH_X
;
1302 point
->y
= edge
->pos
;
1303 point
->flags
|= AF_FLAG_TOUCH_Y
;
1306 if ( point
== seg
->last
)
1309 point
= point
->next
;
1312 seg
= seg
->edge_next
;
1314 } while ( seg
!= edge
->first
);
1318 FT_Pos delta
= edge
->pos
- edge
->opos
;
1323 AF_Point point
= seg
->first
;
1328 if ( dim
== AF_DIMENSION_HORZ
)
1331 point
->flags
|= AF_FLAG_TOUCH_X
;
1336 point
->flags
|= AF_FLAG_TOUCH_Y
;
1339 if ( point
== seg
->last
)
1342 point
= point
->next
;
1345 seg
= seg
->edge_next
;
1347 } while ( seg
!= edge
->first
);
1354 af_cjk_hints_apply( AF_GlyphHints hints
,
1355 FT_Outline
* outline
,
1356 AF_LatinMetrics metrics
)
1361 FT_UNUSED( metrics
);
1364 error
= af_glyph_hints_reload( hints
, outline
, 0 );
1368 /* analyze glyph outline */
1369 if ( AF_HINTS_DO_HORIZONTAL( hints
) )
1371 error
= af_cjk_hints_detect_features( hints
, AF_DIMENSION_HORZ
);
1376 if ( AF_HINTS_DO_VERTICAL( hints
) )
1378 error
= af_cjk_hints_detect_features( hints
, AF_DIMENSION_VERT
);
1383 /* grid-fit the outline */
1384 for ( dim
= 0; dim
< AF_DIMENSION_MAX
; dim
++ )
1386 if ( ( dim
== AF_DIMENSION_HORZ
&& AF_HINTS_DO_HORIZONTAL( hints
) ) ||
1387 ( dim
== AF_DIMENSION_VERT
&& AF_HINTS_DO_VERTICAL( hints
) ) )
1390 #ifdef AF_USE_WARPER
1391 if ( dim
== AF_DIMENSION_HORZ
&&
1392 metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_NORMAL
)
1394 AF_WarperRec warper
;
1399 af_warper_compute( &warper
, hints
, dim
, &scale
, &delta
);
1400 af_glyph_hints_scale_dim( hints
, dim
, scale
, delta
);
1403 #endif /* AF_USE_WARPER */
1405 af_cjk_hint_edges( hints
, (AF_Dimension
)dim
);
1406 af_cjk_align_edge_points( hints
, (AF_Dimension
)dim
);
1407 af_glyph_hints_align_strong_points( hints
, (AF_Dimension
)dim
);
1408 af_glyph_hints_align_weak_points( hints
, (AF_Dimension
)dim
);
1413 af_glyph_hints_dump_points( hints
);
1414 af_glyph_hints_dump_segments( hints
);
1415 af_glyph_hints_dump_edges( hints
);
1418 af_glyph_hints_save( hints
, outline
);
1425 /*************************************************************************/
1426 /*************************************************************************/
1428 /***** C J K S C R I P T C L A S S *****/
1430 /*************************************************************************/
1431 /*************************************************************************/
1434 static const AF_Script_UniRangeRec af_cjk_uniranges
[] =
1437 { 0x0100, 0xFFFF }, /* why this? */
1439 { 0x2E80, 0x2EFF }, /* CJK Radicals Supplement */
1440 { 0x2F00, 0x2FDF }, /* Kangxi Radicals */
1441 { 0x3000, 0x303F }, /* CJK Symbols and Punctuation */
1442 { 0x3040, 0x309F }, /* Hiragana */
1443 { 0x30A0, 0x30FF }, /* Katakana */
1444 { 0x3100, 0x312F }, /* Bopomofo */
1445 { 0x3130, 0x318F }, /* Hangul Compatibility Jamo */
1446 { 0x31A0, 0x31BF }, /* Bopomofo Extended */
1447 { 0x31C0, 0x31EF }, /* CJK Strokes */
1448 { 0x31F0, 0x31FF }, /* Katakana Phonetic Extensions */
1449 { 0x3200, 0x32FF }, /* Enclosed CJK Letters and Months */
1450 { 0x3300, 0x33FF }, /* CJK Compatibility */
1451 { 0x3400, 0x4DBF }, /* CJK Unified Ideographs Extension A */
1452 { 0x4DC0, 0x4DFF }, /* Yijing Hexagram Symbols */
1453 { 0x4E00, 0x9FFF }, /* CJK Unified Ideographs */
1454 { 0xF900, 0xFAFF }, /* CJK Compatibility Ideographs */
1455 { 0xFE30, 0xFE4F }, /* CJK Compatibility Forms */
1456 { 0xFF00, 0xFFEF }, /* Halfwidth and Fullwidth Forms */
1457 { 0x20000, 0x2A6DF }, /* CJK Unified Ideographs Extension B */
1458 { 0x2F800, 0x2FA1F }, /* CJK Compatibility Ideographs Supplement */
1463 FT_CALLBACK_TABLE_DEF
const AF_ScriptClassRec
1464 af_cjk_script_class
=
1469 sizeof( AF_LatinMetricsRec
),
1471 (AF_Script_InitMetricsFunc
) af_cjk_metrics_init
,
1472 (AF_Script_ScaleMetricsFunc
)af_cjk_metrics_scale
,
1473 (AF_Script_DoneMetricsFunc
) NULL
,
1475 (AF_Script_InitHintsFunc
) af_cjk_hints_init
,
1476 (AF_Script_ApplyHintsFunc
) af_cjk_hints_apply
1479 #else /* !AF_CONFIG_OPTION_CJK */
1481 static const AF_Script_UniRangeRec af_cjk_uniranges
[] =
1487 FT_CALLBACK_TABLE_DEF
const AF_ScriptClassRec
1488 af_cjk_script_class
=
1493 sizeof( AF_LatinMetricsRec
),
1495 (AF_Script_InitMetricsFunc
) NULL
,
1496 (AF_Script_ScaleMetricsFunc
)NULL
,
1497 (AF_Script_DoneMetricsFunc
) NULL
,
1499 (AF_Script_InitHintsFunc
) NULL
,
1500 (AF_Script_ApplyHintsFunc
) NULL
1503 #endif /* !AF_CONFIG_OPTION_CJK */