Skip to content
Commits on Source (2)
......@@ -8,12 +8,20 @@
#undef FT_COMPONENT
#define FT_COMPONENT afadjust
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
#include <hb.h>
#include <hb-ot.h>
#endif
/*TODO: find out whether capital u/U with accent entries are needed*/
/*the accent won't merge with the rest of the glyph because the accent mark is sitting above empty space*/
/*
All entries in this list must be sorted by unicode codepoint ascending
*/
FT_LOCAL_ARRAY_DEF( AF_AdjustmentDatabaseEntry )
adjustment_database[] =
{
{0x21, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /* ! */
{0x21, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /* ! *
{0x69, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /* i */
{0x6A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /* j */
{0xA1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*Inverted Exclamation Mark*/
......@@ -130,6 +138,12 @@ adjustment_database[] =
{0x17E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}
};
FT_LOCAL_DEF( FT_Bool )
af_adjustment_database_entry_equals( const AF_AdjustmentDatabaseEntry* a, const AF_AdjustmentDatabaseEntry* b )
{
return a->codepoint == b->codepoint && a->vertical_separation_adjustment_type == b->vertical_separation_adjustment_type;
}
/*Helper function: get the adjustment database entry for a codepoint*/
FT_LOCAL_DEF( const AF_AdjustmentDatabaseEntry* )
af_adjustment_database_lookup( FT_UInt32 codepoint ) {
......@@ -187,12 +201,12 @@ typedef struct AF_ReverseMapEntry_
typedef struct AF_ReverseCharacterMap_
{
FT_UInt length;
FT_Long length;
AF_ReverseMapEntry *entries;
} AF_ReverseCharacterMap_Rec;
FT_LOCAL_DEF( FT_UInt32 )
af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index )
af_reverse_character_map_lookup_( AF_ReverseCharacterMap map, FT_Int glyph_index, FT_Long length )
{
if ( map == NULL )
{
......@@ -222,19 +236,59 @@ af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index
return 0;
}
FT_LOCAL_DEF( FT_UInt32 )
af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index )
{
return af_reverse_character_map_lookup_( map, glyph_index, map->length );
}
/*prepare to add one more entry to the reverse character map
this is a helper for af_reverse_character_map_new*/
FT_LOCAL_DEF( FT_Error )
af_reverse_character_map_expand( AF_ReverseCharacterMap map, FT_Long *capacity, FT_Memory memory )
{
FT_Error error;
if ( map->length < *capacity )
{
return FT_Err_Ok;
}
if ( map->length == *capacity )
{
FT_Long new_capacity = *capacity + *capacity / 2;
if ( FT_RENEW_ARRAY( map->entries, map->length, new_capacity ) ) {
return error;
}
*capacity = new_capacity;
}
return FT_Err_Ok;
}
/* qsort compare function for reverse character map */
FT_LOCAL_DEF( FT_Int )
af_reverse_character_map_entry_compare( const void *a, const void *b ) {
const AF_ReverseMapEntry entry_a = *((const AF_ReverseMapEntry *)a);
const AF_ReverseMapEntry entry_b = *((const AF_ReverseMapEntry *)b);
return entry_a.glyph_index < entry_b.glyph_index ? -1 : entry_a.glyph_index > entry_b.glyph_index ? 1 : 0;
}
FT_LOCAL_DEF( FT_Error )
af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memory memory )
af_reverse_character_map_new( AF_ReverseCharacterMap *map, AF_FaceGlobals globals )
{
FT_Face face = globals->face;
FT_Memory memory = face->memory;
/* Search for a unicode charmap */
/* If there isn't one, create a blank map */
/*TODO: use GSUB lookups */
FT_TRACE4(( "af_reverse_character_map_new: building reverse character map\n" ));
FT_Error error;
/* backup face->charmap because find_unicode_charmap sets it */
FT_CharMap old_charmap = face->charmap;
if (( error = find_unicode_charmap( face ) )) {
if ( ( error = find_unicode_charmap( face ) ) ) {
*map = NULL;
goto Exit;
}
......@@ -244,8 +298,8 @@ af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memo
goto Exit;
}
FT_Int capacity = 10;
FT_Int size = 0;
FT_Long capacity = 10;
( *map )->length = 0;
if ( FT_NEW_ARRAY( ( *map )->entries, capacity) )
{
......@@ -265,19 +319,117 @@ af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memo
#endif
continue;
}
if ( size == capacity )
error = af_reverse_character_map_expand( *map, &capacity, memory );
if ( error ) {
goto Exit;
}
( *map )->length++;
( *map )->entries[i].glyph_index = glyph;
( *map )->entries[i].codepoint = codepoint;
}
ft_qsort(
( *map )->entries,
( *map )->length,
sizeof( AF_ReverseMapEntry ),
af_reverse_character_map_entry_compare
);
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
hb_font_t *hb_font = globals->hb_font;
hb_face_t *hb_face = hb_font_get_face( hb_font );
hb_set_t *feature_indicies = hb_set_create();
FT_Long oldlength = ( *map )->length;
hb_ot_layout_collect_lookups(
hb_face,
HB_OT_TAG_GSUB,
NULL, /*all scripts*/
NULL, /*all languages*/
NULL, /*all features*/
feature_indicies
);
hb_codepoint_t feature_index = HB_SET_VALUE_INVALID;
FT_UInt added_entries = 0;
while ( hb_set_next(feature_indicies, &feature_index) )
{
hb_codepoint_t output_glyph_index;
/*TODO: find out whether I can reuse set instances instead of recreating*/
hb_set_t *glyphs_before = hb_set_create();
hb_set_t *glyphs_input = hb_set_create();
hb_set_t *glyphs_after = hb_set_create();
hb_set_t *glyphs_output = hb_set_create();
hb_ot_layout_lookup_collect_glyphs( hb_face, HB_OT_TAG_GSUB,
feature_index, glyphs_before,
glyphs_input, glyphs_after,
glyphs_output);
/*Don't consider anything involving context. Just do the
simple cases*/
if ( hb_set_get_population( glyphs_before ) > 0 ||
hb_set_get_population( glyphs_after ) > 0 )
{
continue;
}
if ( hb_set_get_population( glyphs_output ) != 1 )
{
capacity += capacity / 2;
if ( FT_RENEW_ARRAY((*map)->entries, size, capacity) )
continue;
}
hb_codepoint_t input_glyph_index = HB_SET_VALUE_INVALID;
const AF_AdjustmentDatabaseEntry* input_entry = NULL;
FT_UInt32 input_codepoint;
while ( hb_set_next( glyphs_input, &input_glyph_index ) ) {
input_codepoint = af_reverse_character_map_lookup_( *map, input_glyph_index, oldlength );
if ( input_codepoint == 0 )
{
continue;
}
const AF_AdjustmentDatabaseEntry* entry = af_adjustment_database_lookup( input_codepoint );
if ( entry == NULL )
{
goto Exit;
continue;
}
if ( input_entry == NULL )
{
input_entry = entry;
}
else
{
if ( !af_adjustment_database_entry_equals( input_entry, entry ) )
{
goto end;
}
}
}
size++;
( *map )->entries[i].glyph_index = glyph;
( *map )->entries[i].codepoint = codepoint;
output_glyph_index = HB_SET_VALUE_INVALID;
hb_set_next( glyphs_output, &output_glyph_index );
/*Make pair output glyph index -> input unicode*/
error = af_reverse_character_map_expand( *map, &capacity, memory );
if ( error ) {
goto Exit;
}
FT_Long index = ( *map )->length++;
( *map )->entries[index].glyph_index = output_glyph_index;
( *map )->entries[index].codepoint = input_codepoint;
FT_TRACE4(("Adding entry: %d -> %d\n", output_glyph_index, input_codepoint));
end: ;
}
( *map )->length = size;
ft_qsort(
( *map )->entries,
( *map )->length,
sizeof( AF_ReverseMapEntry ),
af_reverse_character_map_entry_compare
);
#endif /*FT_CONFIG_OPTION_USE_HARFBUZZ*/
Exit:
face->charmap = old_charmap;
......@@ -293,7 +445,7 @@ Exit:
}
#ifdef FT_DEBUG_LEVEL_TRACE
FT_TRACE4(( " reverse character map built successfully"\
" with %d entries and %d failed lookups.\n", size, failed_lookups ));
" with %d entries and %d failed lookups.\n", (*map)->length, failed_lookups ));
#endif
return FT_Err_Ok;
}
......
#ifndef AFADJUST_H_
#define AFADJUST_H_
#include <freetype/fttypes.h>
#include "aftypes.h"
#include "afglobal.h"
FT_BEGIN_HEADER
......@@ -28,10 +29,6 @@ typedef struct AF_AdjustmentDatabaseEntry_
AF_VerticalSeparationAdjustmentType vertical_separation_adjustment_type;
} AF_AdjustmentDatabaseEntry;
struct AF_ReverseCharacterMap_;
typedef struct AF_ReverseCharacterMap_ *AF_ReverseCharacterMap;
FT_LOCAL(AF_VerticalSeparationAdjustmentType)
af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index );
......@@ -43,7 +40,7 @@ af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index
/*allocate and populate the reverse character map, using the character map within the face*/
FT_LOCAL( FT_Error )
af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memory memory );
af_reverse_character_map_new( AF_ReverseCharacterMap *map, AF_FaceGlobals globals );
/*free the reverse character map*/
FT_LOCAL( FT_Error )
......
......@@ -1154,7 +1154,7 @@
}
af_latin_metrics_check_digits( metrics, face );
af_reverse_character_map_new( face, &metrics->root.reverse_charmap, face->memory );
af_reverse_character_map_new( &metrics->root.reverse_charmap, metrics->root.globals );
}
Exit:
......@@ -2799,6 +2799,87 @@ af_find_highest_contour( AF_GlyphHints hints ) {
return highest_contour;
}
static void
af_remove_segments_containing_point(AF_GlyphHints hints, AF_Point point)
{
AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT];
AF_Segment segments = axis->segments;
AF_Segment segment_limit = FT_OFFSET( segments, axis->num_segments );
for ( FT_Int i = 0; i < axis->num_segments; i++ )
{
AF_Segment seg = &segments[i];
FT_Bool remove = 0;
for ( AF_Point p = seg->first; p <= seg->last; p = p->next )
{
if ( p == point )
{
remove = 1;
break;
}
}
if ( remove )
{
FT_TRACE4(("Removing segment %d\n", i));
/* first, check the first and last fields of the edge */
AF_Edge edge = seg->edge;
if ( edge->first == seg && edge->last == seg )
{
/* The edge only consists of the segment to be removed. remove the edge*/
*edge = axis->edges[--axis->num_edges];
}
else
{
if ( edge->first == seg )
{
edge->first = seg->edge_next;
}
if ( edge->last == seg )
{
edge->last = edge->first;
while ( edge->last->edge_next != seg )
{
edge->last = edge->last->edge_next;
}
}
}
/* Now, delete the segment */
*seg = axis->segments[--axis->num_segments];
i--; /* we have to check the new segment at this position */
}
}
}
static void
af_latin_stretch_tildes_step_2( AF_GlyphHints hints,
FT_Int glyph_index,
AF_ReverseCharacterMap reverse_charmap )
{
if (af_lookup_tilde_correction_type(reverse_charmap, glyph_index)) {
FT_Int highest_contour = af_find_highest_contour(hints);
AF_Point first_point = hints->contours[highest_contour];
/* search for any curve tips that are on a y extrema, and delete any
segments that contain this point.*/
AF_Point p = first_point;
do
{
p = p->next;
if ( /*!(p->flags & AF_FLAG_CONTROL)
&& p->prev->y == p->y && p->next->y == p->y
&& p->prev->flags & AF_FLAG_CONTROL
&& p->next->flags & AF_FLAG_CONTROL*/ 1 )
{
af_remove_segments_containing_point( hints, p );
}
} while ( p != first_point );
}
}
void
af_latin_stretch_tildes( AF_GlyphHints hints,
FT_Int glyph_index,
......@@ -2814,82 +2895,99 @@ af_latin_stretch_tildes( AF_GlyphHints hints,
FT_Short min_fy, max_fy;
min_fy = max_fy = p->fy;
do {
do
{
p = p->next;
if ( p->y < min_y ) {
if ( p->y < min_y )
{
min_y = p->y;
}
if ( p->y > max_y ) {
if ( p->y > max_y )
{
max_y = p->y;
}
if ( p->fy < min_fy ) {
if ( p->fy < min_fy )
{
min_fy = p->fy;
}
if ( p->fy > max_fy ) {
if ( p->fy > max_fy )
{
max_fy = p->fy;
}
} while ( p != first_point );
}
while ( p != first_point );
FT_Pos min_measurement = 32000;
FT_UInt measurements_taken = 0;
do {
do
{
p = p->next;
if ( !(p->flags & AF_FLAG_CONTROL)
&& p->prev->y == p->y && p->next->y == p->y
&& p->y != min_y && p->y != max_y
&& p->prev->flags & AF_FLAG_CONTROL
&& p->next->flags & AF_FLAG_CONTROL ) {
&& p->next->flags & AF_FLAG_CONTROL )
{
/* This point could be a candidate. Find the next and previous on-curve */
/* points, and make sure they are both either above or below the point, */
/* Then make the measurement */
AF_Point prevOn = p->prev;
AF_Point nextOn = p->next;
while ( prevOn->flags & AF_FLAG_CONTROL ) {
while ( prevOn->flags & AF_FLAG_CONTROL )
{
prevOn = prevOn->prev;
}
while ( nextOn->flags & AF_FLAG_CONTROL ) {
while ( nextOn->flags & AF_FLAG_CONTROL )
{
nextOn = nextOn->next;
}
FT_Pos measurement;
if ( nextOn->y > p->y && prevOn->y > p->y ) {
if ( nextOn->y > p->y && prevOn->y > p->y )
{
measurement = p->y - min_y;
} else if ( nextOn->y < p->y && prevOn->y < p->y ) {
}
else if ( nextOn->y < p->y && prevOn->y < p->y )
{
measurement = max_y - p->y;
} else {
}
else
{
continue;
}
if (measurement < min_measurement) {
if (measurement < min_measurement)
{
min_measurement = measurement;
}
measurements_taken++;
}
} while ( p != first_point );
}
while ( p != first_point );
FT_Pos height = max_y - min_y;
FT_Pos target_height = min_measurement + 64;
if ( height >= target_height ) {
if ( height >= target_height )
{
return;
}
p = first_point;
do {
do
{
p = p->next;
/*if ( p->flags & AF_FLAG_CONTROL ) {
continue;
}*/
p->y = ((p->y - min_y) * target_height / height) + min_y;
p->fy = ((p->fy - min_fy) * target_height / height) + min_fy;
p->oy = p->y;
if ( !(p->flags & AF_FLAG_CONTROL) )
p->flags |= AF_FLAG_TOUCH_Y;
} while ( p != first_point );
}
while ( p != first_point );
FT_TRACE4(( "af_latin_stretch_tildes: Height: %d, measurement: %d, measurements taken: %d\n", height, min_measurement, measurements_taken ));
......@@ -2898,18 +2996,73 @@ af_latin_stretch_tildes( AF_GlyphHints hints,
p = first_point;
do {
p = p->next;
if ( p->y < new_min_y ) {
if ( p->y < new_min_y )
{
new_min_y = p->y;
}
if ( p->y > new_max_y ) {
if ( p->y > new_max_y )
{
new_max_y = p->y;
}
} while ( p != first_point );
}
while ( p != first_point );
FT_TRACE4(( "af_latin_stretch_tildes: New height: %d\n, miny: %d, maxy: %d", new_max_y - new_min_y, new_min_y, new_max_y));
}
}
/*True if the given contour overlaps horizontally with the bounding box
Of all other contours combined.
This is a helper for af_glyph_hints_apply_vertical_separation_adjustments */
FT_Bool
af_check_contour_horizontal_overlap( AF_GlyphHints hints,
FT_Int contour_index )
{
FT_Pos contour_max_x = -32000;
FT_Pos contour_min_x = 32000;
FT_Pos others_max_x = -32000;
FT_Pos others_min_x = 32000;
FT_TRACE4(( "af_latin_stretch_tildes_merp: New height: %d\n, miny: %d, maxy: %d", new_max_y - new_min_y, new_min_y, new_max_y));
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point first_point = hints->contours[contour];
AF_Point p = first_point;
do
{
p = p->next;
if ( contour == contour_index )
{
if ( p->x < contour_min_x )
{
contour_min_x = p->x;
}
if ( p->x > contour_max_x )
{
contour_max_x = p->x;
}
}
else /* ( contour != contour_index ) */
{
if ( p->x < others_min_x )
{
others_min_x = p->x;
}
if ( p->x > others_max_x )
{
others_max_x = p->x;
}
}
}
while (p != first_point);
}
FT_Bool horizontal_overlap =
(others_min_x <= contour_max_x && contour_max_x <= others_max_x) ||
(others_min_x <= contour_min_x && contour_min_x <= others_max_x) ||
(contour_max_x >= others_max_x && contour_min_x <= others_min_x);
return horizontal_overlap;
}
void
......@@ -2961,6 +3114,15 @@ af_glyph_hints_apply_vertical_separation_adjustments( AF_GlyphHints hints,
}
}
/* check for a horizontal overtap between the top contour and the rest */
/* if there is no overlap, do not adjust. */
FT_Bool horizontal_overlap = af_check_contour_horizontal_overlap( hints, highest_contour );
if (!horizontal_overlap) {
FT_TRACE4(( " Top contour does not horizontally overlap with other contours. Skipping adjustment.\n" ));
return;
}
/* If there are any contours that have a maximum y coordinate */
/* greater or equal to the minimum y coordinate of the previously found highest*/
/* contour, bump the high contour up until the distance is one pixel */
......@@ -2995,9 +3157,13 @@ af_glyph_hints_apply_vertical_separation_adjustments( AF_GlyphHints hints,
}
}
FT_TRACE4(( " Pushing top contour %d units up\n", adjustment_amount ));
if ( adjustment_amount > 0 )
if ( adjustment_amount > 64 )
{
FT_TRACE4(( " Calculated adjustment amount %d was more than threshold of 64. Not adjusting\n", adjustment_amount ));
}
else if ( adjustment_amount > 0 )
{
FT_TRACE4(( " Pushing top contour %d units up\n", adjustment_amount ));
af_move_contour_vertically(hints->contours[highest_contour], adjustment_amount);
}
} else if ( af_lookup_vertical_seperation_type( reverse_charmap, glyph_index ) == AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN
......@@ -3065,9 +3231,13 @@ af_glyph_hints_apply_vertical_separation_adjustments( AF_GlyphHints hints,
adjustment_amount = 64 - ( min_y - lowest_max_y );
}
FT_TRACE4(( " Pushing bottom contour %d units down\n", adjustment_amount ));
if ( adjustment_amount > 0 )
if ( adjustment_amount > 64 )
{
FT_TRACE4(( " Calculated adjustment amount %d was more than threshold of 64. Not adjusting\n", adjustment_amount ));
}
else if ( adjustment_amount > 0 )
{
FT_TRACE4(( " Pushing bottom contour %d units down\n", adjustment_amount ));
af_move_contour_vertically(hints->contours[lowest_contour], -adjustment_amount);
}
}
......@@ -3900,7 +4070,6 @@ static void traceheight(FT_UInt num, AF_GlyphHints hints) {
FT_Pos min_y, max_y;
min_y = max_y = p->y;
FT_UInt candidates = 0;
do {
p = p->next;
......@@ -3952,12 +4121,13 @@ static void traceheight(FT_UInt num, AF_GlyphHints hints) {
if ( AF_HINTS_DO_VERTICAL( hints ) )
{
af_latin_stretch_tildes( hints, glyph_index, metrics->root.reverse_charmap );
/*af_latin_stretch_tildes( hints, glyph_index, metrics->root.reverse_charmap );*/
axis = &metrics->axis[AF_DIMENSION_VERT];
error = af_latin_hints_detect_features( hints,
axis->width_count,
axis->widths,
AF_DIMENSION_VERT );
af_latin_stretch_tildes_step_2( hints, glyph_index, metrics->root.reverse_charmap );
if ( error )
goto Exit;
......
......@@ -39,7 +39,6 @@
#include <freetype/internal/ftdebug.h>
#include "afblue.h"
#include "afadjust.h"
#ifdef FT_DEBUG_AUTOFIT
#include FT_CONFIG_STANDARD_LIBRARY_H
......@@ -411,6 +410,10 @@ extern void* af_debug_hints_;
/* specific to writing systems derive their structures from it, for */
/* example `AF_LatinMetrics'. */
struct AF_ReverseCharacterMap_;
typedef struct AF_ReverseCharacterMap_ *AF_ReverseCharacterMap;
typedef struct AF_StyleMetricsRec_
{
AF_StyleClass style_class;
......