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 /*************************************************************************/
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
) )
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
,
431 (AF_Direction
)seg
->dir
,
436 /* add the segment to the new edge's list */
441 edge
->fpos
= seg
->pos
;
442 edge
->opos
= edge
->pos
= FT_MulFix( seg
->pos
, scale
);
443 seg
->edge_next
= seg
;
444 edge
->dir
= seg
->dir
;
448 /* if an edge was found, simply add the segment to the edge's */
450 seg
->edge_next
= found
->first
;
451 found
->last
->edge_next
= seg
;
456 /*********************************************************************/
458 /* Good, we now compute each edge's properties according to segments */
459 /* found on its position. Basically, these are as follows. */
461 /* - edge's main direction */
462 /* - stem edge, serif edge or both (which defaults to stem then) */
463 /* - rounded edge, straight or both (which defaults to straight) */
464 /* - link for edge */
466 /*********************************************************************/
468 /* first of all, set the `edge' field in each segment -- this is */
469 /* required in order to compute edge links */
471 /* Note that removing this loop and setting the `edge' field of each */
472 /* segment directly in the code above slows down execution speed for */
473 /* some reasons on platforms like the Sun. */
476 AF_Edge edges
= axis
->edges
;
477 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
481 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
488 seg
= seg
->edge_next
;
490 } while ( seg
!= edge
->first
);
493 /* now compute each edge properties */
494 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
496 FT_Int is_round
= 0; /* does it contain round segments? */
497 FT_Int is_straight
= 0; /* does it contain straight segments? */
507 /* check for roundness of segment */
508 if ( seg
->flags
& AF_EDGE_ROUND
)
513 /* check for links -- if seg->serif is set, then seg->link must */
515 is_serif
= (FT_Bool
)( seg
->serif
&& seg
->serif
->edge
!= edge
);
517 if ( seg
->link
|| is_serif
)
538 edge_delta
= edge
->fpos
- edge2
->fpos
;
539 if ( edge_delta
< 0 )
540 edge_delta
= -edge_delta
;
542 seg_delta
= AF_SEGMENT_DIST( seg
, seg2
);
544 if ( seg_delta
< edge_delta
)
553 edge2
->flags
|= AF_EDGE_SERIF
;
559 seg
= seg
->edge_next
;
561 } while ( seg
!= edge
->first
);
563 /* set the round/straight flags */
564 edge
->flags
= AF_EDGE_NORMAL
;
566 if ( is_round
> 0 && is_round
>= is_straight
)
567 edge
->flags
|= AF_EDGE_ROUND
;
569 /* get rid of serifs if link is set */
570 /* XXX: This gets rid of many unpleasant artefacts! */
571 /* Example: the `c' in cour.pfa at size 13 */
573 if ( edge
->serif
&& edge
->link
)
584 af_cjk_hints_detect_features( AF_GlyphHints hints
,
590 error
= af_cjk_hints_compute_segments( hints
, dim
);
593 af_cjk_hints_link_segments( hints
, dim
);
595 error
= af_cjk_hints_compute_edges( hints
, dim
);
601 FT_LOCAL_DEF( FT_Error
)
602 af_cjk_hints_init( AF_GlyphHints hints
,
603 AF_LatinMetrics metrics
)
606 FT_UInt32 scaler_flags
, other_flags
;
609 af_glyph_hints_rescale( hints
, (AF_ScriptMetrics
)metrics
);
612 * correct x_scale and y_scale when needed, since they may have
613 * been modified af_cjk_scale_dim above
615 hints
->x_scale
= metrics
->axis
[AF_DIMENSION_HORZ
].scale
;
616 hints
->x_delta
= metrics
->axis
[AF_DIMENSION_HORZ
].delta
;
617 hints
->y_scale
= metrics
->axis
[AF_DIMENSION_VERT
].scale
;
618 hints
->y_delta
= metrics
->axis
[AF_DIMENSION_VERT
].delta
;
620 /* compute flags depending on render mode, etc. */
621 mode
= metrics
->root
.scaler
.render_mode
;
624 if ( mode
== FT_RENDER_MODE_LCD
|| mode
== FT_RENDER_MODE_LCD_V
)
625 metrics
->root
.scaler
.render_mode
= mode
= FT_RENDER_MODE_NORMAL
;
628 scaler_flags
= hints
->scaler_flags
;
632 * We snap the width of vertical stems for the monochrome and
633 * horizontal LCD rendering targets only.
635 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD
)
636 other_flags
|= AF_LATIN_HINTS_HORZ_SNAP
;
639 * We snap the width of horizontal stems for the monochrome and
640 * vertical LCD rendering targets only.
642 if ( mode
== FT_RENDER_MODE_MONO
|| mode
== FT_RENDER_MODE_LCD_V
)
643 other_flags
|= AF_LATIN_HINTS_VERT_SNAP
;
646 * We adjust stems to full pixels only if we don't use the `light' mode.
648 if ( mode
!= FT_RENDER_MODE_LIGHT
)
649 other_flags
|= AF_LATIN_HINTS_STEM_ADJUST
;
651 if ( mode
== FT_RENDER_MODE_MONO
)
652 other_flags
|= AF_LATIN_HINTS_MONO
;
654 scaler_flags
|= AF_SCALER_FLAG_NO_ADVANCE
;
656 hints
->scaler_flags
= scaler_flags
;
657 hints
->other_flags
= other_flags
;
663 /*************************************************************************/
664 /*************************************************************************/
666 /***** C J K G L Y P H G R I D - F I T T I N G *****/
668 /*************************************************************************/
669 /*************************************************************************/
671 /* snap a given width in scaled coordinates to one of the */
672 /* current standard widths */
675 af_cjk_snap_width( AF_Width widths
,
680 FT_Pos best
= 64 + 32 + 2;
681 FT_Pos reference
= width
;
685 for ( n
= 0; n
< count
; n
++ )
702 scaled
= FT_PIX_ROUND( reference
);
704 if ( width
>= reference
)
706 if ( width
< scaled
+ 48 )
711 if ( width
> scaled
- 48 )
719 /* compute the snapped width of a given stem */
722 af_cjk_compute_stem_width( AF_GlyphHints hints
,
725 AF_Edge_Flags base_flags
,
726 AF_Edge_Flags stem_flags
)
728 AF_LatinMetrics metrics
= (AF_LatinMetrics
) hints
->metrics
;
729 AF_LatinAxis axis
= & metrics
->axis
[dim
];
732 FT_Int vertical
= ( dim
== AF_DIMENSION_VERT
);
734 FT_UNUSED( base_flags
);
735 FT_UNUSED( stem_flags
);
738 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
747 if ( ( vertical
&& !AF_LATIN_HINTS_DO_VERT_SNAP( hints
) ) ||
748 ( !vertical
&& !AF_LATIN_HINTS_DO_HORZ_SNAP( hints
) ) )
750 /* smooth hinting process: very lightly quantize the stem width */
752 if ( axis
->width_count
> 0 )
754 if ( FT_ABS( dist
- axis
->widths
[0].cur
) < 40 )
756 dist
= axis
->widths
[0].cur
;
765 dist
+= ( 54 - dist
) / 2 ;
766 else if ( dist
< 3 * 64 )
776 else if ( delta
< 22 )
778 else if ( delta
< 42 )
780 else if ( delta
< 54 )
788 /* strong hinting process: snap the stem width to integer pixels */
790 dist
= af_cjk_snap_width( axis
->widths
, axis
->width_count
, dist
);
794 /* in the case of vertical hinting, always round */
795 /* the stem heights to integer pixels */
798 dist
= ( dist
+ 16 ) & ~63;
804 if ( AF_LATIN_HINTS_DO_MONO( hints
) )
806 /* monochrome horizontal hinting: snap widths to integer pixels */
807 /* with a different threshold */
812 dist
= ( dist
+ 32 ) & ~63;
816 /* for horizontal anti-aliased hinting, we adopt a more subtle */
817 /* approach: we strengthen small stems, round stems whose size */
818 /* is between 1 and 2 pixels to an integer, otherwise nothing */
821 dist
= ( dist
+ 64 ) >> 1;
823 else if ( dist
< 128 )
824 dist
= ( dist
+ 22 ) & ~63;
826 /* round otherwise to prevent color fringes in LCD mode */
827 dist
= ( dist
+ 32 ) & ~63;
840 /* align one stem edge relative to the previous stem edge */
843 af_cjk_align_linked_edge( AF_GlyphHints hints
,
848 FT_Pos dist
= stem_edge
->opos
- base_edge
->opos
;
850 FT_Pos fitted_width
= af_cjk_compute_stem_width(
852 (AF_Edge_Flags
)base_edge
->flags
,
853 (AF_Edge_Flags
)stem_edge
->flags
);
856 stem_edge
->pos
= base_edge
->pos
+ fitted_width
;
861 af_cjk_align_serif_edge( AF_GlyphHints hints
,
867 serif
->pos
= base
->pos
+ ( serif
->opos
- base
->opos
);
871 /*************************************************************************/
872 /*************************************************************************/
873 /*************************************************************************/
875 /**** E D G E H I N T I N G ****/
877 /*************************************************************************/
878 /*************************************************************************/
879 /*************************************************************************/
882 #define AF_LIGHT_MODE_MAX_HORZ_GAP 9
883 #define AF_LIGHT_MODE_MAX_VERT_GAP 15
884 #define AF_LIGHT_MODE_MAX_DELTA_ABS 14
888 af_hint_normal_stem( AF_GlyphHints hints
,
894 FT_Pos org_len
, cur_len
, org_center
;
895 FT_Pos cur_pos1
, cur_pos2
;
896 FT_Pos d_off1
, u_off1
, d_off2
, u_off2
, delta
;
898 FT_Pos threshold
= 64;
901 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
903 if ( ( edge
->flags
& AF_EDGE_ROUND
) &&
904 ( edge2
->flags
& AF_EDGE_ROUND
) )
906 if ( dim
== AF_DIMENSION_VERT
)
907 threshold
= 64 - AF_LIGHT_MODE_MAX_HORZ_GAP
;
909 threshold
= 64 - AF_LIGHT_MODE_MAX_VERT_GAP
;
913 if ( dim
== AF_DIMENSION_VERT
)
914 threshold
= 64 - AF_LIGHT_MODE_MAX_HORZ_GAP
/ 3;
916 threshold
= 64 - AF_LIGHT_MODE_MAX_VERT_GAP
/ 3;
920 org_len
= edge2
->opos
- edge
->opos
;
921 cur_len
= af_cjk_compute_stem_width( hints
, dim
, org_len
,
922 (AF_Edge_Flags
)edge
->flags
,
923 (AF_Edge_Flags
)edge2
->flags
);
925 org_center
= ( edge
->opos
+ edge2
->opos
) / 2 + anchor
;
926 cur_pos1
= org_center
- cur_len
/ 2;
927 cur_pos2
= cur_pos1
+ cur_len
;
928 d_off1
= cur_pos1
- FT_PIX_FLOOR( cur_pos1
);
929 d_off2
= cur_pos2
- FT_PIX_FLOOR( cur_pos2
);
930 u_off1
= 64 - d_off1
;
931 u_off2
= 64 - d_off2
;
935 if ( d_off1
== 0 || d_off2
== 0 )
938 if ( cur_len
<= threshold
)
940 if ( d_off2
< cur_len
)
942 if ( u_off1
<= d_off2
)
951 if ( threshold
< 64 )
953 if ( d_off1
>= threshold
|| u_off1
>= threshold
||
954 d_off2
>= threshold
|| u_off2
>= threshold
)
958 offset
= cur_len
% 64;
962 if ( u_off1
<= offset
|| d_off2
<= offset
)
966 offset
= 64 - threshold
;
968 d_off1
= threshold
- u_off1
;
969 u_off1
= u_off1
- offset
;
970 u_off2
= threshold
- d_off2
;
971 d_off2
= d_off2
- offset
;
973 if ( d_off1
<= u_off1
)
976 if ( d_off2
<= u_off2
)
979 if ( FT_ABS( u_off1
) <= FT_ABS( u_off2
) )
987 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints
) )
989 if ( delta
> AF_LIGHT_MODE_MAX_DELTA_ABS
)
990 delta
= AF_LIGHT_MODE_MAX_DELTA_ABS
;
991 else if ( delta
< -AF_LIGHT_MODE_MAX_DELTA_ABS
)
992 delta
= -AF_LIGHT_MODE_MAX_DELTA_ABS
;
998 if ( edge
->opos
< edge2
->opos
)
1000 edge
->pos
= cur_pos1
;
1001 edge2
->pos
= cur_pos1
+ cur_len
;
1005 edge
->pos
= cur_pos1
+ cur_len
;
1006 edge2
->pos
= cur_pos1
;
1014 af_cjk_hint_edges( AF_GlyphHints hints
,
1017 AF_AxisHints axis
= &hints
->axis
[dim
];
1018 AF_Edge edges
= axis
->edges
;
1019 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1027 /* now we align all stem edges. */
1028 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1033 if ( edge
->flags
& AF_EDGE_DONE
)
1036 /* skip all non-stem edges */
1044 /* now align the stem */
1048 af_cjk_align_linked_edge( hints
, dim
, edge2
, edge
);
1049 edge
->flags
|= AF_EDGE_DONE
;
1053 if ( dim
!= AF_DIMENSION_VERT
&& !anchor
)
1059 AF_Edge left
= edge
;
1060 AF_Edge right
= edge_limit
- 1;
1061 AF_EdgeRec left1
, left2
, right1
, right2
;
1062 FT_Pos target
, center1
, center2
;
1063 FT_Pos delta1
, delta2
, d1
, d2
;
1066 while ( right
> left
&& !right
->link
)
1070 left2
= *left
->link
;
1071 right1
= *right
->link
;
1074 delta
= ( ( ( hinter
->pp2
.x
+ 32 ) & -64 ) - hinter
->pp2
.x
) / 2;
1075 target
= left
->opos
+ ( right
->opos
- left
->opos
) / 2 + delta
- 16;
1078 delta1
+= af_hint_normal_stem( hints
, left
, left
->link
,
1081 if ( left
->link
!= right
)
1082 af_hint_normal_stem( hints
, right
->link
, right
, delta1
, 0 );
1084 center1
= left
->pos
+ ( right
->pos
- left
->pos
) / 2;
1086 if ( center1
>= target
)
1087 delta2
= delta
- 32;
1089 delta2
= delta
+ 32;
1091 delta2
+= af_hint_normal_stem( hints
, &left1
, &left2
, delta2
, 0 );
1093 if ( delta1
!= delta2
)
1095 if ( left
->link
!= right
)
1096 af_hint_normal_stem( hints
, &right1
, &right2
, delta2
, 0 );
1098 center2
= left1
.pos
+ ( right2
.pos
- left1
.pos
) / 2;
1100 d1
= center1
- target
;
1101 d2
= center2
- target
;
1103 if ( FT_ABS( d2
) < FT_ABS( d1
) )
1105 left
->pos
= left1
.pos
;
1106 left
->link
->pos
= left2
.pos
;
1108 if ( left
->link
!= right
)
1110 right
->link
->pos
= right1
.pos
;
1111 right
->pos
= right2
.pos
;
1119 right
->link
->flags
|= AF_EDGE_DONE
;
1120 right
->flags
|= AF_EDGE_DONE
;
1126 delta
= af_hint_normal_stem( hints
, edge
, edge2
, 0,
1127 AF_DIMENSION_HORZ
);
1130 af_hint_normal_stem( hints
, edge
, edge2
, delta
, dim
);
1133 printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
1134 edge
- edges
, edge2
- edges
,
1135 ( edge
->pos
- edge
->opos
) / 64.0,
1136 ( edge2
->pos
- edge2
->opos
) / 64.0 );
1140 edge
->flags
|= AF_EDGE_DONE
;
1141 edge2
->flags
|= AF_EDGE_DONE
;
1144 /* make sure that lowercase m's maintain their symmetry */
1146 /* In general, lowercase m's have six vertical edges if they are sans */
1147 /* serif, or twelve if they are with serifs. This implementation is */
1148 /* based on that assumption, and seems to work very well with most */
1149 /* faces. However, if for a certain face this assumption is not */
1150 /* true, the m is just rendered like before. In addition, any stem */
1151 /* correction will only be applied to symmetrical glyphs (even if the */
1152 /* glyph is not an m), so the potential for unwanted distortion is */
1153 /* relatively low. */
1155 /* We don't handle horizontal edges since we can't easily assure that */
1156 /* the third (lowest) stem aligns with the base line; it might end up */
1157 /* one pixel higher or lower. */
1159 n_edges
= edge_limit
- edges
;
1160 if ( dim
== AF_DIMENSION_HORZ
&& ( n_edges
== 6 || n_edges
== 12 ) )
1162 AF_Edge edge1
, edge2
, edge3
;
1163 FT_Pos dist1
, dist2
, span
;
1179 dist1
= edge2
->opos
- edge1
->opos
;
1180 dist2
= edge3
->opos
- edge2
->opos
;
1182 span
= dist1
- dist2
;
1186 if ( edge1
->link
== edge1
+ 1 &&
1187 edge2
->link
== edge2
+ 1 &&
1188 edge3
->link
== edge3
+ 1 && span
< 8 )
1190 delta
= edge3
->pos
- ( 2 * edge2
->pos
- edge1
->pos
);
1191 edge3
->pos
-= delta
;
1193 edge3
->link
->pos
-= delta
;
1195 /* move the serifs along with the stem */
1196 if ( n_edges
== 12 )
1198 ( edges
+ 8 )->pos
-= delta
;
1199 ( edges
+ 11 )->pos
-= delta
;
1202 edge3
->flags
|= AF_EDGE_DONE
;
1204 edge3
->link
->flags
|= AF_EDGE_DONE
;
1212 * now hint the remaining edges (serifs and single) in order
1213 * to complete our processing
1215 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1217 if ( edge
->flags
& AF_EDGE_DONE
)
1222 af_cjk_align_serif_edge( hints
, edge
->serif
, edge
);
1223 edge
->flags
|= AF_EDGE_DONE
;
1231 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1233 AF_Edge before
, after
;
1236 if ( edge
->flags
& AF_EDGE_DONE
)
1239 before
= after
= edge
;
1241 while ( --before
>= edges
)
1242 if ( before
->flags
& AF_EDGE_DONE
)
1245 while ( ++after
< edge_limit
)
1246 if ( after
->flags
& AF_EDGE_DONE
)
1249 if ( before
>= edges
|| after
< edge_limit
)
1251 if ( before
< edges
)
1252 af_cjk_align_serif_edge( hints
, after
, edge
);
1253 else if ( after
>= edge_limit
)
1254 af_cjk_align_serif_edge( hints
, before
, edge
);
1256 edge
->pos
= before
->pos
+
1257 FT_MulDiv( edge
->fpos
- before
->fpos
,
1258 after
->pos
- before
->pos
,
1259 after
->fpos
- before
->fpos
);
1266 af_cjk_align_edge_points( AF_GlyphHints hints
,
1269 AF_AxisHints axis
= & hints
->axis
[dim
];
1270 AF_Edge edges
= axis
->edges
;
1271 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1276 snapping
= FT_BOOL( ( dim
== AF_DIMENSION_HORZ
&&
1277 AF_LATIN_HINTS_DO_HORZ_SNAP( hints
) ) ||
1278 ( dim
== AF_DIMENSION_VERT
&&
1279 AF_LATIN_HINTS_DO_VERT_SNAP( hints
) ) );
1281 for ( edge
= edges
; edge
< edge_limit
; edge
++ )
1283 /* move the points of each segment */
1284 /* in each edge to the edge's position */
1285 AF_Segment seg
= edge
->first
;
1292 AF_Point point
= seg
->first
;
1297 if ( dim
== AF_DIMENSION_HORZ
)
1299 point
->x
= edge
->pos
;
1300 point
->flags
|= AF_FLAG_TOUCH_X
;
1304 point
->y
= edge
->pos
;
1305 point
->flags
|= AF_FLAG_TOUCH_Y
;
1308 if ( point
== seg
->last
)
1311 point
= point
->next
;
1314 seg
= seg
->edge_next
;
1316 } while ( seg
!= edge
->first
);
1320 FT_Pos delta
= edge
->pos
- edge
->opos
;
1325 AF_Point point
= seg
->first
;
1330 if ( dim
== AF_DIMENSION_HORZ
)
1333 point
->flags
|= AF_FLAG_TOUCH_X
;
1338 point
->flags
|= AF_FLAG_TOUCH_Y
;
1341 if ( point
== seg
->last
)
1344 point
= point
->next
;
1347 seg
= seg
->edge_next
;
1349 } while ( seg
!= edge
->first
);
1355 FT_LOCAL_DEF( FT_Error
)
1356 af_cjk_hints_apply( AF_GlyphHints hints
,
1357 FT_Outline
* outline
,
1358 AF_LatinMetrics metrics
)
1363 FT_UNUSED( metrics
);
1366 error
= af_glyph_hints_reload( hints
, outline
, 0 );
1370 /* analyze glyph outline */
1371 if ( AF_HINTS_DO_HORIZONTAL( hints
) )
1373 error
= af_cjk_hints_detect_features( hints
, AF_DIMENSION_HORZ
);
1378 if ( AF_HINTS_DO_VERTICAL( hints
) )
1380 error
= af_cjk_hints_detect_features( hints
, AF_DIMENSION_VERT
);
1385 /* grid-fit the outline */
1386 for ( dim
= 0; dim
< AF_DIMENSION_MAX
; dim
++ )
1388 if ( ( dim
== AF_DIMENSION_HORZ
&& AF_HINTS_DO_HORIZONTAL( hints
) ) ||
1389 ( dim
== AF_DIMENSION_VERT
&& AF_HINTS_DO_VERTICAL( hints
) ) )
1392 #ifdef AF_USE_WARPER
1393 if ( dim
== AF_DIMENSION_HORZ
&&
1394 metrics
->root
.scaler
.render_mode
== FT_RENDER_MODE_NORMAL
)
1396 AF_WarperRec warper
;
1401 af_warper_compute( &warper
, hints
, dim
, &scale
, &delta
);
1402 af_glyph_hints_scale_dim( hints
, dim
, scale
, delta
);
1405 #endif /* AF_USE_WARPER */
1407 af_cjk_hint_edges( hints
, (AF_Dimension
)dim
);
1408 af_cjk_align_edge_points( hints
, (AF_Dimension
)dim
);
1409 af_glyph_hints_align_strong_points( hints
, (AF_Dimension
)dim
);
1410 af_glyph_hints_align_weak_points( hints
, (AF_Dimension
)dim
);
1415 af_glyph_hints_dump_points( hints
);
1416 af_glyph_hints_dump_segments( hints
);
1417 af_glyph_hints_dump_edges( hints
);
1420 af_glyph_hints_save( hints
, outline
);
1427 /*************************************************************************/
1428 /*************************************************************************/
1430 /***** C J K S C R I P T C L A S S *****/
1432 /*************************************************************************/
1433 /*************************************************************************/
1436 static const AF_Script_UniRangeRec af_cjk_uniranges
[] =
1439 { 0x0100, 0xFFFF }, /* why this? */
1441 { 0x2E80, 0x2EFF }, /* CJK Radicals Supplement */
1442 { 0x2F00, 0x2FDF }, /* Kangxi Radicals */
1443 { 0x3000, 0x303F }, /* CJK Symbols and Punctuation */
1444 { 0x3040, 0x309F }, /* Hiragana */
1445 { 0x30A0, 0x30FF }, /* Katakana */
1446 { 0x3100, 0x312F }, /* Bopomofo */
1447 { 0x3130, 0x318F }, /* Hangul Compatibility Jamo */
1448 { 0x31A0, 0x31BF }, /* Bopomofo Extended */
1449 { 0x31C0, 0x31EF }, /* CJK Strokes */
1450 { 0x31F0, 0x31FF }, /* Katakana Phonetic Extensions */
1451 { 0x3200, 0x32FF }, /* Enclosed CJK Letters and Months */
1452 { 0x3300, 0x33FF }, /* CJK Compatibility */
1453 { 0x3400, 0x4DBF }, /* CJK Unified Ideographs Extension A */
1454 { 0x4DC0, 0x4DFF }, /* Yijing Hexagram Symbols */
1455 { 0x4E00, 0x9FFF }, /* CJK Unified Ideographs */
1456 { 0xF900, 0xFAFF }, /* CJK Compatibility Ideographs */
1457 { 0xFE30, 0xFE4F }, /* CJK Compatibility Forms */
1458 { 0xFF00, 0xFFEF }, /* Halfwidth and Fullwidth Forms */
1459 { 0x20000, 0x2A6DF }, /* CJK Unified Ideographs Extension B */
1460 { 0x2F800, 0x2FA1F }, /* CJK Compatibility Ideographs Supplement */
1465 FT_CALLBACK_TABLE_DEF
const AF_ScriptClassRec
1466 af_cjk_script_class
=
1471 sizeof( AF_LatinMetricsRec
),
1473 (AF_Script_InitMetricsFunc
) af_cjk_metrics_init
,
1474 (AF_Script_ScaleMetricsFunc
)af_cjk_metrics_scale
,
1475 (AF_Script_DoneMetricsFunc
) NULL
,
1477 (AF_Script_InitHintsFunc
) af_cjk_hints_init
,
1478 (AF_Script_ApplyHintsFunc
) af_cjk_hints_apply
1481 #else /* !AF_CONFIG_OPTION_CJK */
1483 static const AF_Script_UniRangeRec af_cjk_uniranges
[] =
1489 FT_CALLBACK_TABLE_DEF
const AF_ScriptClassRec
1490 af_cjk_script_class
=
1495 sizeof( AF_LatinMetricsRec
),
1497 (AF_Script_InitMetricsFunc
) NULL
,
1498 (AF_Script_ScaleMetricsFunc
)NULL
,
1499 (AF_Script_DoneMetricsFunc
) NULL
,
1501 (AF_Script_InitHintsFunc
) NULL
,
1502 (AF_Script_ApplyHintsFunc
) NULL
1505 #endif /* !AF_CONFIG_OPTION_CJK */