1 /***************************************************************************/ 2 /* */ 3 /* ahglobal.c */ 4 /* */ 5 /* Routines used to compute global metrics automatically (body). */ 6 /* */ 7 /* Copyright 2000-2001, 2002 Catharon Productions Inc. */ 8 /* Author: David Turner */ 9 /* */ 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. */ 16 /* */ 17 /* Note that this license is compatible with the FreeType license. */ 18 /* */ 19 /***************************************************************************/ 20 21 22 #include <ft2build.h> 23 #include FT_INTERNAL_DEBUG_H 24 #include "ahglobal.h" 25 #include "ahglyph.h" 26 27 28 #define MAX_TEST_CHARACTERS 12 29 30 static 31 const char* blue_chars[AH_BLUE_MAX] = 32 { 33 "THEZOCQS", 34 "HEZLOCUS", 35 "xzroesc", 36 "xzroesc", 37 "pqgjy" 38 }; 39 40 41 /* simple insertion sort */ 42 static void sort_values(FT_Int count,FT_Pos * table)43 sort_values( FT_Int count, 44 FT_Pos* table ) 45 { 46 FT_Int i, j; 47 FT_Pos swap; 48 49 50 for ( i = 1; i < count; i++ ) 51 { 52 for ( j = i; j > 0; j-- ) 53 { 54 if ( table[j] > table[j - 1] ) 55 break; 56 57 swap = table[j]; 58 table[j] = table[j - 1]; 59 table[j - 1] = swap; 60 } 61 } 62 } 63 64 65 static FT_Error ah_hinter_compute_blues(AH_Hinter hinter)66 ah_hinter_compute_blues( AH_Hinter hinter ) 67 { 68 AH_Blue blue; 69 AH_Globals globals = &hinter->globals->design; 70 FT_Pos flats [MAX_TEST_CHARACTERS]; 71 FT_Pos rounds[MAX_TEST_CHARACTERS]; 72 FT_Int num_flats; 73 FT_Int num_rounds; 74 75 FT_Face face; 76 FT_GlyphSlot glyph; 77 FT_Error error; 78 FT_CharMap charmap; 79 80 81 face = hinter->face; 82 glyph = face->glyph; 83 84 /* save current charmap */ 85 charmap = face->charmap; 86 87 /* do we have a Unicode charmap in there? */ 88 error = FT_Select_Charmap( face, FT_ENCODING_UNICODE ); 89 if ( error ) 90 goto Exit; 91 92 /* we compute the blues simply by loading each character from the */ 93 /* 'blue_chars[blues]' string, then compute its top-most and */ 94 /* bottom-most points */ 95 96 AH_LOG(( "blue zones computation\n" )); 97 AH_LOG(( "------------------------------------------------\n" )); 98 99 for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ ) 100 { 101 const char* p = blue_chars[blue]; 102 const char* limit = p + MAX_TEST_CHARACTERS; 103 FT_Pos *blue_ref, *blue_shoot; 104 105 106 AH_LOG(( "blue %3d: ", blue )); 107 108 num_flats = 0; 109 num_rounds = 0; 110 111 for ( ; p < limit; p++ ) 112 { 113 FT_UInt glyph_index; 114 FT_Vector* extremum; 115 FT_Vector* points; 116 FT_Vector* point_limit; 117 FT_Vector* point; 118 FT_Bool round; 119 120 121 /* exit if we reach the end of the string */ 122 if ( !*p ) 123 break; 124 125 AH_LOG(( "`%c'", *p )); 126 127 /* load the character in the face -- skip unknown or empty ones */ 128 glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p ); 129 if ( glyph_index == 0 ) 130 continue; 131 132 error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); 133 if ( error || glyph->outline.n_points <= 0 ) 134 continue; 135 136 /* now compute min or max point indices and coordinates */ 137 points = glyph->outline.points; 138 point_limit = points + glyph->outline.n_points; 139 point = points; 140 extremum = point; 141 point++; 142 143 if ( AH_IS_TOP_BLUE( blue ) ) 144 { 145 for ( ; point < point_limit; point++ ) 146 if ( point->y > extremum->y ) 147 extremum = point; 148 } 149 else 150 { 151 for ( ; point < point_limit; point++ ) 152 if ( point->y < extremum->y ) 153 extremum = point; 154 } 155 156 AH_LOG(( "%5d", (int)extremum->y )); 157 158 /* now, check whether the point belongs to a straight or round */ 159 /* segment; we first need to find in which contour the extremum */ 160 /* lies, then see its previous and next points */ 161 { 162 FT_Int idx = (FT_Int)( extremum - points ); 163 FT_Int n; 164 FT_Int first, last, prev, next, end; 165 FT_Pos dist; 166 167 168 last = -1; 169 first = 0; 170 171 for ( n = 0; n < glyph->outline.n_contours; n++ ) 172 { 173 end = glyph->outline.contours[n]; 174 if ( end >= idx ) 175 { 176 last = end; 177 break; 178 } 179 first = end + 1; 180 } 181 182 /* XXX: should never happen! */ 183 if ( last < 0 ) 184 continue; 185 186 /* now look for the previous and next points that are not on the */ 187 /* same Y coordinate. Threshold the `closeness'... */ 188 189 prev = idx; 190 next = prev; 191 192 do 193 { 194 if ( prev > first ) 195 prev--; 196 else 197 prev = last; 198 199 dist = points[prev].y - extremum->y; 200 if ( dist < -5 || dist > 5 ) 201 break; 202 203 } while ( prev != idx ); 204 205 do 206 { 207 if ( next < last ) 208 next++; 209 else 210 next = first; 211 212 dist = points[next].y - extremum->y; 213 if ( dist < -5 || dist > 5 ) 214 break; 215 216 } while ( next != idx ); 217 218 /* now, set the `round' flag depending on the segment's kind */ 219 round = FT_BOOL( 220 FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_CURVE_TAG_ON || 221 FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_CURVE_TAG_ON ); 222 223 AH_LOG(( "%c ", round ? 'r' : 'f' )); 224 } 225 226 if ( round ) 227 rounds[num_rounds++] = extremum->y; 228 else 229 flats[num_flats++] = extremum->y; 230 } 231 232 AH_LOG(( "\n" )); 233 234 /* we have computed the contents of the `rounds' and `flats' tables, */ 235 /* now determine the reference and overshoot position of the blue; */ 236 /* we simply take the median value after a simple short */ 237 sort_values( num_rounds, rounds ); 238 sort_values( num_flats, flats ); 239 240 blue_ref = globals->blue_refs + blue; 241 blue_shoot = globals->blue_shoots + blue; 242 if ( num_flats == 0 && num_rounds == 0 ) 243 { 244 *blue_ref = -10000; 245 *blue_shoot = -10000; 246 } 247 else if ( num_flats == 0 ) 248 { 249 *blue_ref = 250 *blue_shoot = rounds[num_rounds / 2]; 251 } 252 else if ( num_rounds == 0 ) 253 { 254 *blue_ref = 255 *blue_shoot = flats[num_flats / 2]; 256 } 257 else 258 { 259 *blue_ref = flats[num_flats / 2]; 260 *blue_shoot = rounds[num_rounds / 2]; 261 } 262 263 /* there are sometimes problems: if the overshoot position of top */ 264 /* zones is under its reference position, or the opposite for bottom */ 265 /* zones. We must thus check everything there and correct the errors */ 266 if ( *blue_shoot != *blue_ref ) 267 { 268 FT_Pos ref = *blue_ref; 269 FT_Pos shoot = *blue_shoot; 270 FT_Bool over_ref = FT_BOOL( shoot > ref ); 271 272 273 if ( AH_IS_TOP_BLUE( blue ) ^ over_ref ) 274 *blue_shoot = *blue_ref = ( shoot + ref ) / 2; 275 } 276 277 AH_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot )); 278 } 279 280 /* reset original face charmap */ 281 FT_Set_Charmap( face, charmap ); 282 error = 0; 283 284 Exit: 285 return error; 286 } 287 288 289 static FT_Error ah_hinter_compute_widths(AH_Hinter hinter)290 ah_hinter_compute_widths( AH_Hinter hinter ) 291 { 292 /* scan the array of segments in each direction */ 293 AH_Outline outline = hinter->glyph; 294 AH_Segment segments; 295 AH_Segment limit; 296 AH_Globals globals = &hinter->globals->design; 297 FT_Pos* widths; 298 FT_Int dimension; 299 FT_Int* p_num_widths; 300 FT_Error error = 0; 301 FT_Pos edge_distance_threshold = 32000; 302 303 304 globals->num_widths = 0; 305 globals->num_heights = 0; 306 307 /* For now, compute the standard width and height from the `o' */ 308 /* character. I started computing the stem width of the `i' and the */ 309 /* stem height of the "-", but it wasn't too good. Moreover, we now */ 310 /* have a single character that gives us standard width and height. */ 311 { 312 FT_UInt glyph_index; 313 314 315 glyph_index = FT_Get_Char_Index( hinter->face, 'o' ); 316 if ( glyph_index == 0 ) 317 return 0; 318 319 error = FT_Load_Glyph( hinter->face, glyph_index, FT_LOAD_NO_SCALE ); 320 if ( error ) 321 goto Exit; 322 323 error = ah_outline_load( hinter->glyph, hinter->face ); 324 if ( error ) 325 goto Exit; 326 327 ah_outline_compute_segments( hinter->glyph ); 328 ah_outline_link_segments( hinter->glyph ); 329 } 330 331 segments = outline->horz_segments; 332 limit = segments + outline->num_hsegments; 333 widths = globals->heights; 334 p_num_widths = &globals->num_heights; 335 336 for ( dimension = 1; dimension >= 0; dimension-- ) 337 { 338 AH_Segment seg = segments; 339 AH_Segment link; 340 FT_Int num_widths = 0; 341 342 343 for ( ; seg < limit; seg++ ) 344 { 345 link = seg->link; 346 /* we only consider stem segments there! */ 347 if ( link && link->link == seg && link > seg ) 348 { 349 FT_Pos dist; 350 351 352 dist = seg->pos - link->pos; 353 if ( dist < 0 ) 354 dist = -dist; 355 356 if ( num_widths < AH_MAX_WIDTHS ) 357 widths[num_widths++] = dist; 358 } 359 } 360 361 sort_values( num_widths, widths ); 362 *p_num_widths = num_widths; 363 364 /* we will now try to find the smallest width */ 365 if ( num_widths > 0 && widths[0] < edge_distance_threshold ) 366 edge_distance_threshold = widths[0]; 367 368 segments = outline->vert_segments; 369 limit = segments + outline->num_vsegments; 370 widths = globals->widths; 371 p_num_widths = &globals->num_widths; 372 } 373 374 /* Now, compute the edge distance threshold as a fraction of the */ 375 /* smallest width in the font. Set it in `hinter.glyph' too! */ 376 if ( edge_distance_threshold == 32000 ) 377 edge_distance_threshold = 50; 378 379 /* let's try 20% */ 380 hinter->glyph->edge_distance_threshold = edge_distance_threshold / 5; 381 382 Exit: 383 return error; 384 } 385 386 387 FT_LOCAL_DEF( FT_Error ) ah_hinter_compute_globals(AH_Hinter hinter)388 ah_hinter_compute_globals( AH_Hinter hinter ) 389 { 390 return ah_hinter_compute_widths( hinter ) || 391 ah_hinter_compute_blues ( hinter ); 392 } 393 394 395 /* END */ 396