1 /***************************************************************************/
5 /* Routines used to compute global metrics automatically (body). */
7 /* Copyright 2000 Catharon Productions Inc. */
8 /* Author: David Turner */
10 /* This file is part of the Catharon Typography Project and shall only */
11 /* be used, modified, and distributed under the terms of the Catharon */
12 /* Open Source License that should come with this file under the name */
13 /* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
14 /* this file you indicate that you have read the license and */
15 /* understand and accept it fully. */
17 /* Note that this license is compatible with the FreeType license. */
19 /***************************************************************************/
22 #ifdef FT_FLAT_COMPILE
29 #include <autohint/ahglobal.h>
30 #include <autohint/ahglyph.h>
35 #define MAX_TEST_CHARACTERS 12
38 const char* blue_chars
[ah_blue_max
] =
48 /* simple insertion sort */
50 void sort_values( FT_Int count
,
56 for ( i
= 1; i
< count
; i
++ )
58 for ( j
= i
; j
> 1; j
-- )
60 if ( table
[j
] > table
[j
- 1] )
64 table
[j
] = table
[j
- 1];
72 FT_Error
ah_hinter_compute_blues( AH_Hinter
* hinter
)
75 AH_Globals
* globals
= &hinter
->globals
->design
;
76 FT_Pos flats
[MAX_TEST_CHARACTERS
];
77 FT_Pos rounds
[MAX_TEST_CHARACTERS
];
90 /* save current charmap */
91 charmap
= face
->charmap
;
93 /* do we have a Unicode charmap in there? */
94 error
= FT_Select_Charmap( face
, ft_encoding_unicode
);
98 /* we compute the blues simply by loading each character from the */
99 /* 'blue_chars[blues]' string, then compute its top-most and */
100 /* bottom-most points */
102 AH_LOG(( "blue zones computation\n" ));
103 AH_LOG(( "------------------------------------------------\n" ));
105 for ( blue
= ah_blue_capital_top
; blue
< ah_blue_max
; blue
++ )
107 const char* p
= blue_chars
[blue
];
108 const char* limit
= p
+ MAX_TEST_CHARACTERS
;
109 FT_Pos
*blue_ref
, *blue_shoot
;
112 AH_LOG(( "blue %3d: ", blue
));
117 for ( ; p
< limit
; p
++ )
122 FT_Vector
* point_limit
;
127 /* exit if we reach the end of the string */
131 AH_LOG(( "`%c'", *p
));
133 /* load the character in the face -- skip unknown or empty ones */
134 glyph_index
= FT_Get_Char_Index( face
, (FT_UInt
)*p
);
135 if ( glyph_index
== 0 )
138 error
= FT_Load_Glyph( face
, glyph_index
, FT_LOAD_NO_SCALE
);
139 if ( error
|| glyph
->outline
.n_points
<= 0 )
142 /* now compute min or max point indices and coordinates */
143 points
= glyph
->outline
.points
;
144 point_limit
= points
+ glyph
->outline
.n_points
;
149 if ( AH_IS_TOP_BLUE( blue
) )
151 for ( ; point
< point_limit
; point
++ )
152 if ( point
->y
> extremum
->y
)
157 for ( ; point
< point_limit
; point
++ )
158 if ( point
->y
< extremum
->y
)
162 AH_LOG(( "%5d", (int)extremum
->y
));
164 /* now, check whether the point belongs to a straight or round */
165 /* segment; we first need to find in which contour the extremum */
166 /* lies, then see its previous and next points */
168 FT_Int index
= extremum
- points
;
170 FT_Int first
, last
, prev
, next
, end
;
177 for ( n
= 0; n
< glyph
->outline
.n_contours
; n
++ )
179 end
= glyph
->outline
.contours
[n
];
188 /* XXX: should never happen! */
192 /* now look for the previous and next points that are not on the */
193 /* same Y coordinate. Threshold the `closeness'... */
205 dist
= points
[prev
].y
- extremum
->y
;
206 if ( dist
< -5 || dist
> 5 )
209 } while ( prev
!= index
);
218 dist
= points
[next
].y
- extremum
->y
;
219 if ( dist
< -5 || dist
> 5 )
222 } while ( next
!= index
);
224 /* now, set the `round' flag depending on the segment's kind */
226 FT_CURVE_TAG( glyph
->outline
.tags
[prev
] ) != FT_Curve_Tag_On
||
227 FT_CURVE_TAG( glyph
->outline
.tags
[next
] ) != FT_Curve_Tag_On
;
229 AH_LOG(( "%c ", round
? 'r' : 'f' ));
233 rounds
[num_rounds
++] = extremum
->y
;
235 flats
[num_flats
++] = extremum
->y
;
240 /* we have computed the contents of the `rounds' and `flats' tables, */
241 /* now determine the reference and overshoot position of the blue; */
242 /* we simply take the median value after a simple short */
243 sort_values( num_rounds
, rounds
);
244 sort_values( num_flats
, flats
);
246 blue_ref
= globals
->blue_refs
+ blue
;
247 blue_shoot
= globals
->blue_shoots
+ blue
;
248 if ( num_flats
== 0 && num_rounds
== 0 )
251 *blue_shoot
= -10000;
253 else if ( num_flats
== 0 )
256 *blue_shoot
= rounds
[num_rounds
/ 2];
258 else if ( num_rounds
== 0 )
261 *blue_shoot
= flats
[num_flats
/ 2];
265 *blue_ref
= flats
[num_flats
/ 2];
266 *blue_shoot
= rounds
[num_rounds
/ 2];
269 /* there are sometimes problems: if the overshoot position of top */
270 /* zones is under its reference position, or the opposite for bottom */
271 /* zones. We must thus check everything there and correct the errors */
272 if ( *blue_shoot
!= *blue_ref
)
274 FT_Pos ref
= *blue_ref
;
275 FT_Pos shoot
= *blue_shoot
;
276 FT_Bool over_ref
= ( shoot
> ref
);
279 if ( AH_IS_TOP_BLUE( blue
) ^ over_ref
)
280 *blue_shoot
= *blue_ref
= ( shoot
+ ref
) / 2;
283 AH_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref
, *blue_shoot
));
286 /* reset original face charmap */
287 FT_Set_Charmap( face
, charmap
);
296 FT_Error
ah_hinter_compute_widths( AH_Hinter
* hinter
)
298 /* scan the array of segments in each direction */
299 AH_Outline
* outline
= hinter
->glyph
;
300 AH_Segment
* segments
;
302 AH_Globals
* globals
= &hinter
->globals
->design
;
305 FT_Int
* p_num_widths
;
307 FT_Pos edge_distance_threshold
= 32000;
310 globals
->num_widths
= 0;
311 globals
->num_heights
= 0;
313 /* For now, compute the standard width and height from the `o' */
314 /* character. I started computing the stem width of the `i' and the */
315 /* stem height of the "-", but it wasn't too good. Moreover, we now */
316 /* have a single character that gives us standard width and height. */
321 glyph_index
= FT_Get_Char_Index( hinter
->face
, 'o' );
322 if ( glyph_index
== 0 )
325 error
= FT_Load_Glyph( hinter
->face
, glyph_index
, FT_LOAD_NO_SCALE
);
329 error
= ah_outline_load( hinter
->glyph
, hinter
->face
);
333 ah_outline_compute_segments( hinter
->glyph
);
334 ah_outline_link_segments( hinter
->glyph
);
337 segments
= outline
->horz_segments
;
338 limit
= segments
+ outline
->num_hsegments
;
339 widths
= globals
->heights
;
340 p_num_widths
= &globals
->num_heights
;
342 for ( dimension
= 1; dimension
>= 0; dimension
-- )
344 AH_Segment
* seg
= segments
;
346 FT_Int num_widths
= 0;
349 for ( ; seg
< limit
; seg
++ )
352 /* we only consider stem segments there! */
353 if ( link
&& link
->link
== seg
&& link
> seg
)
358 dist
= seg
->pos
- link
->pos
;
362 if ( num_widths
< 12 )
363 widths
[num_widths
++] = dist
;
367 sort_values( num_widths
, widths
);
368 *p_num_widths
= num_widths
;
370 /* we will now try to find the smallest width */
371 if ( num_widths
> 0 && widths
[0] < edge_distance_threshold
)
372 edge_distance_threshold
= widths
[0];
374 segments
= outline
->vert_segments
;
375 limit
= segments
+ outline
->num_vsegments
;
376 widths
= globals
->widths
;
377 p_num_widths
= &globals
->num_widths
;
381 /* Now, compute the edge distance threshold as a fraction of the */
382 /* smallest width in the font. Set it in `hinter.glyph' too! */
383 if ( edge_distance_threshold
== 32000 )
384 edge_distance_threshold
= 50;
387 hinter
->glyph
->edge_distance_threshold
= edge_distance_threshold
/ 5;
395 FT_Error
ah_hinter_compute_globals( AH_Hinter
* hinter
)
397 return ah_hinter_compute_widths( hinter
) ||
398 ah_hinter_compute_blues ( hinter
);