📄 aflatin.c
字号:
/***************************************************************************//* *//* aflatin.c *//* *//* Auto-fitter hinting routines for latin script (body). *//* *//* Copyright 2003, 2004, 2005, 2006, 2007 by *//* David Turner, Robert Wilhelm, and Werner Lemberg. *//* *//* This file is part of the FreeType project, and may only be used, *//* modified, and distributed under the terms of the FreeType project *//* license, LICENSE.TXT. By continuing to use, modify, or distribute *//* this file you indicate that you have read the license and *//* understand and accept it fully. *//* *//***************************************************************************/#include "aflatin.h"#include "aferrors.h"#ifdef AF_USE_WARPER#include "afwarp.h"#endif /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** L A T I N G L O B A L M E T R I C S *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ FT_LOCAL_DEF( void ) af_latin_metrics_init_widths( AF_LatinMetrics metrics, FT_Face face, FT_ULong charcode ) { /* scan the array of segments in each direction */ AF_GlyphHintsRec hints[1]; af_glyph_hints_init( hints, face->memory ); metrics->axis[AF_DIMENSION_HORZ].width_count = 0; metrics->axis[AF_DIMENSION_VERT].width_count = 0; { FT_Error error; FT_UInt glyph_index; int dim; AF_LatinMetricsRec dummy[1]; AF_Scaler scaler = &dummy->root.scaler; glyph_index = FT_Get_Char_Index( face, charcode ); if ( glyph_index == 0 ) goto Exit; error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); if ( error || face->glyph->outline.n_points <= 0 ) goto Exit; FT_ZERO( dummy ); dummy->units_per_em = metrics->units_per_em; scaler->x_scale = scaler->y_scale = 0x10000L; scaler->x_delta = scaler->y_delta = 0; scaler->face = face; scaler->render_mode = FT_RENDER_MODE_NORMAL; scaler->flags = 0; af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy ); error = af_glyph_hints_reload( hints, &face->glyph->outline ); if ( error ) goto Exit; for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) { AF_LatinAxis axis = &metrics->axis[dim]; AF_AxisHints axhints = &hints->axis[dim]; AF_Segment seg, limit, link; FT_UInt num_widths = 0; error = af_latin_hints_compute_segments( hints, (AF_Dimension)dim ); if ( error ) goto Exit; af_latin_hints_link_segments( hints, (AF_Dimension)dim ); seg = axhints->segments; limit = seg + axhints->num_segments; for ( ; seg < limit; seg++ ) { link = seg->link; /* we only consider stem segments there! */ if ( link && link->link == seg && link > seg ) { FT_Pos dist; dist = seg->pos - link->pos; if ( dist < 0 ) dist = -dist; if ( num_widths < AF_LATIN_MAX_WIDTHS ) axis->widths[ num_widths++ ].org = dist; } } af_sort_widths( num_widths, axis->widths ); axis->width_count = num_widths; } Exit: for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) { AF_LatinAxis axis = &metrics->axis[dim]; FT_Pos stdw; stdw = ( axis->width_count > 0 ) ? axis->widths[0].org : AF_LATIN_CONSTANT( metrics, 50 ); /* let's try 20% of the smallest width */ axis->edge_distance_threshold = stdw / 5; axis->standard_width = stdw; axis->extra_light = 0; } } af_glyph_hints_done( hints ); }#define AF_LATIN_MAX_TEST_CHARACTERS 12 static const char* const af_latin_blue_chars[AF_LATIN_MAX_BLUES] = { "THEZOCQS", "HEZLOCUS", "fijkdbh", "xzroesc", "xzroesc", "pqgjy" }; static void af_latin_metrics_init_blues( AF_LatinMetrics metrics, FT_Face face ) { FT_Pos flats [AF_LATIN_MAX_TEST_CHARACTERS]; FT_Pos rounds[AF_LATIN_MAX_TEST_CHARACTERS]; FT_Int num_flats; FT_Int num_rounds; FT_Int bb; AF_LatinBlue blue; FT_Error error; AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT]; FT_GlyphSlot glyph = face->glyph; /* we compute the blues simply by loading each character from the */ /* 'af_latin_blue_chars[blues]' string, then compute its top-most or */ /* bottom-most points (depending on `AF_IS_TOP_BLUE') */ AF_LOG(( "blue zones computation\n" )); AF_LOG(( "------------------------------------------------\n" )); for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ ) { const char* p = af_latin_blue_chars[bb]; const char* limit = p + AF_LATIN_MAX_TEST_CHARACTERS; FT_Pos* blue_ref; FT_Pos* blue_shoot; AF_LOG(( "blue %3d: ", bb )); num_flats = 0; num_rounds = 0; for ( ; p < limit && *p; p++ ) { FT_UInt glyph_index; FT_Int best_point, best_y, best_first, best_last; FT_Vector* points; FT_Bool round; AF_LOG(( "'%c'", *p )); /* load the character in the face -- skip unknown or empty ones */ glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p ); if ( glyph_index == 0 ) continue; error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); if ( error || glyph->outline.n_points <= 0 ) continue; /* now compute min or max point indices and coordinates */ points = glyph->outline.points; best_point = -1; best_y = 0; /* make compiler happy */ best_first = 0; /* ditto */ best_last = 0; /* ditto */ { FT_Int nn; FT_Int first = 0; FT_Int last = -1; for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ ) { FT_Int old_best_point = best_point; FT_Int pp; last = glyph->outline.contours[nn]; /* Avoid single-point contours since they are never rasterized. */ /* In some fonts, they correspond to mark attachment points */ /* which are way outside of the glyph's real outline. */ if ( last <= first ) continue; if ( AF_LATIN_IS_TOP_BLUE( bb ) ) { for ( pp = first; pp <= last; pp++ ) if ( best_point < 0 || points[pp].y > best_y ) { best_point = pp; best_y = points[pp].y; } } else { for ( pp = first; pp <= last; pp++ ) if ( best_point < 0 || points[pp].y < best_y ) { best_point = pp; best_y = points[pp].y; } } if ( best_point != old_best_point ) { best_first = first; best_last = last; } } AF_LOG(( "%5d", best_y )); } /* now check whether the point belongs to a straight or round */ /* segment; we first need to find in which contour the extremum */ /* lies, then inspect its previous and next points */ { FT_Int prev, next; FT_Pos dist; /* now look for the previous and next points that are not on the */ /* same Y coordinate. Threshold the `closeness'... */ prev = best_point; next = prev; do { if ( prev > best_first ) prev--; else prev = best_last; dist = points[prev].y - best_y; if ( dist < -5 || dist > 5 ) break; } while ( prev != best_point ); do { if ( next < best_last ) next++; else next = best_first; dist = points[next].y - best_y; if ( dist < -5 || dist > 5 ) break; } while ( next != best_point ); /* now, set the `round' flag depending on the segment's kind */ round = FT_BOOL( FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_CURVE_TAG_ON || FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_CURVE_TAG_ON ); AF_LOG(( "%c ", round ? 'r' : 'f' )); } if ( round ) rounds[num_rounds++] = best_y; else flats[num_flats++] = best_y; } AF_LOG(( "\n" )); if ( num_flats == 0 && num_rounds == 0 ) { /* * we couldn't find a single glyph to compute this blue zone, * we will simply ignore it then */ AF_LOG(( "empty!\n" )); continue; } /* we have computed the contents of the `rounds' and `flats' tables, */ /* now determine the reference and overshoot position of the blue -- */ /* we simply take the median value after a simple sort */ af_sort_pos( num_rounds, rounds ); af_sort_pos( num_flats, flats ); blue = & axis->blues[axis->blue_count]; blue_ref = & blue->ref.org; blue_shoot = & blue->shoot.org; axis->blue_count++; if ( num_flats == 0 ) { *blue_ref = *blue_shoot = rounds[num_rounds / 2]; } else if ( num_rounds == 0 ) { *blue_ref = *blue_shoot = flats[num_flats / 2]; } else { *blue_ref = flats[num_flats / 2]; *blue_shoot = rounds[num_rounds / 2]; } /* there are sometimes problems: if the overshoot position of top */ /* zones is under its reference position, or the opposite for bottom */ /* zones. We must thus check everything there and correct the errors */ if ( *blue_shoot != *blue_ref ) { FT_Pos ref = *blue_ref; FT_Pos shoot = *blue_shoot; FT_Bool over_ref = FT_BOOL( shoot > ref ); if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref ) *blue_shoot = *blue_ref = ( shoot + ref ) / 2; } blue->flags = 0; if ( AF_LATIN_IS_TOP_BLUE( bb ) ) blue->flags |= AF_LATIN_BLUE_TOP; /* * The following flags is used later to adjust the y and x scales * in order to optimize the pixel grid alignment of the top of small * letters. */ if ( bb == AF_LATIN_BLUE_SMALL_TOP ) blue->flags |= AF_LATIN_BLUE_ADJUSTMENT; AF_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot )); } return; } FT_LOCAL_DEF( FT_Error ) af_latin_metrics_init( AF_LatinMetrics metrics, FT_Face face ) { FT_Error error = AF_Err_Ok; FT_CharMap oldmap = face->charmap; FT_UInt ee; static const FT_Encoding latin_encodings[] = { FT_ENCODING_UNICODE, FT_ENCODING_APPLE_ROMAN, FT_ENCODING_ADOBE_STANDARD, FT_ENCODING_ADOBE_LATIN_1, FT_ENCODING_NONE /* end of list */ }; metrics->units_per_em = face->units_per_EM; /* do we have a latin charmap in there? */ for ( ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++ ) { error = FT_Select_Charmap( face, latin_encodings[ee] ); if ( !error ) break; } if ( !error ) { /* For now, compute the standard width and height from the `o'. */ af_latin_metrics_init_widths( metrics, face, 'o' ); af_latin_metrics_init_blues( metrics, face ); } FT_Set_Charmap( face, oldmap ); return AF_Err_Ok; } static void af_latin_metrics_scale_dim( AF_LatinMetrics metrics, AF_Scaler scaler, AF_Dimension dim ) { FT_Fixed scale; FT_Pos delta; AF_LatinAxis axis; FT_UInt nn; if ( dim == AF_DIMENSION_HORZ )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -