freetype2/src/autofit/afcjk.c

1513 lines
43 KiB
C
Raw Normal View History

/***************************************************************************/
/* */
/* afcjk.c */
/* */
/* Auto-fitter hinting routines for CJK script (body). */
/* */
/* Copyright 2006, 2007, 2008, 2009 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. */
/* */
/***************************************************************************/
2006-02-10 23:15:22 +01:00
/*
* The algorithm is based on akito's autohint patch, available here:
*
* http://www.kde.gr.jp/~akito/patch/freetype2/
*
*/
#include "aftypes.h"
#include "aflatin.h"
#ifdef AF_CONFIG_OPTION_CJK
#include "afcjk.h"
#include "aferrors.h"
#ifdef AF_USE_WARPER
#include "afwarp.h"
#endif
/*************************************************************************/
/*************************************************************************/
/***** *****/
2006-02-10 23:15:22 +01:00
/***** C J K G L O B A L M E T R I C S *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
FT_LOCAL_DEF( FT_Error )
af_cjk_metrics_init( AF_LatinMetrics metrics,
FT_Face face )
{
FT_CharMap oldmap = face->charmap;
metrics->units_per_em = face->units_per_EM;
/* TODO are there blues? */
if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
face->charmap = NULL;
else
{
/* latin's version would suffice */
af_latin_metrics_init_widths( metrics, face, 0x7530 );
af_latin_metrics_check_digits( metrics, face );
}
FT_Set_Charmap( face, oldmap );
return AF_Err_Ok;
}
static void
af_cjk_metrics_scale_dim( AF_LatinMetrics metrics,
AF_Scaler scaler,
AF_Dimension dim )
{
AF_LatinAxis axis;
axis = &metrics->axis[dim];
if ( dim == AF_DIMENSION_HORZ )
{
axis->scale = scaler->x_scale;
axis->delta = scaler->x_delta;
}
else
{
axis->scale = scaler->y_scale;
axis->delta = scaler->y_delta;
}
}
FT_LOCAL_DEF( void )
af_cjk_metrics_scale( AF_LatinMetrics metrics,
AF_Scaler scaler )
{
metrics->root.scaler = *scaler;
af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
2006-02-10 23:15:22 +01:00
/***** C J K G L Y P H A N A L Y S I S *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
static FT_Error
af_cjk_hints_compute_segments( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = &hints->axis[dim];
AF_Segment segments = axis->segments;
AF_Segment segment_limit = segments + axis->num_segments;
FT_Error error;
AF_Segment seg;
error = af_latin_hints_compute_segments( hints, dim );
if ( error )
return error;
/* a segment is round if it doesn't have successive */
/* on-curve points. */
for ( seg = segments; seg < segment_limit; seg++ )
{
AF_Point pt = seg->first;
AF_Point last = seg->last;
AF_Flags f0 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
AF_Flags f1;
seg->flags &= ~AF_EDGE_ROUND;
for ( ; pt != last; f0 = f1 )
{
pt = pt->next;
f1 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
if ( !f0 && !f1 )
break;
if ( pt == last )
seg->flags |= AF_EDGE_ROUND;
}
}
return AF_Err_Ok;
}
static void
af_cjk_hints_link_segments( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = &hints->axis[dim];
AF_Segment segments = axis->segments;
AF_Segment segment_limit = segments + axis->num_segments;
AF_Direction major_dir = axis->major_dir;
AF_Segment seg1, seg2;
FT_Pos len_threshold;
FT_Pos dist_threshold;
len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
2006-02-10 23:15:22 +01:00
dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
: hints->y_scale;
dist_threshold = FT_DivFix( 64 * 3, dist_threshold );
/* now compare each segment to the others */
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
{
/* the fake segments are for metrics hinting only */
if ( seg1->first == seg1->last )
continue;
if ( seg1->dir != major_dir )
continue;
for ( seg2 = segments; seg2 < segment_limit; seg2++ )
if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
{
FT_Pos dist = seg2->pos - seg1->pos;
if ( dist < 0 )
continue;
{
FT_Pos min = seg1->min_coord;
FT_Pos max = seg1->max_coord;
FT_Pos len;
if ( min < seg2->min_coord )
min = seg2->min_coord;
if ( max > seg2->max_coord )
max = seg2->max_coord;
len = max - min;
if ( len >= len_threshold )
{
2006-02-10 23:15:22 +01:00
if ( dist * 8 < seg1->score * 9 &&
( dist * 8 < seg1->score * 7 || seg1->len < len ) )
{
seg1->score = dist;
seg1->len = len;
seg1->link = seg2;
}
2006-02-10 23:15:22 +01:00
if ( dist * 8 < seg2->score * 9 &&
( dist * 8 < seg2->score * 7 || seg2->len < len ) )
{
seg2->score = dist;
seg2->len = len;
seg2->link = seg1;
}
}
}
}
}
/*
2006-02-10 23:15:22 +01:00
* now compute the `serif' segments
*
* In Hanzi, some strokes are wider on one or both of the ends.
* We either identify the stems on the ends as serifs or remove
* the linkage, depending on the length of the stems.
*
*/
{
AF_Segment link1, link2;
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
{
link1 = seg1->link;
if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos )
continue;
if ( seg1->score >= dist_threshold )
continue;
for ( seg2 = segments; seg2 < segment_limit; seg2++ )
{
if ( seg2->pos > seg1->pos || seg1 == seg2 )
continue;
link2 = seg2->link;
if ( !link2 || link2->link != seg2 || link2->pos < link1->pos )
continue;
if ( seg1->pos == seg2->pos && link1->pos == link2->pos )
continue;
if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score )
continue;
/* seg2 < seg1 < link1 < link2 */
if ( seg1->len >= seg2->len * 3 )
{
AF_Segment seg;
for ( seg = segments; seg < segment_limit; seg++ )
{
AF_Segment link = seg->link;
if ( link == seg2 )
{
seg->link = 0;
seg->serif = link1;
}
else if ( link == link2 )
{
seg->link = 0;
seg->serif = seg1;
}
}
}
else
{
seg1->link = link1->link = 0;
break;
}
}
}
}
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
{
seg2 = seg1->link;
if ( seg2 )
{
seg2->num_linked++;
if ( seg2->link != seg1 )
{
seg1->link = 0;
if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 )
seg1->serif = seg2->link;
else
seg2->num_linked--;
}
}
}
}
static FT_Error
af_cjk_hints_compute_edges( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = &hints->axis[dim];
FT_Error error = AF_Err_Ok;
FT_Memory memory = hints->memory;
AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
AF_Segment segments = axis->segments;
AF_Segment segment_limit = segments + axis->num_segments;
AF_Segment seg;
FT_Fixed scale;
FT_Pos edge_distance_threshold;
axis->num_edges = 0;
scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
: hints->y_scale;
/*********************************************************************/
/* */
2006-02-10 23:15:22 +01:00
/* We begin by generating a sorted table of edges for the current */
/* direction. To do so, we simply scan each segment and try to find */
/* an edge in our table that corresponds to its position. */
/* */
/* If no edge is found, we create and insert a new edge in the */
/* sorted table. Otherwise, we simply add the segment to the edge's */
2006-02-10 23:15:22 +01:00
/* list which is then processed in the second step to compute the */
/* edge's properties. */
/* */
/* Note that the edges table is sorted along the segment/edge */
/* position. */
/* */
/*********************************************************************/
edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
scale );
if ( edge_distance_threshold > 64 / 4 )
edge_distance_threshold = FT_DivFix( 64 / 4, scale );
else
edge_distance_threshold = laxis->edge_distance_threshold;
for ( seg = segments; seg < segment_limit; seg++ )
{
AF_Edge found = 0;
FT_Pos best = 0xFFFFU;
FT_Int ee;
/* look for an edge corresponding to the segment */
for ( ee = 0; ee < axis->num_edges; ee++ )
{
AF_Edge edge = axis->edges + ee;
FT_Pos dist;
if ( edge->dir != seg->dir )
continue;
dist = seg->pos - edge->fpos;
if ( dist < 0 )
dist = -dist;
if ( dist < edge_distance_threshold && dist < best )
{
AF_Segment link = seg->link;
2006-02-10 23:15:22 +01:00
/* check whether all linked segments of the candidate edge */
/* can make a single edge. */
if ( link )
{
AF_Segment seg1 = edge->first;
AF_Segment link1;
FT_Pos dist2 = 0;
do
{
link1 = seg1->link;
if ( link1 )
{
dist2 = AF_SEGMENT_DIST( link, link1 );
if ( dist2 >= edge_distance_threshold )
break;
}
} while ( ( seg1 = seg1->edge_next ) != edge->first );
if ( dist2 >= edge_distance_threshold )
continue;
}
2006-02-10 23:15:22 +01:00
best = dist;
found = edge;
}
}
if ( !found )
{
2006-02-10 23:15:22 +01:00
AF_Edge edge;
/* insert a new edge in the list and */
/* sort according to the position */
error = af_axis_hints_new_edge( axis, seg->pos,
(AF_Direction)seg->dir,
memory, &edge );
if ( error )
goto Exit;
/* add the segment to the new edge's list */
FT_ZERO( edge );
edge->first = seg;
edge->last = seg;
edge->fpos = seg->pos;
edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
seg->edge_next = seg;
edge->dir = seg->dir;
}
else
{
/* if an edge was found, simply add the segment to the edge's */
/* list */
seg->edge_next = found->first;
found->last->edge_next = seg;
found->last = seg;
}
}
/*********************************************************************/
/* */
2006-02-10 23:15:22 +01:00
/* Good, we now compute each edge's properties according to segments */
/* found on its position. Basically, these are as follows. */
/* */
/* - edge's main direction */
/* - stem edge, serif edge or both (which defaults to stem then) */
/* - rounded edge, straight or both (which defaults to straight) */
/* - link for edge */
/* */
/*********************************************************************/
2006-02-10 23:15:22 +01:00
/* first of all, set the `edge' field in each segment -- this is */
/* required in order to compute edge links */
/* */
/* Note that removing this loop and setting the `edge' field of each */
/* segment directly in the code above slows down execution speed for */
/* some reasons on platforms like the Sun. */
{
AF_Edge edges = axis->edges;
AF_Edge edge_limit = edges + axis->num_edges;
AF_Edge edge;
for ( edge = edges; edge < edge_limit; edge++ )
{
seg = edge->first;
if ( seg )
do
{
seg->edge = edge;
seg = seg->edge_next;
} while ( seg != edge->first );
}
2006-02-10 23:15:22 +01:00
/* now compute each edge properties */
for ( edge = edges; edge < edge_limit; edge++ )
{
FT_Int is_round = 0; /* does it contain round segments? */
FT_Int is_straight = 0; /* does it contain straight segments? */
seg = edge->first;
do
{
FT_Bool is_serif;
/* check for roundness of segment */
if ( seg->flags & AF_EDGE_ROUND )
is_round++;
else
is_straight++;
/* check for links -- if seg->serif is set, then seg->link must */
/* be ignored */
is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
if ( seg->link || is_serif )
{
AF_Edge edge2;
AF_Segment seg2;
edge2 = edge->link;
seg2 = seg->link;
if ( is_serif )
{
seg2 = seg->serif;
edge2 = edge->serif;
}
if ( edge2 )
{
FT_Pos edge_delta;
FT_Pos seg_delta;
edge_delta = edge->fpos - edge2->fpos;
if ( edge_delta < 0 )
edge_delta = -edge_delta;
seg_delta = AF_SEGMENT_DIST( seg, seg2 );
if ( seg_delta < edge_delta )
edge2 = seg2->edge;
}
else
edge2 = seg2->edge;
if ( is_serif )
{
edge->serif = edge2;
edge2->flags |= AF_EDGE_SERIF;
}
else
edge->link = edge2;
}
seg = seg->edge_next;
} while ( seg != edge->first );
/* set the round/straight flags */
edge->flags = AF_EDGE_NORMAL;
if ( is_round > 0 && is_round >= is_straight )
edge->flags |= AF_EDGE_ROUND;
2006-02-10 23:15:22 +01:00
/* get rid of serifs if link is set */
/* XXX: This gets rid of many unpleasant artefacts! */
/* Example: the `c' in cour.pfa at size 13 */
if ( edge->serif && edge->link )
edge->serif = 0;
}
}
Exit:
return error;
}
static FT_Error
af_cjk_hints_detect_features( AF_GlyphHints hints,
AF_Dimension dim )
{
FT_Error error;
error = af_cjk_hints_compute_segments( hints, dim );
if ( !error )
{
af_cjk_hints_link_segments( hints, dim );
error = af_cjk_hints_compute_edges( hints, dim );
}
return error;
}
FT_LOCAL_DEF( FT_Error )
af_cjk_hints_init( AF_GlyphHints hints,
AF_LatinMetrics metrics )
{
FT_Render_Mode mode;
FT_UInt32 scaler_flags, other_flags;
af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics );
/*
* correct x_scale and y_scale when needed, since they may have
* been modified af_cjk_scale_dim above
*/
hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
/* compute flags depending on render mode, etc. */
mode = metrics->root.scaler.render_mode;
#ifdef AF_USE_WARPER
if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
#endif
scaler_flags = hints->scaler_flags;
other_flags = 0;
/*
* We snap the width of vertical stems for the monochrome and
* horizontal LCD rendering targets only.
*/
if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
/*
* We snap the width of horizontal stems for the monochrome and
* vertical LCD rendering targets only.
*/
if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
other_flags |= AF_LATIN_HINTS_VERT_SNAP;
/*
* We adjust stems to full pixels only if we don't use the `light' mode.
*/
if ( mode != FT_RENDER_MODE_LIGHT )
other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
if ( mode == FT_RENDER_MODE_MONO )
other_flags |= AF_LATIN_HINTS_MONO;
scaler_flags |= AF_SCALER_FLAG_NO_ADVANCE;
hints->scaler_flags = scaler_flags;
hints->other_flags = other_flags;
return 0;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
2006-02-10 23:15:22 +01:00
/***** C J K G L Y P H G R I D - F I T T I N G *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* snap a given width in scaled coordinates to one of the */
/* current standard widths */
static FT_Pos
af_cjk_snap_width( AF_Width widths,
FT_Int count,
FT_Pos width )
{
int n;
FT_Pos best = 64 + 32 + 2;
FT_Pos reference = width;
FT_Pos scaled;
for ( n = 0; n < count; n++ )
{
FT_Pos w;
FT_Pos dist;
w = widths[n].cur;
dist = width - w;
if ( dist < 0 )
dist = -dist;
if ( dist < best )
{
best = dist;
reference = w;
}
}
scaled = FT_PIX_ROUND( reference );
if ( width >= reference )
{
if ( width < scaled + 48 )
width = reference;
}
else
{
if ( width > scaled - 48 )
width = reference;
}
return width;
}
/* compute the snapped width of a given stem */
static FT_Pos
af_cjk_compute_stem_width( AF_GlyphHints hints,
AF_Dimension dim,
FT_Pos width,
AF_Edge_Flags base_flags,
AF_Edge_Flags stem_flags )
{
AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics;
AF_LatinAxis axis = & metrics->axis[dim];
FT_Pos dist = width;
FT_Int sign = 0;
FT_Int vertical = ( dim == AF_DIMENSION_VERT );
FT_UNUSED( base_flags );
FT_UNUSED( stem_flags );
2006-02-10 23:15:22 +01:00
if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
return width;
if ( dist < 0 )
{
dist = -width;
sign = 1;
}
if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
{
/* smooth hinting process: very lightly quantize the stem width */
if ( axis->width_count > 0 )
{
if ( FT_ABS( dist - axis->widths[0].cur ) < 40 )
{
dist = axis->widths[0].cur;
if ( dist < 48 )
dist = 48;
goto Done_Width;
}
}
2006-02-10 23:15:22 +01:00
if ( dist < 54 )
dist += ( 54 - dist ) / 2 ;
else if ( dist < 3 * 64 )
{
FT_Pos delta;
delta = dist & 63;
dist &= -64;
if ( delta < 10 )
dist += delta;
else if ( delta < 22 )
dist += 10;
else if ( delta < 42 )
dist += delta;
else if ( delta < 54 )
dist += 54;
else
dist += delta;
}
}
else
{
/* strong hinting process: snap the stem width to integer pixels */
dist = af_cjk_snap_width( axis->widths, axis->width_count, dist );
if ( vertical )
{
/* in the case of vertical hinting, always round */
/* the stem heights to integer pixels */
if ( dist >= 64 )
dist = ( dist + 16 ) & ~63;
else
dist = 64;
}
else
{
if ( AF_LATIN_HINTS_DO_MONO( hints ) )
{
/* monochrome horizontal hinting: snap widths to integer pixels */
/* with a different threshold */
if ( dist < 64 )
dist = 64;
else
dist = ( dist + 32 ) & ~63;
}
else
{
/* for horizontal anti-aliased hinting, we adopt a more subtle */
/* approach: we strengthen small stems, round stems whose size */
/* is between 1 and 2 pixels to an integer, otherwise nothing */
if ( dist < 48 )
dist = ( dist + 64 ) >> 1;
else if ( dist < 128 )
dist = ( dist + 22 ) & ~63;
else
/* round otherwise to prevent color fringes in LCD mode */
dist = ( dist + 32 ) & ~63;
}
}
}
Done_Width:
if ( sign )
dist = -dist;
return dist;
}
/* align one stem edge relative to the previous stem edge */
static void
af_cjk_align_linked_edge( AF_GlyphHints hints,
AF_Dimension dim,
AF_Edge base_edge,
AF_Edge stem_edge )
{
FT_Pos dist = stem_edge->opos - base_edge->opos;
FT_Pos fitted_width = af_cjk_compute_stem_width(
hints, dim, dist,
(AF_Edge_Flags)base_edge->flags,
(AF_Edge_Flags)stem_edge->flags );
stem_edge->pos = base_edge->pos + fitted_width;
}
static void
af_cjk_align_serif_edge( AF_GlyphHints hints,
AF_Edge base,
AF_Edge serif )
{
FT_UNUSED( hints );
serif->pos = base->pos + ( serif->opos - base->opos );
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** E D G E H I N T I N G ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
2006-02-10 23:15:22 +01:00
#define AF_LIGHT_MODE_MAX_HORZ_GAP 9
#define AF_LIGHT_MODE_MAX_VERT_GAP 15
#define AF_LIGHT_MODE_MAX_DELTA_ABS 14
static FT_Pos
af_hint_normal_stem( AF_GlyphHints hints,
AF_Edge edge,
AF_Edge edge2,
FT_Pos anchor,
AF_Dimension dim )
{
2006-02-10 23:15:22 +01:00
FT_Pos org_len, cur_len, org_center;
FT_Pos cur_pos1, cur_pos2;
FT_Pos d_off1, u_off1, d_off2, u_off2, delta;
FT_Pos offset;
FT_Pos threshold = 64;
if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
{
if ( ( edge->flags & AF_EDGE_ROUND ) &&
2006-02-10 23:15:22 +01:00
( edge2->flags & AF_EDGE_ROUND ) )
{
if ( dim == AF_DIMENSION_VERT )
threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP;
else
threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP;
}
else
{
if ( dim == AF_DIMENSION_VERT )
2006-02-10 23:15:22 +01:00
threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP / 3;
else
2006-02-10 23:15:22 +01:00
threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP / 3;
}
}
org_len = edge2->opos - edge->opos;
cur_len = af_cjk_compute_stem_width( hints, dim, org_len,
(AF_Edge_Flags)edge->flags,
(AF_Edge_Flags)edge2->flags );
org_center = ( edge->opos + edge2->opos ) / 2 + anchor;
cur_pos1 = org_center - cur_len / 2;
cur_pos2 = cur_pos1 + cur_len;
d_off1 = cur_pos1 - FT_PIX_FLOOR( cur_pos1 );
d_off2 = cur_pos2 - FT_PIX_FLOOR( cur_pos2 );
u_off1 = 64 - d_off1;
u_off2 = 64 - d_off2;
delta = 0;
if ( d_off1 == 0 || d_off2 == 0 )
goto Exit;
if ( cur_len <= threshold )
{
if ( d_off2 < cur_len )
{
if ( u_off1 <= d_off2 )
delta = u_off1;
else
delta = -d_off2;
}
goto Exit;
}
if ( threshold < 64 )
{
if ( d_off1 >= threshold || u_off1 >= threshold ||
d_off2 >= threshold || u_off2 >= threshold )
goto Exit;
}
offset = cur_len % 64;
if ( offset < 32 )
{
if ( u_off1 <= offset || d_off2 <= offset )
goto Exit;
}
else
offset = 64 - threshold;
d_off1 = threshold - u_off1;
u_off1 = u_off1 - offset;
u_off2 = threshold - d_off2;
d_off2 = d_off2 - offset;
if ( d_off1 <= u_off1 )
u_off1 = -d_off1;
if ( d_off2 <= u_off2 )
u_off2 = -d_off2;
if ( FT_ABS( u_off1 ) <= FT_ABS( u_off2 ) )
delta = u_off1;
else
delta = u_off2;
Exit:
2006-02-10 23:15:22 +01:00
#if 1
if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
{
if ( delta > AF_LIGHT_MODE_MAX_DELTA_ABS )
delta = AF_LIGHT_MODE_MAX_DELTA_ABS;
else if ( delta < -AF_LIGHT_MODE_MAX_DELTA_ABS )
delta = -AF_LIGHT_MODE_MAX_DELTA_ABS;
}
#endif
cur_pos1 += delta;
if ( edge->opos < edge2->opos )
{
edge->pos = cur_pos1;
edge2->pos = cur_pos1 + cur_len;
}
else
{
edge->pos = cur_pos1 + cur_len;
edge2->pos = cur_pos1;
}
return delta;
}
2006-02-10 23:15:22 +01:00
static void
af_cjk_hint_edges( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = &hints->axis[dim];
AF_Edge edges = axis->edges;
AF_Edge edge_limit = edges + axis->num_edges;
FT_Int n_edges;
AF_Edge edge;
AF_Edge anchor = 0;
FT_Pos delta = 0;
FT_Int skipped = 0;
2006-02-10 23:15:22 +01:00
/* now we align all stem edges. */
for ( edge = edges; edge < edge_limit; edge++ )
{
AF_Edge edge2;
if ( edge->flags & AF_EDGE_DONE )
continue;
/* skip all non-stem edges */
edge2 = edge->link;
if ( !edge2 )
{
skipped++;
continue;
}
/* now align the stem */
if ( edge2 < edge )
{
af_cjk_align_linked_edge( hints, dim, edge2, edge );
edge->flags |= AF_EDGE_DONE;
continue;
}
if ( dim != AF_DIMENSION_VERT && !anchor )
{
2006-02-10 23:15:22 +01:00
#if 0
if ( fixedpitch )
{
AF_Edge left = edge;
AF_Edge right = edge_limit - 1;
AF_EdgeRec left1, left2, right1, right2;
FT_Pos target, center1, center2;
FT_Pos delta1, delta2, d1, d2;
while ( right > left && !right->link )
right--;
left1 = *left;
left2 = *left->link;
right1 = *right->link;
right2 = *right;
2006-02-10 23:15:22 +01:00
delta = ( ( ( hinter->pp2.x + 32 ) & -64 ) - hinter->pp2.x ) / 2;
target = left->opos + ( right->opos - left->opos ) / 2 + delta - 16;
delta1 = delta;
delta1 += af_hint_normal_stem( hints, left, left->link,
2006-02-10 23:15:22 +01:00
delta1, 0 );
if ( left->link != right )
af_hint_normal_stem( hints, right->link, right, delta1, 0 );
2006-02-10 23:15:22 +01:00
center1 = left->pos + ( right->pos - left->pos ) / 2;
if ( center1 >= target )
delta2 = delta - 32;
else
delta2 = delta + 32;
delta2 += af_hint_normal_stem( hints, &left1, &left2, delta2, 0 );
if ( delta1 != delta2 )
{
if ( left->link != right )
af_hint_normal_stem( hints, &right1, &right2, delta2, 0 );
2006-02-10 23:15:22 +01:00
center2 = left1.pos + ( right2.pos - left1.pos ) / 2;
d1 = center1 - target;
d2 = center2 - target;
if ( FT_ABS( d2 ) < FT_ABS( d1 ) )
{
left->pos = left1.pos;
left->link->pos = left2.pos;
if ( left->link != right )
{
right->link->pos = right1.pos;
right->pos = right2.pos;
}
delta1 = delta2;
}
}
delta = delta1;
right->link->flags |= AF_EDGE_DONE;
right->flags |= AF_EDGE_DONE;
}
else
2006-02-10 23:15:22 +01:00
#endif /* 0 */
delta = af_hint_normal_stem( hints, edge, edge2, 0,
AF_DIMENSION_HORZ );
}
else
af_hint_normal_stem( hints, edge, edge2, delta, dim );
#if 0
2006-02-10 23:15:22 +01:00
printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
edge - edges, edge2 - edges,
( edge->pos - edge->opos ) / 64.0,
( edge2->pos - edge2->opos ) / 64.0 );
#endif
anchor = edge;
edge->flags |= AF_EDGE_DONE;
edge2->flags |= AF_EDGE_DONE;
}
/* make sure that lowercase m's maintain their symmetry */
/* In general, lowercase m's have six vertical edges if they are sans */
/* serif, or twelve if they are with serifs. This implementation is */
/* based on that assumption, and seems to work very well with most */
/* faces. However, if for a certain face this assumption is not */
/* true, the m is just rendered like before. In addition, any stem */
/* correction will only be applied to symmetrical glyphs (even if the */
/* glyph is not an m), so the potential for unwanted distortion is */
/* relatively low. */
/* We don't handle horizontal edges since we can't easily assure that */
/* the third (lowest) stem aligns with the base line; it might end up */
/* one pixel higher or lower. */
n_edges = edge_limit - edges;
if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
{
AF_Edge edge1, edge2, edge3;
FT_Pos dist1, dist2, span;
if ( n_edges == 6 )
{
edge1 = edges;
edge2 = edges + 2;
edge3 = edges + 4;
}
else
{
edge1 = edges + 1;
edge2 = edges + 5;
edge3 = edges + 9;
}
dist1 = edge2->opos - edge1->opos;
dist2 = edge3->opos - edge2->opos;
span = dist1 - dist2;
if ( span < 0 )
span = -span;
if ( edge1->link == edge1 + 1 &&
edge2->link == edge2 + 1 &&
edge3->link == edge3 + 1 && span < 8 )
{
delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
edge3->pos -= delta;
if ( edge3->link )
edge3->link->pos -= delta;
/* move the serifs along with the stem */
if ( n_edges == 12 )
{
( edges + 8 )->pos -= delta;
( edges + 11 )->pos -= delta;
}
edge3->flags |= AF_EDGE_DONE;
if ( edge3->link )
edge3->link->flags |= AF_EDGE_DONE;
}
}
if ( !skipped )
return;
/*
* now hint the remaining edges (serifs and single) in order
* to complete our processing
*/
for ( edge = edges; edge < edge_limit; edge++ )
{
if ( edge->flags & AF_EDGE_DONE )
continue;
if ( edge->serif )
{
af_cjk_align_serif_edge( hints, edge->serif, edge );
edge->flags |= AF_EDGE_DONE;
skipped--;
}
}
if ( !skipped )
return;
for ( edge = edges; edge < edge_limit; edge++ )
{
AF_Edge before, after;
if ( edge->flags & AF_EDGE_DONE )
continue;
before = after = edge;
while ( --before >= edges )
if ( before->flags & AF_EDGE_DONE )
break;
while ( ++after < edge_limit )
if ( after->flags & AF_EDGE_DONE )
break;
if ( before >= edges || after < edge_limit )
{
if ( before < edges )
af_cjk_align_serif_edge( hints, after, edge );
else if ( after >= edge_limit )
af_cjk_align_serif_edge( hints, before, edge );
else
{
if ( after->fpos == before->fpos )
edge->pos = before->pos;
else
edge->pos = before->pos +
FT_MulDiv( edge->fpos - before->fpos,
after->pos - before->pos,
after->fpos - before->fpos );
}
}
}
}
static void
af_cjk_align_edge_points( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = & hints->axis[dim];
AF_Edge edges = axis->edges;
AF_Edge edge_limit = edges + axis->num_edges;
AF_Edge edge;
FT_Bool snapping;
snapping = FT_BOOL( ( dim == AF_DIMENSION_HORZ &&
AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ||
( dim == AF_DIMENSION_VERT &&
AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) );
for ( edge = edges; edge < edge_limit; edge++ )
{
/* move the points of each segment */
/* in each edge to the edge's position */
AF_Segment seg = edge->first;
if ( snapping )
{
do
{
AF_Point point = seg->first;
for (;;)
{
if ( dim == AF_DIMENSION_HORZ )
{
point->x = edge->pos;
point->flags |= AF_FLAG_TOUCH_X;
}
else
{
point->y = edge->pos;
point->flags |= AF_FLAG_TOUCH_Y;
}
if ( point == seg->last )
break;
point = point->next;
}
seg = seg->edge_next;
} while ( seg != edge->first );
}
else
{
FT_Pos delta = edge->pos - edge->opos;
do
{
AF_Point point = seg->first;
for (;;)
{
if ( dim == AF_DIMENSION_HORZ )
{
point->x += delta;
point->flags |= AF_FLAG_TOUCH_X;
}
else
{
point->y += delta;
point->flags |= AF_FLAG_TOUCH_Y;
}
if ( point == seg->last )
break;
point = point->next;
}
seg = seg->edge_next;
} while ( seg != edge->first );
}
}
}
FT_LOCAL_DEF( FT_Error )
af_cjk_hints_apply( AF_GlyphHints hints,
FT_Outline* outline,
AF_LatinMetrics metrics )
{
FT_Error error;
int dim;
FT_UNUSED( metrics );
2006-02-10 23:15:22 +01:00
error = af_glyph_hints_reload( hints, outline, 0 );
if ( error )
goto Exit;
/* analyze glyph outline */
if ( AF_HINTS_DO_HORIZONTAL( hints ) )
{
error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ );
if ( error )
goto Exit;
}
if ( AF_HINTS_DO_VERTICAL( hints ) )
{
error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT );
if ( error )
goto Exit;
}
/* grid-fit the outline */
for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
{
if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
{
2006-02-10 23:15:22 +01:00
#ifdef AF_USE_WARPER
2006-02-10 23:15:22 +01:00
if ( dim == AF_DIMENSION_HORZ &&
metrics->root.scaler.render_mode == FT_RENDER_MODE_NORMAL )
{
AF_WarperRec warper;
FT_Fixed scale;
FT_Pos delta;
af_warper_compute( &warper, hints, dim, &scale, &delta );
af_glyph_hints_scale_dim( hints, dim, scale, delta );
continue;
}
2006-02-10 23:15:22 +01:00
#endif /* AF_USE_WARPER */
af_cjk_hint_edges( hints, (AF_Dimension)dim );
af_cjk_align_edge_points( hints, (AF_Dimension)dim );
af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
}
}
#if 0
af_glyph_hints_dump_points( hints );
af_glyph_hints_dump_segments( hints );
af_glyph_hints_dump_edges( hints );
#endif
af_glyph_hints_save( hints, outline );
Exit:
return error;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
2006-02-10 23:15:22 +01:00
/***** C J K S C R I P T C L A S S *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
static const AF_Script_UniRangeRec af_cjk_uniranges[] =
{
#if 0
AF_UNIRANGE_REC( 0x0100UL, 0xFFFFUL ), /* why this? */
#endif
AF_UNIRANGE_REC( 0x2E80UL, 0x2EFFUL ), /* CJK Radicals Supplement */
AF_UNIRANGE_REC( 0x2F00UL, 0x2FDFUL ), /* Kangxi Radicals */
AF_UNIRANGE_REC( 0x3000UL, 0x303FUL ), /* CJK Symbols and Punctuation */
AF_UNIRANGE_REC( 0x3040UL, 0x309FUL ), /* Hiragana */
AF_UNIRANGE_REC( 0x30A0UL, 0x30FFUL ), /* Katakana */
AF_UNIRANGE_REC( 0x3100UL, 0x312FUL ), /* Bopomofo */
AF_UNIRANGE_REC( 0x3130UL, 0x318FUL ), /* Hangul Compatibility Jamo */
AF_UNIRANGE_REC( 0x31A0UL, 0x31BFUL ), /* Bopomofo Extended */
AF_UNIRANGE_REC( 0x31C0UL, 0x31EFUL ), /* CJK Strokes */
AF_UNIRANGE_REC( 0x31F0UL, 0x31FFUL ), /* Katakana Phonetic Extensions */
AF_UNIRANGE_REC( 0x3200UL, 0x32FFUL ), /* Enclosed CJK Letters and Months */
AF_UNIRANGE_REC( 0x3300UL, 0x33FFUL ), /* CJK Compatibility */
AF_UNIRANGE_REC( 0x3400UL, 0x4DBFUL ), /* CJK Unified Ideographs Extension A */
AF_UNIRANGE_REC( 0x4DC0UL, 0x4DFFUL ), /* Yijing Hexagram Symbols */
AF_UNIRANGE_REC( 0x4E00UL, 0x9FFFUL ), /* CJK Unified Ideographs */
AF_UNIRANGE_REC( 0xF900UL, 0xFAFFUL ), /* CJK Compatibility Ideographs */
AF_UNIRANGE_REC( 0xFE30UL, 0xFE4FUL ), /* CJK Compatibility Forms */
AF_UNIRANGE_REC( 0xFF00UL, 0xFFEFUL ), /* Halfwidth and Fullwidth Forms */
AF_UNIRANGE_REC( 0x20000UL, 0x2A6DFUL ), /* CJK Unified Ideographs Extension B */
AF_UNIRANGE_REC( 0x2F800UL, 0x2FA1FUL ), /* CJK Compatibility Ideographs Supplement */
AF_UNIRANGE_REC( 0UL, 0UL )
};
Position Independent Code (PIC) support in autofit module. * include/freetype/internal/autohint.h add macros to init instances of FT_AutoHinter_ServiceRec. * src/autofit/afmodule.h declare autofit_module_class using macros from ftmodapi.h, when FT_CONFIG_OPTION_PIC is defined create and destroy functions will be declared. * src/autofit/afmodule.c when FT_CONFIG_OPTION_PIC is defined af_autofitter_service and autofit_module_class structs will have functions to init or create and destroy them instead of being allocated in the global scope. And macros will be used from afpic.h in order to access them. * src/autofit/aftypes.h add macros to init and declare instances of AF_ScriptClassRec. * src/autofit/afcjk.h declare af_cjk_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afcjk.c when FT_CONFIG_OPTION_PIC is defined af_cjk_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afdummy.h declare af_dummy_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afdummy.c when FT_CONFIG_OPTION_PIC is defined af_dummy_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afindic.h declare af_indic_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afindic.c when FT_CONFIG_OPTION_PIC is defined af_indic_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/aflatin.h declare af_latin_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin.c when FT_CONFIG_OPTION_PIC is defined af_latin_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/aflatin2.h declare af_latin2_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin2.c when FT_CONFIG_OPTION_PIC is defined af_latin2_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin2_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/afglobal.c when FT_CONFIG_OPTION_PIC is defined af_script_classes array initialization was moved to afpic.c and is later refered using macros defeined in afpic.h. New Files: * src/autofit/afpic.h declare struct to hold PIC globals for autofit module and macros to access them. * src/autofit/afpic.c implement functions to allocate, destroy and initialize PIC globals for autofit module. * src/autofit/autofit.c add new file to build: afpic.c. * src/autofit/jamfile add new files to FT2_MULTI build: afpic.c.
2009-04-05 17:23:38 +02:00
AF_DEFINE_SCRIPT_CLASS(af_cjk_script_class,
AF_SCRIPT_CJK,
af_cjk_uniranges,
sizeof( AF_LatinMetricsRec ),
(AF_Script_InitMetricsFunc) af_cjk_metrics_init,
(AF_Script_ScaleMetricsFunc)af_cjk_metrics_scale,
(AF_Script_DoneMetricsFunc) NULL,
(AF_Script_InitHintsFunc) af_cjk_hints_init,
(AF_Script_ApplyHintsFunc) af_cjk_hints_apply
Position Independent Code (PIC) support in autofit module. * include/freetype/internal/autohint.h add macros to init instances of FT_AutoHinter_ServiceRec. * src/autofit/afmodule.h declare autofit_module_class using macros from ftmodapi.h, when FT_CONFIG_OPTION_PIC is defined create and destroy functions will be declared. * src/autofit/afmodule.c when FT_CONFIG_OPTION_PIC is defined af_autofitter_service and autofit_module_class structs will have functions to init or create and destroy them instead of being allocated in the global scope. And macros will be used from afpic.h in order to access them. * src/autofit/aftypes.h add macros to init and declare instances of AF_ScriptClassRec. * src/autofit/afcjk.h declare af_cjk_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afcjk.c when FT_CONFIG_OPTION_PIC is defined af_cjk_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afdummy.h declare af_dummy_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afdummy.c when FT_CONFIG_OPTION_PIC is defined af_dummy_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afindic.h declare af_indic_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afindic.c when FT_CONFIG_OPTION_PIC is defined af_indic_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/aflatin.h declare af_latin_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin.c when FT_CONFIG_OPTION_PIC is defined af_latin_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/aflatin2.h declare af_latin2_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin2.c when FT_CONFIG_OPTION_PIC is defined af_latin2_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin2_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/afglobal.c when FT_CONFIG_OPTION_PIC is defined af_script_classes array initialization was moved to afpic.c and is later refered using macros defeined in afpic.h. New Files: * src/autofit/afpic.h declare struct to hold PIC globals for autofit module and macros to access them. * src/autofit/afpic.c implement functions to allocate, destroy and initialize PIC globals for autofit module. * src/autofit/autofit.c add new file to build: afpic.c. * src/autofit/jamfile add new files to FT2_MULTI build: afpic.c.
2009-04-05 17:23:38 +02:00
)
#else /* !AF_CONFIG_OPTION_CJK */
static const AF_Script_UniRangeRec af_cjk_uniranges[] =
{
AF_UNIRANGE_REC( 0UL, 0UL )
};
Position Independent Code (PIC) support in autofit module. * include/freetype/internal/autohint.h add macros to init instances of FT_AutoHinter_ServiceRec. * src/autofit/afmodule.h declare autofit_module_class using macros from ftmodapi.h, when FT_CONFIG_OPTION_PIC is defined create and destroy functions will be declared. * src/autofit/afmodule.c when FT_CONFIG_OPTION_PIC is defined af_autofitter_service and autofit_module_class structs will have functions to init or create and destroy them instead of being allocated in the global scope. And macros will be used from afpic.h in order to access them. * src/autofit/aftypes.h add macros to init and declare instances of AF_ScriptClassRec. * src/autofit/afcjk.h declare af_cjk_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afcjk.c when FT_CONFIG_OPTION_PIC is defined af_cjk_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afdummy.h declare af_dummy_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afdummy.c when FT_CONFIG_OPTION_PIC is defined af_dummy_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afindic.h declare af_indic_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afindic.c when FT_CONFIG_OPTION_PIC is defined af_indic_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/aflatin.h declare af_latin_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin.c when FT_CONFIG_OPTION_PIC is defined af_latin_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/aflatin2.h declare af_latin2_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin2.c when FT_CONFIG_OPTION_PIC is defined af_latin2_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin2_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/afglobal.c when FT_CONFIG_OPTION_PIC is defined af_script_classes array initialization was moved to afpic.c and is later refered using macros defeined in afpic.h. New Files: * src/autofit/afpic.h declare struct to hold PIC globals for autofit module and macros to access them. * src/autofit/afpic.c implement functions to allocate, destroy and initialize PIC globals for autofit module. * src/autofit/autofit.c add new file to build: afpic.c. * src/autofit/jamfile add new files to FT2_MULTI build: afpic.c.
2009-04-05 17:23:38 +02:00
AF_DEFINE_SCRIPT_CLASS(af_cjk_script_class,
AF_SCRIPT_CJK,
af_cjk_uniranges,
sizeof( AF_LatinMetricsRec ),
(AF_Script_InitMetricsFunc) NULL,
(AF_Script_ScaleMetricsFunc)NULL,
(AF_Script_DoneMetricsFunc) NULL,
(AF_Script_InitHintsFunc) NULL,
(AF_Script_ApplyHintsFunc) NULL
Position Independent Code (PIC) support in autofit module. * include/freetype/internal/autohint.h add macros to init instances of FT_AutoHinter_ServiceRec. * src/autofit/afmodule.h declare autofit_module_class using macros from ftmodapi.h, when FT_CONFIG_OPTION_PIC is defined create and destroy functions will be declared. * src/autofit/afmodule.c when FT_CONFIG_OPTION_PIC is defined af_autofitter_service and autofit_module_class structs will have functions to init or create and destroy them instead of being allocated in the global scope. And macros will be used from afpic.h in order to access them. * src/autofit/aftypes.h add macros to init and declare instances of AF_ScriptClassRec. * src/autofit/afcjk.h declare af_cjk_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afcjk.c when FT_CONFIG_OPTION_PIC is defined af_cjk_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afdummy.h declare af_dummy_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afdummy.c when FT_CONFIG_OPTION_PIC is defined af_dummy_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/afindic.h declare af_indic_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/afindic.c when FT_CONFIG_OPTION_PIC is defined af_indic_script_class struct will have function to init it instead of being allocated in the global scope. * src/autofit/aflatin.h declare af_latin_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin.c when FT_CONFIG_OPTION_PIC is defined af_latin_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/aflatin2.h declare af_latin2_script_class using macros from aftypes.h, when FT_CONFIG_OPTION_PIC is defined init function will be declared. * src/autofit/aflatin2.c when FT_CONFIG_OPTION_PIC is defined af_latin2_script_class struct will have function to init it instead of being allocated in the global scope. Change af_latin2_blue_chars to be PIC-compatible by being a two dimentional array rather than array of pointers. * src/autofit/afglobal.c when FT_CONFIG_OPTION_PIC is defined af_script_classes array initialization was moved to afpic.c and is later refered using macros defeined in afpic.h. New Files: * src/autofit/afpic.h declare struct to hold PIC globals for autofit module and macros to access them. * src/autofit/afpic.c implement functions to allocate, destroy and initialize PIC globals for autofit module. * src/autofit/autofit.c add new file to build: afpic.c. * src/autofit/jamfile add new files to FT2_MULTI build: afpic.c.
2009-04-05 17:23:38 +02:00
)
#endif /* !AF_CONFIG_OPTION_CJK */
/* END */