1 /***************************************************************************/
5 /* Routines used to load and analyze a given glyph before hinting */
8 /* Copyright 2000 Catharon Productions Inc. */
9 /* Author: David Turner */
11 /* This file is part of the Catharon Typography Project and shall only */
12 /* be used, modified, and distributed under the terms of the Catharon */
13 /* Open Source License that should come with this file under the name */
14 /* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
15 /* this file you indicate that you have read the license and */
16 /* understand and accept it fully. */
18 /* Note that this license is compatible with the FreeType license. */
20 /***************************************************************************/
23 #ifdef FT_FLAT_COMPILE
31 #include <freetype/src/autohint/ahglyph.h>
32 #include <freetype/src/autohint/ahangles.h>
33 #include <freetype/src/autohint/ahglobal.h>
41 #define xxxAH_DEBUG_GLYPH
44 /* compute the direction value of a given vector.. */
46 AH_Direction ah_compute_direction( FT_Pos dx,
50 FT_Pos ax = ABS( dx );
51 FT_Pos ay = ABS( dy );
56 /* test for vertical direction */
59 dir = dy > 0 ? ah_dir_up : ah_dir_down;
61 /* test for horizontal direction */
62 else if ( ay * 12 < ax )
64 dir = dx > 0 ? ah_dir_right : ah_dir_left;
71 /*************************************************************************/
77 /* Creates a new and empty AH_Outline object. */
80 FT_Error ah_outline_new( FT_Memory memory,
81 AH_Outline** aoutline )
87 if ( !ALLOC( outline, sizeof ( *outline ) ) )
89 outline->memory = memory;
97 /*************************************************************************/
100 /* ah_outline_done */
103 /* Destroys a given AH_Outline object. */
106 void ah_outline_done( AH_Outline* outline )
108 FT_Memory memory = outline->memory;
111 FREE( outline->horz_edges );
112 FREE( outline->horz_segments );
113 FREE( outline->contours );
114 FREE( outline->points );
120 /*************************************************************************/
123 /* ah_outline_save */
126 /* Saves the content of a given AH_Outline object into a face's glyph */
130 void ah_outline_save( AH_Outline* outline,
133 AH_Point* point = outline->points;
134 AH_Point* limit = point + outline->num_points;
135 FT_Vector* vec = gloader->current.outline.points;
136 char* tag = gloader->current.outline.tags;
139 /* we assume that the glyph loader has already been checked for storage */
140 for ( ; point < limit; point++, vec++, tag++ )
145 if ( point->flags & ah_flah_conic )
146 tag[0] = FT_Curve_Tag_Conic;
147 else if ( point->flags & ah_flah_cubic )
148 tag[0] = FT_Curve_Tag_Cubic;
150 tag[0] = FT_Curve_Tag_On;
155 /*************************************************************************/
158 /* ah_outline_load */
161 /* Loads an unscaled outline from a glyph slot into an AH_Outline */
165 FT_Error ah_outline_load( AH_Outline* outline,
168 FT_Memory memory = outline->memory;
169 FT_Error error = FT_Err_Ok;
170 FT_Outline* source = &face->glyph->outline;
171 FT_Int num_points = source->n_points;
172 FT_Int num_contours = source->n_contours;
176 /* check arguments */
179 face->glyph->format != ft_glyph_format_outline )
180 return FT_Err_Invalid_Argument;
182 /* first of all, reallocate the contours array if necessary */
183 if ( num_contours > outline->max_contours )
185 FT_Int new_contours = ( num_contours + 3 ) & -4;
188 if ( REALLOC_ARRAY( outline->contours, outline->max_contours,
189 new_contours, AH_Point* ) )
192 outline->max_contours = new_contours;
195 /* then, realloc the points, segments & edges arrays if needed */
196 if ( num_points > outline->max_points )
198 FT_Int news = ( num_points + 7 ) & -8;
199 FT_Int max = outline->max_points;
202 if ( REALLOC_ARRAY( outline->points, max, news, AH_Point ) ||
203 REALLOC_ARRAY( outline->horz_edges, max, news, AH_Edge ) ||
204 REALLOC_ARRAY( outline->horz_segments, max, news, AH_Segment ) )
207 /* readjust some pointers */
208 outline->vert_edges = outline->horz_edges + ( news >> 1 );
209 outline->vert_segments = outline->horz_segments + ( news >> 1 );
210 outline->max_points = news;
213 outline->num_points = num_points;
214 outline->num_contours = num_contours;
216 outline->num_hedges = 0;
217 outline->num_vedges = 0;
218 outline->num_hsegments = 0;
219 outline->num_vsegments = 0;
221 /* Compute the vertical and horizontal major directions; this is */
222 /* currently done by inspecting the `ft_outline_reverse_fill' flag. */
223 /* However, some fonts have improper glyphs, and it'd be a good idea */
224 /* to be able to re-compute these values on the fly. */
225 outline->vert_major_dir = ah_dir_up;
226 outline->horz_major_dir = ah_dir_left;
228 if ( source->flags & ft_outline_reverse_fill )
230 outline->vert_major_dir = ah_dir_down;
231 outline->horz_major_dir = ah_dir_right;
234 outline->x_scale = face->size->metrics.x_scale;
235 outline->y_scale = face->size->metrics.y_scale;
237 points = outline->points;
240 /* do one thing at a time -- it is easier to understand, and */
241 /* the code is clearer */
242 AH_Point* point = points;
243 AH_Point* limit = point + outline->num_points;
246 /* compute coordinates */
248 FT_Vector* vec = source->points;
249 FT_Fixed x_scale = outline->x_scale;
250 FT_Fixed y_scale = outline->y_scale;
253 for (; point < limit; vec++, point++ )
257 point->ox = point->x = FT_MulFix( vec->x, x_scale );
258 point->oy = point->y = FT_MulFix( vec->y, y_scale );
264 /* compute Bezier flags */
266 char* tag = source->tags;
269 for ( point = points; point < limit; point++, tag++ )
271 switch ( FT_CURVE_TAG( *tag ) )
273 case FT_Curve_Tag_Conic:
274 point->flags = ah_flah_conic; break;
275 case FT_Curve_Tag_Cubic:
276 point->flags = ah_flah_cubic; break;
283 /* compute `next' and `prev' */
285 FT_Int contour_index;
294 end = points + source->contours[0];
297 for ( point = points; point < limit; point++ )
302 point->next = point + 1;
309 if ( point + 1 < limit )
311 end = points + source->contours[contour_index];
319 /* set-up the contours array */
321 AH_Point** contour = outline->contours;
322 AH_Point** limit = contour + outline->num_contours;
323 short* end = source->contours;
327 for ( ; contour < limit; contour++, end++ )
329 contour[0] = points + index;
334 /* compute directions of in & out vectors */
336 for ( point = points; point < limit; point++ )
344 vec.x = point->fx - prev->fx;
345 vec.y = point->fy - prev->fy;
347 point->in_dir = ah_compute_direction( vec.x, vec.y );
349 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
350 point->in_angle = ah_angle( &vec );
354 vec.x = next->fx - point->fx;
355 vec.y = next->fy - point->fy;
357 point->out_dir = ah_compute_direction( vec.x, vec.y );
359 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
360 point->out_angle = ah_angle( &vec );
363 AH_Angle delta = point->in_angle - point->out_angle;
369 point->flags |= ah_flah_weak_interpolation;
373 if ( point->flags & ( ah_flah_conic | ah_flah_cubic ) )
374 point->flags |= ah_flah_weak_interpolation;
377 #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */
379 #ifdef AH_OPTION_NO_STRONG_INTERPOLATION
380 point->flags |= ah_flah_weak_interpolation;
392 void ah_setup_uv( AH_Outline* outline,
395 AH_Point* point = outline->points;
396 AH_Point* limit = point + outline->num_points;
399 for ( ; point < limit; point++ )
446 void ah_outline_compute_segments( AH_Outline* outline )
449 AH_Segment* segments;
450 FT_Int* p_num_segments;
451 AH_Direction segment_dir;
452 AH_Direction major_dir;
455 segments = outline->horz_segments;
456 p_num_segments = &outline->num_hsegments;
457 major_dir = ah_dir_right; /* This value must be positive! */
458 segment_dir = major_dir;
460 /* set up (u,v) in each point */
461 ah_setup_uv( outline, ah_uv_fyx );
463 for ( dimension = 1; dimension >= 0; dimension-- )
465 AH_Point** contour = outline->contours;
466 AH_Point** contour_limit = contour + outline->num_contours;
467 AH_Segment* segment = segments;
468 FT_Int num_segments = 0;
470 #ifdef AH_HINT_METRICS
471 AH_Point* min_point = 0;
472 AH_Point* max_point = 0;
473 FT_Pos min_coord = 32000;
474 FT_Pos max_coord = -32000;
478 /* do each contour separately */
479 for ( ; contour < contour_limit; contour++ )
481 AH_Point* point = contour[0];
482 AH_Point* last = point->prev;
484 FT_Pos min_pos = +32000; /* minimum segment pos != min_coord */
485 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
489 #ifdef AH_HINT_METRICS
490 if ( point->u < min_coord )
492 min_coord = point->u;
495 if ( point->u > max_coord )
497 max_coord = point->u;
502 if ( point == last ) /* skip singletons -- just in case? */
505 if ( ABS( last->out_dir ) == major_dir &&
506 ABS( point->out_dir ) == major_dir )
508 /* we are already on an edge, try to locate its start */
514 if ( ABS( point->out_dir ) != major_dir )
541 if ( point->out_dir != segment_dir || point == last )
543 /* we are just leaving an edge; record a new segment! */
544 segment->last = point;
545 segment->pos = ( min_pos + max_pos ) >> 1;
547 /* a segment is round if either its first or last point */
548 /* is a control point */
549 if ( ( segment->first->flags | point->flags ) &
551 segment->flags |= ah_edge_round;
553 /* compute segment size */
554 min_pos = max_pos = point->v;
556 v = segment->first->v;
562 segment->min_coord = min_pos;
563 segment->max_coord = max_pos;
572 /* now exit if we are at the start/end point */
580 if ( !on_edge && ABS( point->out_dir ) == major_dir )
582 /* this is the start of a new segment! */
583 segment_dir = point->out_dir;
585 /* clear all segment fields */
586 memset( segment, 0, sizeof ( *segment ) );
588 segment->dir = segment_dir;
589 segment->flags = ah_edge_normal;
590 min_pos = max_pos = point->u;
591 segment->first = point;
592 segment->last = point;
593 segment->contour = contour;
596 if ( point == max_point )
599 if ( point == min_point )
608 #ifdef AH_HINT_METRICS
609 /* we need to ensure that there are edges on the left-most and */
610 /* right-most points of the glyph in order to hint the metrics; */
611 /* we do this by inserting fake segments when needed */
612 if ( dimension == 0 )
614 AH_Point* point = outline->points;
615 AH_Point* limit = point + outline->num_points;
617 AH_Point* min_point = 0;
618 AH_Point* max_point = 0;
619 FT_Pos min_pos = 32000;
620 FT_Pos max_pos = -32000;
623 /* compute minimum and maximum points */
624 for ( ; point < limit; point++ )
626 FT_Pos x = point->fx;
641 /* insert minimum segment */
644 /* clear all segment fields */
645 memset( segment, 0, sizeof ( *segment ) );
647 segment->dir = segment_dir;
648 segment->flags = ah_edge_normal;
649 segment->first = min_point;
650 segment->last = min_point;
651 segment->pos = min_pos;
657 /* insert maximum segment */
660 /* clear all segment fields */
661 memset( segment, 0, sizeof ( *segment ) );
663 segment->dir = segment_dir;
664 segment->flags = ah_edge_normal;
665 segment->first = max_point;
666 segment->last = max_point;
667 segment->pos = max_pos;
673 #endif /* AH_HINT_METRICS */
675 *p_num_segments = num_segments;
677 segments = outline->vert_segments;
678 major_dir = ah_dir_up;
679 p_num_segments = &outline->num_vsegments;
680 ah_setup_uv( outline, ah_uv_fxy );
686 void ah_outline_link_segments( AH_Outline* outline )
688 AH_Segment* segments;
693 ah_setup_uv( outline, ah_uv_fyx );
695 segments = outline->horz_segments;
696 limit = segments + outline->num_hsegments;
698 for ( dimension = 1; dimension >= 0; dimension-- )
704 /* now compare each segment to the others */
705 for ( seg1 = segments; seg1 < limit; seg1++ )
707 FT_Pos best_score = 32000;
708 AH_Segment* best_segment = 0;
711 /* the fake segments are introduced to hint the metrics -- */
712 /* we must never link them to anything */
713 if ( seg1->first == seg1->last )
716 for ( seg2 = segments; seg2 < limit; seg2++ )
717 if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 )
719 FT_Pos pos1 = seg1->pos;
720 FT_Pos pos2 = seg2->pos;
725 /* check that the segments are correctly oriented and */
726 /* positioned to form a black distance */
728 is_dir = ( seg1->dir == outline->horz_major_dir ||
729 seg1->dir == outline->vert_major_dir );
730 is_pos = pos1 > pos2;
732 if ( pos1 == pos2 || !(is_dir ^ is_pos) )
735 /* Check the two segments. We now have a better algorithm */
736 /* that doesn't rely on the segment points themselves but */
737 /* on their relative position. This gets rids of many */
738 /* unpleasant artefacts and incorrect stem/serifs */
741 /* first of all, compute the size of the `common' height */
743 FT_Pos min = seg1->min_coord;
744 FT_Pos max = seg1->max_coord;
750 size2 = seg2->max_coord - seg2->min_coord;
752 if ( min < seg2->min_coord )
753 min = seg2->min_coord;
755 if ( max < seg2->max_coord )
756 max = seg2->max_coord;
759 score = seg2->pos - seg1->pos;
763 /* before comparing the scores, take care that the segments */
764 /* are really facing each other (often not for italics..) */
765 if ( 4 * len >= size1 && 4 * len >= size2 )
766 if ( score < best_score )
776 seg1->link = best_segment;
777 seg1->score = best_score;
779 best_segment->num_linked++;
785 /* now, compute the `serif' segments */
786 for ( seg1 = segments; seg1 < limit; seg1++ )
790 if ( seg2 && seg2->link != seg1 )
793 seg1->serif = seg2->link;
797 ah_setup_uv( outline, ah_uv_fxy );
799 segments = outline->vert_segments;
800 limit = segments + outline->num_vsegments;
805 #ifdef AH_DEBUG_GLYPH
807 /* A function used to dump the array of linked segments */
808 void ah_dump_segments( AH_Outline* outline )
810 AH_Segment* segments;
816 points = outline->points;
817 segments = outline->horz_segments;
818 limit = segments + outline->num_hsegments;
820 for ( dimension = 1; dimension >= 0; dimension-- )
825 printf ( "Table of %s segments:\n",
826 !dimension ? "vertical" : "horizontal" );
827 printf ( " [ index | pos | dir | link | serif |"
828 " numl | first | start ]\n" );
830 for ( seg = segments; seg < limit; seg++ )
832 printf ( " [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
835 seg->dir == ah_dir_up
837 : ( seg->dir == ah_dir_down
839 : ( seg->dir == ah_dir_left
841 : ( seg->dir == ah_dir_right
844 seg->link ? (seg->link-segments) : -1,
845 seg->serif ? (seg->serif-segments) : -1,
846 (int)seg->num_linked,
848 seg->last - points );
851 segments = outline->vert_segments;
852 limit = segments + outline->num_vsegments;
856 #endif /* AH_DEBUG_GLYPH */
860 void ah_outline_compute_edges( AH_Outline* outline )
863 AH_Segment* segments;
864 AH_Segment* segment_limit;
869 FT_Pos edge_distance_threshold;
872 edges = outline->horz_edges;
873 segments = outline->horz_segments;
874 segment_limit = segments + outline->num_hsegments;
875 p_num_edges = &outline->num_hedges;
876 up_dir = ah_dir_right;
877 scale = outline->y_scale;
879 for ( dimension = 1; dimension >= 0; dimension-- )
882 AH_Edge* edge_limit; /* really == edge + num_edges */
886 /*********************************************************************/
888 /* We will begin by generating a sorted table of edges for the */
889 /* current direction. To do so, we simply scan each segment and try */
890 /* to find an edge in our table that corresponds to its position. */
892 /* If no edge is found, we create and insert a new edge in the */
893 /* sorted table. Otherwise, we simply add the segment to the edge's */
894 /* list which will be processed in the second step to compute the */
895 /* edge's properties. */
897 /* Note that the edges table is sorted along the segment/edge */
900 /*********************************************************************/
902 edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold,
904 if ( edge_distance_threshold > 64 / 4 )
905 edge_distance_threshold = 64 / 4;
908 for ( seg = segments; seg < segment_limit; seg++ )
913 /* look for an edge corresponding to the segment */
914 for ( edge = edges; edge < edge_limit; edge++ )
919 dist = seg->pos - edge->fpos;
923 dist = FT_MulFix( dist, scale );
924 if ( dist < edge_distance_threshold )
933 /* insert a new edge in the list and */
934 /* sort according to the position */
935 while ( edge > edges && edge[-1].fpos > seg->pos )
942 /* clear all edge fields */
943 memset( edge, 0, sizeof ( *edge ) );
945 /* add the segment to the new edge's list */
948 edge->fpos = seg->pos;
949 edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
950 seg->edge_next = seg;
954 /* if an edge was found, simply add the segment to the edge's */
956 seg->edge_next = edge->first;
957 edge->last->edge_next = seg;
962 *p_num_edges = edge_limit - edges;
965 /*********************************************************************/
967 /* Good, we will now compute each edge's properties according to */
968 /* segments found on its position. Basically, these are: */
970 /* - edge's main direction */
971 /* - stem edge, serif edge or both (which defaults to stem then) */
972 /* - rounded edge, straigth or both (which defaults to straight) */
973 /* - link for edge */
975 /*********************************************************************/
977 /* first of all, set the `edge' field in each segment -- this is */
978 /* required in order to compute edge links */
979 for ( edge = edges; edge < edge_limit; edge++ )
986 seg = seg->edge_next;
988 while ( seg != edge->first );
991 /* now, compute each edge properties */
992 for ( edge = edges; edge < edge_limit; edge++ )
994 int is_round = 0; /* does it contain round segments? */
995 int is_straight = 0; /* does it contain straight segments? */
996 int ups = 0; /* number of upwards segments */
997 int downs = 0; /* number of downwards segments */
1007 /* check for roundness of segment */
1008 if ( seg->flags & ah_edge_round )
1013 /* check for segment direction */
1014 if ( seg->dir == up_dir )
1015 ups += seg->max_coord-seg->min_coord;
1017 downs += seg->max_coord-seg->min_coord;
1019 /* check for links -- if seg->serif is set, then seg->link must */
1021 is_serif = seg->serif && seg->serif->edge != edge;
1023 if ( seg->link || is_serif )
1035 edge2 = edge->serif;
1044 edge_delta = edge->fpos - edge2->fpos;
1045 if ( edge_delta < 0 )
1046 edge_delta = -edge_delta;
1048 seg_delta = seg->pos - seg2->pos;
1049 if ( seg_delta < 0 )
1050 seg_delta = -seg_delta;
1052 if ( seg_delta < edge_delta )
1059 edge->serif = edge2;
1064 seg = seg->edge_next;
1066 } while ( seg != edge->first );
1068 /* set the round/straight flags */
1069 edge->flags = ah_edge_normal;
1071 if ( is_straight == 0 && is_round )
1072 edge->flags |= ah_edge_round;
1074 /* set the edge's main direction */
1075 edge->dir = ah_dir_none;
1080 else if ( ups < downs )
1081 edge->dir = - up_dir;
1083 else if ( ups == downs )
1084 edge->dir = 0; /* both up and down !! */
1086 /* gets rid of serifs if link is set */
1087 /* XXX: This gets rid of many unpleasant artefacts! */
1088 /* Example: the `c' in cour.pfa at size 13 */
1090 if ( edge->serif && edge->link )
1094 edges = outline->vert_edges;
1095 segments = outline->vert_segments;
1096 segment_limit = segments + outline->num_vsegments;
1097 p_num_edges = &outline->num_vedges;
1099 scale = outline->x_scale;
1104 /*************************************************************************/
1107 /* ah_outline_detect_features */
1110 /* Performs feature detection on a given AH_Outline object. */
1113 void ah_outline_detect_features( AH_Outline* outline )
1115 ah_outline_compute_segments( outline );
1116 ah_outline_link_segments ( outline );
1117 ah_outline_compute_edges ( outline );
1121 /*************************************************************************/
1124 /* ah_outline_compute_blue_edges */
1127 /* Computes the `blue edges' in a given outline (i.e. those that must */
1128 /* be snapped to a blue zone edge (top or bottom). */
1131 void ah_outline_compute_blue_edges( AH_Outline* outline,
1132 AH_Face_Globals* face_globals )
1134 AH_Edge* edge = outline->horz_edges;
1135 AH_Edge* limit = edge + outline->num_hedges;
1136 AH_Globals* globals = &face_globals->design;
1137 FT_Fixed y_scale = outline->y_scale;
1140 /* compute for each horizontal edge, which blue zone is closer */
1141 for ( ; edge < limit; edge++ )
1144 FT_Pos* best_blue = 0;
1145 FT_Pos best_dist; /* initial threshold */
1148 /* compute the initial threshold as a fraction of the EM size */
1149 best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale );
1150 if ( best_dist > 64 / 4 )
1153 for ( blue = ah_blue_capital_top; blue < ah_blue_max; blue++ )
1155 /* if it is a top zone, check for right edges -- if it is a bottom */
1156 /* zone, check for left edges */
1158 /* of course, that's for TrueType XXX */
1159 FT_Bool is_top_blue = AH_IS_TOP_BLUE( blue );
1160 FT_Bool is_major_dir = edge->dir == outline->horz_major_dir;
1163 /* if it is a top zone, the edge must be against the major */
1164 /* direction; if it is a bottom zone, it must be in the major */
1166 if ( is_top_blue ^ is_major_dir )
1169 FT_Pos* blue_pos = globals->blue_refs + blue;
1172 /* first of all, compare it to the reference position */
1173 dist = edge->fpos - *blue_pos;
1177 dist = FT_MulFix( dist, y_scale );
1178 if ( dist < best_dist )
1181 best_blue = blue_pos;
1184 /* now, compare it to the overshoot position if the edge is */
1185 /* rounded, and if the edge is over the reference position of a */
1186 /* top zone, or under the reference position of a bottom zone */
1187 if ( edge->flags & ah_edge_round && dist != 0 )
1189 FT_Bool is_under_ref = edge->fpos < *blue_pos;
1192 if ( is_top_blue ^ is_under_ref )
1194 blue_pos = globals->blue_shoots + blue;
1195 dist = edge->fpos - *blue_pos;
1199 dist = FT_MulFix( dist, y_scale );
1200 if ( dist < best_dist )
1203 best_blue = blue_pos;
1211 edge->blue_edge = best_blue;
1216 /*************************************************************************/
1219 /* ah_outline_scale_blue_edges */
1222 /* This functions must be called before hinting in order to re-adjust */
1223 /* the contents of the detected edges (basically change the `blue */
1224 /* edge' pointer from `design units' to `scaled ones'). */
1227 void ah_outline_scale_blue_edges( AH_Outline* outline,
1228 AH_Face_Globals* globals )
1230 AH_Edge* edge = outline->horz_edges;
1231 AH_Edge* limit = edge + outline->num_hedges;
1235 delta = globals->scaled.blue_refs - globals->design.blue_refs;
1237 for ( ; edge < limit; edge++ )
1239 if ( edge->blue_edge )
1240 edge->blue_edge += delta;
1245 #ifdef AH_DEBUG_GLYPH
1247 void ah_dump_edges( AH_Outline* outline )
1251 AH_Segment* segments;
1255 edges = outline->horz_edges;
1256 limit = edges + outline->num_hedges;
1257 segments = outline->horz_segments;
1259 for ( dimension = 1; dimension >= 0; dimension-- )
1264 printf ( "Table of %s edges:\n",
1265 !dimension ? "vertical" : "horizontal" );
1266 printf ( " [ index | pos | dir | link |"
1267 " serif | blue | opos | pos ]\n" );
1269 for ( edge = edges; edge < limit; edge++ )
1271 printf ( " [ %5d | %4d | %5s | %4d | %5d | %c | %5.2f | %5.2f ]\n",
1274 edge->dir == ah_dir_up
1276 : ( edge->dir == ah_dir_down
1278 : ( edge->dir == ah_dir_left
1280 : ( edge->dir == ah_dir_right
1283 edge->link ? ( edge->link - edges ) : -1,
1284 edge->serif ? ( edge->serif - edges ) : -1,
1285 edge->blue_edge ? 'y' : 'n',
1290 edges = outline->vert_edges;
1291 limit = edges + outline->num_vedges;
1292 segments = outline->vert_segments;
1296 #endif /* AH_DEBUG_GLYPH */