📄 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;
}
}
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_Vector* extremum;
FT_Vector* points;
FT_Vector* point_limit;
FT_Vector* point;
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;
point_limit = points + glyph->outline.n_points;
point = points;
extremum = point;
point++;
if ( AF_LATIN_IS_TOP_BLUE( bb ) )
{
for ( ; point < point_limit; point++ )
if ( point->y > extremum->y )
extremum = point;
}
else
{
for ( ; point < point_limit; point++ )
if ( point->y < extremum->y )
extremum = point;
}
AF_LOG(( "%5d", (int)extremum->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 see its previous and next points */
{
FT_Int idx = (FT_Int)( extremum - points );
FT_Int n;
FT_Int first, last, prev, next, end;
FT_Pos dist;
last = -1;
first = 0;
for ( n = 0; n < glyph->outline.n_contours; n++ )
{
end = glyph->outline.contours[n];
if ( end >= idx )
{
last = end;
break;
}
first = end + 1;
}
/* XXX: should never happen! */
if ( last < 0 )
continue;
/* now look for the previous and next points that are not on the */
/* same Y coordinate. Threshold the `closeness'... */
prev = idx;
next = prev;
do
{
if ( prev > first )
prev--;
else
prev = last;
dist = points[prev].y - extremum->y;
if ( dist < -5 || dist > 5 )
break;
} while ( prev != idx );
do
{
if ( next < last )
next++;
else
next = first;
dist = points[next].y - extremum->y;
if ( dist < -5 || dist > 5 )
break;
} while ( next != idx );
/* 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++] = extremum->y;
else
flats[num_flats++] = extremum->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 )
{
scale = scaler->x_scale;
delta = scaler->x_delta;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -