diff options
Diffstat (limited to 'src/autofit/aflatin.c')
-rw-r--r-- | src/autofit/aflatin.c | 439 |
1 files changed, 265 insertions, 174 deletions
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 15a241e..a1f2b33 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -2,9 +2,9 @@ /* */ /* aflatin.c */ /* */ -/* Auto-fitter hinting routines for latin script (body). */ +/* Auto-fitter hinting routines for latin writing system (body). */ /* */ -/* Copyright 2003-2013 by */ +/* Copyright 2003-2014 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -21,6 +21,7 @@ #include FT_INTERNAL_DEBUG_H #include "afglobal.h" +#include "afpic.h" #include "aflatin.h" #include "aferrors.h" @@ -61,10 +62,10 @@ FT_TRACE5(( "\n" - "latin standard widths computation (script `%s')\n" - "=================================================\n" + "latin standard widths computation (style `%s')\n" + "=====================================================\n" "\n", - af_script_names[metrics->root.script_class->script] )); + af_style_names[metrics->root.style_class->style] )); af_glyph_hints_init( hints, face->memory ); @@ -73,20 +74,66 @@ { FT_Error error; - FT_UInt glyph_index; + FT_ULong glyph_index; + FT_Long y_offset; int dim; AF_LatinMetricsRec dummy[1]; AF_Scaler scaler = &dummy->root.scaler; +#ifdef FT_CONFIG_OPTION_PIC + AF_FaceGlobals globals = metrics->root.globals; +#endif - glyph_index = FT_Get_Char_Index( - face, - metrics->root.script_class->standard_char ); - if ( glyph_index == 0 ) - goto Exit; + AF_StyleClass style_class = metrics->root.style_class; + AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET + [style_class->script]; + + FT_UInt32 standard_char; + + + /* + * We check more than a single standard character to catch features + * like `c2sc' (small caps from caps) that don't contain lowercase + * letters by definition, or other features that mainly operate on + * numerals. + */ + + standard_char = script_class->standard_char1; + af_get_char_index( &metrics->root, + standard_char, + &glyph_index, + &y_offset ); + if ( !glyph_index ) + { + if ( script_class->standard_char2 ) + { + standard_char = script_class->standard_char2; + af_get_char_index( &metrics->root, + standard_char, + &glyph_index, + &y_offset ); + if ( !glyph_index ) + { + if ( script_class->standard_char3 ) + { + standard_char = script_class->standard_char3; + af_get_char_index( &metrics->root, + standard_char, + &glyph_index, + &y_offset ); + if ( !glyph_index ) + goto Exit; + } + else + goto Exit; + } + } + else + goto Exit; + } FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n", - metrics->root.script_class->standard_char, glyph_index )); + standard_char, glyph_index )); error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); if ( error || face->glyph->outline.n_points <= 0 ) @@ -105,7 +152,7 @@ scaler->render_mode = FT_RENDER_MODE_NORMAL; scaler->flags = 0; - af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy ); + af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy ); error = af_glyph_hints_reload( hints, &face->glyph->outline ); if ( error ) @@ -124,7 +171,15 @@ if ( error ) goto Exit; + /* + * We assume that the glyphs selected for the stem width + * computation are `featureless' enough so that the linking + * algorithm works fine without adjustments of its scoring + * function. + */ af_latin_hints_link_segments( hints, + 0, + NULL, (AF_Dimension)dim ); seg = axhints->segments; @@ -214,12 +269,14 @@ AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT]; FT_Outline outline; - AF_Blue_Stringset bss = metrics->root.script_class->blue_stringset; + AF_StyleClass sc = metrics->root.style_class; + + AF_Blue_Stringset bss = sc->blue_stringset; const AF_Blue_StringRec* bs = &af_blue_stringsets[bss]; - /* we walk over the blue character strings as specified in the */ - /* script's entry in the `af_blue_stringset' array */ + /* we walk over the blue character strings as specified in the */ + /* style's entry in the `af_blue_stringset' array */ FT_TRACE5(( "latin blue zones computation\n" "============================\n" @@ -249,6 +306,14 @@ have_flag = 1; } + if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) + { + if ( have_flag ) + FT_TRACE5(( ", " )); + FT_TRACE5(( "neutral" )); + have_flag = 1; + } + if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) ) { if ( have_flag ) @@ -277,7 +342,8 @@ while ( *p ) { FT_ULong ch; - FT_UInt glyph_index; + FT_ULong glyph_index; + FT_Long y_offset; FT_Pos best_y; /* same as points.y */ FT_Int best_point, best_contour_first, best_contour_last; FT_Vector* points; @@ -287,7 +353,7 @@ GET_UTF8_CHAR( ch, p ); /* load the character in the face -- skip unknown or empty ones */ - glyph_index = FT_Get_Char_Index( face, ch ); + af_get_char_index( &metrics->root, ch, &glyph_index, &y_offset ); if ( glyph_index == 0 ) { FT_TRACE5(( " U+%04lX unavailable\n", ch )); @@ -470,6 +536,13 @@ FT_Int last; FT_Bool hit; + /* we intentionally declare these two variables */ + /* outside of the loop since various compilers emit */ + /* incorrect warning messages otherwise, talking about */ + /* `possibly uninitialized variables' */ + FT_Int p_first = 0; /* make compiler happy */ + FT_Int p_last = 0; + FT_Bool left2right; @@ -502,7 +575,6 @@ { FT_Bool l2r; FT_Pos d; - FT_Int p_first, p_last; if ( !hit ) @@ -575,7 +647,10 @@ if ( FT_ABS( points[next].x - points[first].x ) <= 20 * dist ) { - last--; + if ( last > best_contour_first ) + last--; + else + last = best_contour_last; break; } @@ -606,6 +681,12 @@ } } + /* for computing blue zones, we add the y offset as returned */ + /* by the currently used OpenType feature -- for example, */ + /* superscript glyphs might be identical to subscript glyphs */ + /* with a vertical shift */ + best_y += y_offset; + FT_TRACE5(( " U+%04lX: best_y = %5ld", ch, best_y )); /* now set the `round' flag depending on the segment's kind: */ @@ -629,6 +710,13 @@ FT_CURVE_TAG( outline.tags[best_segment_last] ) != FT_CURVE_TAG_ON ); + if ( round && AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) + { + /* only use flat segments for a neutral blue zone */ + FT_TRACE5(( " (round, skipped)\n" )); + continue; + } + FT_TRACE5(( " (%s)\n", round ? "round" : "flat" )); } @@ -699,6 +787,8 @@ blue->flags = 0; if ( AF_LATIN_IS_TOP_BLUE( bs ) ) blue->flags |= AF_LATIN_BLUE_TOP; + if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_NEUTRAL; /* * The following flag is used later to adjust the y and x scales @@ -733,10 +823,11 @@ /* digit `0' is 0x30 in all supported charmaps */ for ( i = 0x30; i <= 0x39; i++ ) { - FT_UInt glyph_index; + FT_ULong glyph_index; + FT_Long y_offset; - glyph_index = FT_Get_Char_Index( face, i ); + af_get_char_index( &metrics->root, i, &glyph_index, &y_offset ); if ( glyph_index == 0 ) continue; @@ -879,11 +970,11 @@ FT_TRACE5(( "af_latin_metrics_scale_dim:" - " x height alignment (script `%s'):\n" + " x height alignment (style `%s'):\n" " " " vertical scaling changed from %.4f to %.4f (by %d%%)\n" "\n", - af_script_names[metrics->root.script_class->script], + af_style_names[metrics->root.style_class->style], axis->org_scale / 65536.0, scale / 65536.0, ( fitted - scaled ) * 100 / scaled )); @@ -906,9 +997,9 @@ metrics->root.scaler.y_delta = delta; } - FT_TRACE5(( "%s widths (script `%s')\n", + FT_TRACE5(( "%s widths (style `%s')\n", dim == AF_DIMENSION_HORZ ? "horizontal" : "vertical", - af_script_names[metrics->root.script_class->script] )); + af_style_names[metrics->root.style_class->style] )); /* scale the widths */ for ( nn = 0; nn < axis->width_count; nn++ ) @@ -933,15 +1024,15 @@ #ifdef FT_DEBUG_LEVEL_TRACE if ( axis->extra_light ) - FT_TRACE5(( "`%s' script is extra light (at current resolution)\n" + FT_TRACE5(( "`%s' style is extra light (at current resolution)\n" "\n", - af_script_names[metrics->root.script_class->script] )); + af_style_names[metrics->root.style_class->style] )); #endif if ( dim == AF_DIMENSION_VERT ) { - FT_TRACE5(( "blue zones (script `%s')\n", - af_script_names[metrics->root.script_class->script] )); + FT_TRACE5(( "blue zones (style `%s')\n", + af_style_names[metrics->root.style_class->style] )); /* scale the blue zones */ for ( nn = 0; nn < axis->blue_count; nn++ ) @@ -1277,25 +1368,40 @@ } - /* Link segments to form stems and serifs. */ + /* Link segments to form stems and serifs. If `width_count' and */ + /* `widths' are non-zero, use them to fine-tune the scoring function. */ FT_LOCAL_DEF( void ) af_latin_hints_link_segments( AF_GlyphHints hints, + FT_UInt width_count, + AF_WidthRec* widths, AF_Dimension dim ) { AF_AxisHints axis = &hints->axis[dim]; AF_Segment segments = axis->segments; AF_Segment segment_limit = segments + axis->num_segments; - FT_Pos len_threshold, len_score; + FT_Pos len_threshold, len_score, dist_score, max_width; AF_Segment seg1, seg2; + if ( width_count ) + max_width = widths[width_count - 1].org; + else + max_width = 0; + + /* a heuristic value to set up a minimum value for overlapping */ len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 ); if ( len_threshold == 0 ) len_threshold = 1; + /* a heuristic value to weight lengths */ len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 ); + /* a heuristic value to weight distances (no call to */ + /* AF_LATIN_CONSTANT needed, since we work on multiples */ + /* of the stem width) */ + dist_score = 3000; + /* now compare each segment to the others */ for ( seg1 = segments; seg1 < segment_limit; seg1++ ) { @@ -1315,10 +1421,9 @@ if ( seg1->dir + seg2->dir == 0 && pos2 > pos1 ) { /* compute distance between the two segments */ - FT_Pos dist = pos2 - pos1; - FT_Pos min = seg1->min_coord; - FT_Pos max = seg1->max_coord; - FT_Pos len, score; + FT_Pos min = seg1->min_coord; + FT_Pos max = seg1->max_coord; + FT_Pos len; if ( min < seg2->min_coord ) @@ -1328,15 +1433,49 @@ max = seg2->max_coord; /* compute maximum coordinate difference of the two segments */ + /* (this is, how much they overlap) */ len = max - min; if ( len >= len_threshold ) { - /* small coordinate differences cause a higher score, and */ - /* segments with a greater distance cause a higher score also */ - score = dist + len_score / len; + /* + * The score is the sum of two demerits indicating the + * `badness' of a fit, measured along the segments' main axis + * and orthogonal to it, respectively. + * + * o The less overlapping along the main axis, the worse it + * is, causing a larger demerit. + * + * o The nearer the orthogonal distance to a stem width, the + * better it is, causing a smaller demerit. For simplicity, + * however, we only increase the demerit for values that + * exceed the largest stem width. + */ + + FT_Pos dist = pos2 - pos1; + + FT_Pos dist_demerit, score; + + + if ( max_width ) + { + /* distance demerits are based on multiples of `max_width'; */ + /* we scale by 1024 for getting more precision */ + FT_Pos delta = ( dist << 10 ) / max_width - ( 1 << 10 ); + + + if ( delta > 10000 ) + dist_demerit = 32000; + else if ( delta > 0 ) + dist_demerit = delta * delta / dist_score; + else + dist_demerit = 0; + } + else + dist_demerit = dist; /* default if no widths available */ + + score = dist_demerit + len_score / len; /* and we search for the smallest score */ - /* of the sum of the two values */ if ( score < seg1->score ) { seg1->score = score; @@ -1668,6 +1807,8 @@ FT_LOCAL_DEF( FT_Error ) af_latin_hints_detect_features( AF_GlyphHints hints, + FT_UInt width_count, + AF_WidthRec* widths, AF_Dimension dim ) { FT_Error error; @@ -1676,7 +1817,7 @@ error = af_latin_hints_compute_segments( hints, dim ); if ( !error ) { - af_latin_hints_link_segments( hints, dim ); + af_latin_hints_link_segments( hints, width_count, widths, dim ); error = af_latin_hints_compute_edges( hints, dim ); } @@ -1705,8 +1846,9 @@ for ( ; edge < edge_limit; edge++ ) { FT_UInt bb; - AF_Width best_blue = NULL; - FT_Pos best_dist; /* initial threshold */ + AF_Width best_blue = NULL; + FT_Bool best_blue_is_neutral = 0; + FT_Pos best_dist; /* initial threshold */ /* compute the initial threshold as a fraction of the EM size */ @@ -1720,24 +1862,26 @@ for ( bb = 0; bb < latin->blue_count; bb++ ) { AF_LatinBlue blue = latin->blues + bb; - FT_Bool is_top_blue, is_major_dir; + FT_Bool is_top_blue, is_neutral_blue, is_major_dir; /* skip inactive blue zones (i.e., those that are too large) */ if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) ) continue; - /* if it is a top zone, check for right edges -- if it is a bottom */ - /* zone, check for left edges */ - /* */ - /* of course, that's for TrueType */ - is_top_blue = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 ); - is_major_dir = FT_BOOL( edge->dir == axis->major_dir ); - - /* if it is a top zone, the edge must be against the major */ - /* direction; if it is a bottom zone, it must be in the major */ - /* direction */ - if ( is_top_blue ^ is_major_dir ) + /* if it is a top zone, check for right edges (against the major */ + /* direction); if it is a bottom zone, check for left edges (in */ + /* the major direction) -- this assumes the TrueType convention */ + /* for the orientation of contours */ + is_top_blue = + (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 ); + is_neutral_blue = + (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_NEUTRAL ) != 0); + is_major_dir = + FT_BOOL( edge->dir == axis->major_dir ); + + /* neutral blue zones are handled for both directions */ + if ( is_top_blue ^ is_major_dir || is_neutral_blue ) { FT_Pos dist; @@ -1750,15 +1894,19 @@ dist = FT_MulFix( dist, scale ); if ( dist < best_dist ) { - best_dist = dist; - best_blue = &blue->ref; + best_dist = dist; + best_blue = &blue->ref; + best_blue_is_neutral = is_neutral_blue; } /* now compare it to the overshoot position and check whether */ /* the edge is rounded, and whether the edge is over the */ /* reference position of a top zone, or under the reference */ - /* position of a bottom zone */ - if ( edge->flags & AF_EDGE_ROUND && dist != 0 ) + /* position of a bottom zone (provided we don't have a */ + /* neutral blue zone) */ + if ( edge->flags & AF_EDGE_ROUND && + dist != 0 && + !is_neutral_blue ) { FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org ); @@ -1772,8 +1920,9 @@ dist = FT_MulFix( dist, scale ); if ( dist < best_dist ) { - best_dist = dist; - best_blue = &blue->shoot; + best_dist = dist; + best_blue = &blue->shoot; + best_blue_is_neutral = is_neutral_blue; } } } @@ -1781,7 +1930,11 @@ } if ( best_blue ) + { edge->blue_edge = best_blue; + if ( best_blue_is_neutral ) + edge->flags |= AF_EDGE_NEUTRAL; + } } } @@ -1797,7 +1950,7 @@ FT_Face face = metrics->root.scaler.face; - af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics ); + af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics ); /* * correct x_scale and y_scale if needed, since they may have @@ -2099,7 +2252,7 @@ FT_TRACE5(( " LINK: edge %d (opos=%.2f) linked to %.2f," " dist was %.2f, now %.2f\n", - stem_edge-hints->axis[dim].edges, stem_edge->opos / 64.0, + stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0, stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 )); } @@ -2148,9 +2301,9 @@ #endif - FT_TRACE5(( "latin %s edge hinting (script `%s')\n", + FT_TRACE5(( "latin %s edge hinting (style `%s')\n", dim == AF_DIMENSION_VERT ? "horizontal" : "vertical", - af_script_names[hints->metrics->script_class->script] )); + af_style_names[hints->metrics->style_class->style] )); /* we begin by aligning all stems relative to the blue zone */ /* if needed -- that's only for horizontal edges */ @@ -2166,14 +2319,41 @@ if ( edge->flags & AF_EDGE_DONE ) continue; - blue = edge->blue_edge; edge1 = NULL; edge2 = edge->link; + /* + * If a stem contains both a neutral and a non-neutral blue zone, + * skip the neutral one. Otherwise, outlines with different + * directions might be incorrectly aligned at the same vertical + * position. + * + * If we have two neutral blue zones, skip one of them. + * + */ + if ( edge->blue_edge && edge2 && edge2->blue_edge ) + { + FT_Byte neutral = edge->flags & AF_EDGE_NEUTRAL; + FT_Byte neutral2 = edge2->flags & AF_EDGE_NEUTRAL; + + + if ( ( neutral && neutral2 ) || neutral2 ) + { + edge2->blue_edge = NULL; + edge2->flags &= ~AF_EDGE_NEUTRAL; + } + else if ( neutral ) + { + edge->blue_edge = NULL; + edge->flags &= ~AF_EDGE_NEUTRAL; + } + } + + blue = edge->blue_edge; if ( blue ) edge1 = edge; - /* flip edges if the other stem is aligned to a blue zone */ + /* flip edges if the other edge is aligned to a blue zone */ else if ( edge2 && edge2->blue_edge ) { blue = edge2->blue_edge; @@ -2240,7 +2420,7 @@ /* this should not happen, but it's better to be safe */ if ( edge2->blue_edge ) { - FT_TRACE5(( " ASSERTION FAILED for edge %d\n", edge2-edges )); + FT_TRACE5(( " ASSERTION FAILED for edge %d\n", edge2 - edges )); af_latin_align_linked_edge( hints, dim, edge2, edge ); edge->flags |= AF_EDGE_DONE; @@ -2629,6 +2809,8 @@ FT_Error error; int dim; + AF_LatinAxis axis; + error = af_glyph_hints_reload( hints, outline ); if ( error ) @@ -2642,14 +2824,22 @@ if ( AF_HINTS_DO_HORIZONTAL( hints ) ) #endif { - error = af_latin_hints_detect_features( hints, AF_DIMENSION_HORZ ); + axis = &metrics->axis[AF_DIMENSION_HORZ]; + error = af_latin_hints_detect_features( hints, + axis->width_count, + axis->widths, + AF_DIMENSION_HORZ ); if ( error ) goto Exit; } if ( AF_HINTS_DO_VERTICAL( hints ) ) { - error = af_latin_hints_detect_features( hints, AF_DIMENSION_VERT ); + axis = &metrics->axis[AF_DIMENSION_VERT]; + error = af_latin_hints_detect_features( hints, + axis->width_count, + axis->widths, + AF_DIMENSION_VERT ); if ( error ) goto Exit; @@ -2709,111 +2899,12 @@ sizeof ( AF_LatinMetricsRec ), - (AF_Script_InitMetricsFunc) af_latin_metrics_init, - (AF_Script_ScaleMetricsFunc)af_latin_metrics_scale, - (AF_Script_DoneMetricsFunc) NULL, - - (AF_Script_InitHintsFunc) af_latin_hints_init, - (AF_Script_ApplyHintsFunc) af_latin_hints_apply - ) - - - /* XXX: this should probably fine tuned to differentiate better between */ - /* scripts... */ - - static const AF_Script_UniRangeRec af_latn_uniranges[] = - { - AF_UNIRANGE_REC( 0x0020UL, 0x007FUL ), /* Basic Latin (no control chars) */ - AF_UNIRANGE_REC( 0x00A0UL, 0x00FFUL ), /* Latin-1 Supplement (no control chars) */ - AF_UNIRANGE_REC( 0x0100UL, 0x017FUL ), /* Latin Extended-A */ - AF_UNIRANGE_REC( 0x0180UL, 0x024FUL ), /* Latin Extended-B */ - AF_UNIRANGE_REC( 0x0250UL, 0x02AFUL ), /* IPA Extensions */ - AF_UNIRANGE_REC( 0x02B0UL, 0x02FFUL ), /* Spacing Modifier Letters */ - AF_UNIRANGE_REC( 0x0300UL, 0x036FUL ), /* Combining Diacritical Marks */ - AF_UNIRANGE_REC( 0x1D00UL, 0x1D7FUL ), /* Phonetic Extensions */ - AF_UNIRANGE_REC( 0x1D80UL, 0x1DBFUL ), /* Phonetic Extensions Supplement */ - AF_UNIRANGE_REC( 0x1DC0UL, 0x1DFFUL ), /* Combining Diacritical Marks Supplement */ - AF_UNIRANGE_REC( 0x1E00UL, 0x1EFFUL ), /* Latin Extended Additional */ - AF_UNIRANGE_REC( 0x2000UL, 0x206FUL ), /* General Punctuation */ - AF_UNIRANGE_REC( 0x2070UL, 0x209FUL ), /* Superscripts and Subscripts */ - AF_UNIRANGE_REC( 0x20A0UL, 0x20CFUL ), /* Currency Symbols */ - AF_UNIRANGE_REC( 0x2150UL, 0x218FUL ), /* Number Forms */ - AF_UNIRANGE_REC( 0x2460UL, 0x24FFUL ), /* Enclosed Alphanumerics */ - AF_UNIRANGE_REC( 0x2C60UL, 0x2C7FUL ), /* Latin Extended-C */ - AF_UNIRANGE_REC( 0x2E00UL, 0x2E7FUL ), /* Supplemental Punctuation */ - AF_UNIRANGE_REC( 0xA720UL, 0xA7FFUL ), /* Latin Extended-D */ - AF_UNIRANGE_REC( 0xFB00UL, 0xFB06UL ), /* Alphab. Present. Forms (Latin Ligs) */ - AF_UNIRANGE_REC( 0x1D400UL, 0x1D7FFUL ), /* Mathematical Alphanumeric Symbols */ - AF_UNIRANGE_REC( 0x1F100UL, 0x1F1FFUL ), /* Enclosed Alphanumeric Supplement */ - AF_UNIRANGE_REC( 0UL, 0UL ) - }; - - static const AF_Script_UniRangeRec af_grek_uniranges[] = - { - AF_UNIRANGE_REC( 0x0370UL, 0x03FFUL ), /* Greek and Coptic */ - AF_UNIRANGE_REC( 0x1F00UL, 0x1FFFUL ), /* Greek Extended */ - AF_UNIRANGE_REC( 0UL, 0UL ) - }; - - static const AF_Script_UniRangeRec af_cyrl_uniranges[] = - { - AF_UNIRANGE_REC( 0x0400UL, 0x04FFUL ), /* Cyrillic */ - AF_UNIRANGE_REC( 0x0500UL, 0x052FUL ), /* Cyrillic Supplement */ - AF_UNIRANGE_REC( 0x2DE0UL, 0x2DFFUL ), /* Cyrillic Extended-A */ - AF_UNIRANGE_REC( 0xA640UL, 0xA69FUL ), /* Cyrillic Extended-B */ - AF_UNIRANGE_REC( 0UL, 0UL ) - }; - - static const AF_Script_UniRangeRec af_hebr_uniranges[] = - { - AF_UNIRANGE_REC( 0x0590UL, 0x05FFUL ), /* Hebrew */ - AF_UNIRANGE_REC( 0xFB1DUL, 0xFB4FUL ), /* Alphab. Present. Forms (Hebrew) */ - AF_UNIRANGE_REC( 0UL, 0UL ) - }; - - - AF_DEFINE_SCRIPT_CLASS( - af_latn_script_class, - - AF_SCRIPT_LATN, - AF_BLUE_STRINGSET_LATN, - AF_WRITING_SYSTEM_LATIN, - - af_latn_uniranges, - 'o' - ) - - AF_DEFINE_SCRIPT_CLASS( - af_grek_script_class, - - AF_SCRIPT_GREK, - AF_BLUE_STRINGSET_GREK, - AF_WRITING_SYSTEM_LATIN, - - af_grek_uniranges, - 0x3BF /* ο */ - ) - - AF_DEFINE_SCRIPT_CLASS( - af_cyrl_script_class, - - AF_SCRIPT_CYRL, - AF_BLUE_STRINGSET_CYRL, - AF_WRITING_SYSTEM_LATIN, - - af_cyrl_uniranges, - 0x43E /* о */ - ) - - AF_DEFINE_SCRIPT_CLASS( - af_hebr_script_class, - - AF_SCRIPT_HEBR, - AF_BLUE_STRINGSET_HEBR, - AF_WRITING_SYSTEM_LATIN, + (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, + (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, + (AF_WritingSystem_DoneMetricsFunc) NULL, - af_hebr_uniranges, - 0x5DD /* ם */ + (AF_WritingSystem_InitHintsFunc) af_latin_hints_init, + (AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply ) |