Compare commits

...

16 Commits

Author SHA1 Message Date
Craig White
aeba770c07 add more comments 2023-10-27 01:51:31 -04:00
Craig White
99f56eed57 Add type 3 lookup handling 2023-10-27 01:30:32 -04:00
Craig White
23daa69048 Add tilde correction as an entry to the adjustment database. Add comment block about glyph variants algorithm 2023-10-15 01:12:42 -04:00
Craig White
2e5620a46f Fix memory leak in error handling logic 2023-09-26 23:00:10 -04:00
Craig White
e0f9830b00 Wrap up all glyph variant algorithm 2023-09-26 22:43:47 -04:00
Craig White
2c473abb1c WIP GSUB handling 2023-09-25 01:30:28 -04:00
Craig White
ed574779d7 Fix infinite loop bug in tilde fix, added then commented out a bunch of non-working GSUB handling 2023-09-16 19:05:10 -04:00
Craig White
75876af476 Remove testing changes 2023-08-26 23:13:49 -04:00
Craig White
86e5080e86 Add support for simple cases of GSUB lookups 2023-08-26 22:58:43 -04:00
Craig White
fa336db624 Improve validation for vertical seperation adjustments, and use new approach for tilde unflattening 2023-08-12 22:47:00 -04:00
Craig White
9a813d0c55 Add a vertical adjustment mode for pushing the bottom contour down and add a WIP tilde correction algorithm 2023-08-02 18:52:29 -04:00
Craig White
cfce1acb14 Fill in more of the database, fix bug in binary search 2023-07-04 13:10:18 -04:00
Craig White
6650b9ebc7 Make find_unicode_charmap public and use it to choose the best charmap for building the reverse character map 2023-06-28 21:03:22 -04:00
Craig White
37e3e348b5 * Make reverse charmap lookup a binary search
* Style fixes
2023-06-25 23:43:23 -04:00
Craig White
62378cca3a * Add trace output
* Remove assumption of exactly 2 contours
* Adjustment database lookups use binary search now
2023-06-25 23:24:02 -04:00
Craig White
f8e996bfb1 Prototype adjustment database, reverse character lookup, and fixes for a few characters 2023-06-04 15:31:13 -04:00
9 changed files with 1339 additions and 22 deletions

View File

@ -431,7 +431,7 @@ FT_BEGIN_HEADER
* them for certain configurations only.
*/
/* #define FT_DEBUG_LEVEL_ERROR */
/* #define FT_DEBUG_LEVEL_TRACE */
#define FT_DEBUG_LEVEL_TRACE
/**************************************************************************

View File

@ -275,6 +275,26 @@ FT_BEGIN_HEADER
FT_GlyphSlot slot,
FT_Render_Mode mode );
/**************************************************************************
*
* @Function:
* find_unicode_charmap
*
* @Description:
* This function finds a Unicode charmap, if there is one.
* And if there is more than one, it tries to favour the more
* extensive one, i.e., one that supports UCS-4 against those which
* are limited to the BMP ( UCS-2 encoding.)
*
* If a unicode charmap is found, face->charmap is set to it.
*
* This function is called from open_face(),
* from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ),
* and also from afadjust.c in the autofit module.
*/
FT_BASE( FT_Error )
find_unicode_charmap( FT_Face face );
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
typedef void (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap* bitmap,

View File

@ -164,6 +164,7 @@ FT_TRACE_DEF( afhints )
FT_TRACE_DEF( afmodule )
FT_TRACE_DEF( aflatin )
FT_TRACE_DEF( afshaper )
FT_TRACE_DEF( afadjust )
/* SDF components */
FT_TRACE_DEF( sdf ) /* signed distance raster for outlines (ftsdf.c) */

731
src/autofit/afadjust.c Normal file
View File

@ -0,0 +1,731 @@
#include "afadjust.h"
#include <freetype/freetype.h>
#include <freetype/internal/ftobjs.h>
#include <freetype/internal/ftmemory.h>
#include <freetype/internal/ftdebug.h>
#define AF_ADJUSTMENT_DATABASE_LENGTH ( sizeof(adjustment_database)/sizeof(adjustment_database[0]) )
#undef FT_COMPONENT
#define FT_COMPONENT afadjust
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
#include <hb.h>
#include <hb-ot.h>
#endif
/*
All entries in this list must be sorted by unicode codepoint ascending
The table entries are 3 numbers consisting of:
- unicode codepoint. THESE MUST BE LISTED IN ASCENDING ORDER
- the vertical adjustment type. One of the entries in AF_VerticalSeparationAdjustmentType
- 0 if the topmost contour is a tilde and should be prevented from flattening.
*/
FT_LOCAL_ARRAY_DEF( AF_AdjustmentDatabaseEntry )
adjustment_database[] =
{
{0x21, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* ! */
{0x69, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* i */
{0x6A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* j */
{0xA1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Exclamation Mark*/
{0xBF, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* j */
{0xA1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Exclamation Mark*/
{0xBF, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Question Mark*/
{0xC0, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with grave*/
{0xC1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with acute*/
{0xC2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with circumflex*/
{0xC3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with tilde*/
{0xC8, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*E with grave*/
{0xCC, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*I with grave*/
{0xCD, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xCE, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xD1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xD2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xD3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xD4, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xD5, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xD9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xD1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xDB, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xDD, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xE0, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xE1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xE2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xE3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*a with tilde*/
{0xE8, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xE9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xEA, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xEC, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xED, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xEE, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xF1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*n with tilde*/
{0xF2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xF3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xF4, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xF5, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*o with tilde*/
{0xF9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xFA, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xFB, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0xFD, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x100, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x101, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x102, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x103, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x106, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x108, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x109, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x10A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x10B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x10C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x10D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x10E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x112, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x113, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x114, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x115, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x116, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x117, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x11B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x11C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x11D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x11E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x11F, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x120, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x121, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x123, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x124, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x125, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x128, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x129, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*i with tilde*/
{0x12A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x12B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x12C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x12D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x12F, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x130, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x134, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x135, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x139, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x13A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x143, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x144, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x147, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x14C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x14D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x14E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x14F, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x154, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x155, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x158, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x159, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x15A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x15B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x15C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x15D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x160, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x161, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x164, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x168, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x169, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1}, /*u with tilde*/
{0x16A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x16B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x16C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x16D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x174, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x175, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x176, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x177, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x179, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x17A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x17B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x17C, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x17D, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0},
{0x17E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}
};
/*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 ) {
/* Binary search for database entry */
FT_Int low = 0;
FT_Int high = AF_ADJUSTMENT_DATABASE_LENGTH - 1;
while ( high >= low )
{
FT_Int mid = ( low + high ) / 2;
FT_UInt32 mid_codepoint = adjustment_database[mid].codepoint;
if ( mid_codepoint < codepoint )
{
low = mid + 1;
}
else if ( mid_codepoint > codepoint )
{
high = mid - 1;
}
else
{
return &adjustment_database[mid];
}
}
return NULL;
}
FT_LOCAL_DEF( AF_VerticalSeparationAdjustmentType )
af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
FT_UInt32 codepoint = af_reverse_character_map_lookup( map, glyph_index );
const AF_AdjustmentDatabaseEntry *entry = af_adjustment_database_lookup( codepoint );
if ( entry == NULL )
{
return AF_VERTICAL_ADJUSTMENT_NONE;
}
return entry->vertical_separation_adjustment_type;
}
/*1 if tilde correction should be applied to the topmost contour
else 0*/
FT_LOCAL_DEF( FT_Bool )
af_lookup_tilde_correction_type( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
FT_UInt32 codepoint = af_reverse_character_map_lookup( map, glyph_index );
const AF_AdjustmentDatabaseEntry *entry = af_adjustment_database_lookup( codepoint );
if ( entry == NULL )
{
return 0;
}
return entry->apply_tilde;
}
typedef struct AF_ReverseMapEntry_
{
FT_Int glyph_index;
FT_UInt32 codepoint;
} AF_ReverseMapEntry;
typedef struct AF_ReverseCharacterMap_
{
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, FT_Long length )
{
if ( map == NULL )
{
return 0;
}
/* Binary search for reverse character map entry */
FT_Int low = 0;
FT_Int high = length - 1;
while ( high >= low )
{
FT_Int mid = ( high + low ) / 2;
FT_Int mid_glyph_index = map->entries[mid].glyph_index;
if ( glyph_index < mid_glyph_index )
{
high = mid - 1;
}
else if ( glyph_index > mid_glyph_index )
{
low = mid + 1;
}
else
{
return map->entries[mid].codepoint;
}
}
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;
}
#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
/*
Recursive algorithm to find all glyphs that a codepoint could turn into from the GSUB table.
buffer: a buffer containing only the input codepoint
feature_tag_pool: the current list of features under consideration
current_features: the current list of features being applied
num_features: length of current_features
result: the set of glyphs that the input codepoint can map to.
The algorithm works by running the hb_ot_shape_glyphs_closure function on different lists of features
to see which features will map to glyph onto something different. This functions returns
the result of transforming a glyph using a list of features as well as all intermediate
forms if the glyph was transformed multiple times.
With no features enabled, hb_ot_shape_glyphs_closure will only return the glyph given by cmap.
This character will be the first to be placed into the results set.
Next, the algorithm will test the same lookup enabline one feature at a time
and see if any of those features change the result.
If any new glyph variants are found this way, they are added to the results set
and the algorithm will recurce, trying that feature in combination will every other feature
to look for further glyph variants.
example:
suppose we have the following features in the GSUB table:
f1:
a -> b
f2:
b -> c
f3:
d -> e
The algorithm will take the following steps to find all variants of "a":
- a is added to the results
- lookup with feature list {f1}, yielding {a, b}. b is added to the results list and the algorithm recurses
- - lookup with feature list {f1, f2}, yielding {a, b, c}. c is added to the results list and the algorithm recurses
- - - lookup with feature list {f1, f2, f3} yielding {a, b, c}. No new glyphs
- - lookup with feature list {f1, f3}, yielding {a, b}. No new glyphs
- lookup with feature list {f2}, yielding {a}. No new glyphs
- lookup with feature list {f3}, yielding {a}. No new glyphs
*/
FT_LOCAL_DEF( FT_Error )
af_all_glyph_variants_helper( hb_font_t *font,
hb_buffer_t *buffer,
hb_set_t *feature_tag_pool,
hb_feature_t *current_features,
FT_UInt32 num_features,
hb_set_t* result )
{
FT_Error error;
/*get the list of glyphs that are created by only transforming based on the
features in current_features*/
hb_set_t *baseline_glyphs = NULL, *new_glyphs = NULL;
baseline_glyphs = hb_set_create();
if ( !hb_set_allocation_successful( baseline_glyphs ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
hb_ot_shape_glyphs_closure ( font,
buffer,
current_features,
num_features,
baseline_glyphs );
if ( !hb_set_allocation_successful( baseline_glyphs ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
/*Add these baseline glyphs to the results. The baseline glyphs
will contain at minimum the glyph specified by CMAP*/
hb_set_union( result, baseline_glyphs );
if ( !hb_set_allocation_successful( result ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
if ( hb_set_get_population( feature_tag_pool ) == 0 )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
/*setup to try adding different features to current_features
to see if any of them have an effect of the glyphs we get from
hb_ot_shape_glyphs_closure*/
current_features[num_features].start = HB_FEATURE_GLOBAL_START;
current_features[num_features].end = HB_FEATURE_GLOBAL_END;
current_features[num_features].value = 1; /*set the feature to enabled*/
/*quote from docs about value attribute:
0 disables the feature, non-zero (usually 1) enables the feature. For features implemented as lookup type 3 (like 'salt') the value is a one based index into the alternates.
this algorithm does not handle these lookup type 3 cases fully*/
new_glyphs = hb_set_create();
if ( !hb_set_allocation_successful( new_glyphs ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
hb_tag_t feature_tag = HB_SET_VALUE_INVALID;
while ( hb_set_next( feature_tag_pool, &feature_tag ) )
{
hb_set_clear( new_glyphs );
current_features[num_features].tag = feature_tag;
hb_ot_shape_glyphs_closure ( font,
buffer,
current_features,
num_features + 1,
new_glyphs );
if ( !hb_set_allocation_successful( new_glyphs ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
hb_set_subtract( new_glyphs, result );
/*new_glyphs now contains all glyphs that appeared in the result
of hb_ot_shape_glyphs_closure that haven't already been accounted for in the result.
If this contains any glyphs, we also need to try this feature
in combination will other features by recursing
*/
if ( hb_set_get_population( new_glyphs ) != 0 )
{
/*remove this feature from the feature pool so that
the later recursion won't try it*/
hb_set_del( feature_tag_pool, feature_tag );
error = af_all_glyph_variants_helper( font,
buffer,
feature_tag_pool,
current_features,
num_features + 1,
result );
if ( error )
{
goto Exit;
}
/*add back the feature we removed*/
hb_set_add( feature_tag_pool, feature_tag );
if ( !hb_set_allocation_successful( feature_tag_pool ) ) {
return FT_Err_Out_Of_Memory;
}
} /* if( !hb_set_is_subset( glyphs, result ) ) */
} /*while ( hb_set_next( feature_tag_pool, &feature_tag ) )*/
Exit:
hb_set_destroy( baseline_glyphs );
hb_set_destroy( new_glyphs );
return FT_Err_Ok;
}
FT_LOCAL_DEF( FT_Error )
af_all_glyph_variants( FT_Face face,
hb_font_t *hb_font,
FT_UInt32 codepoint,
hb_set_t* result )
{
FT_Memory memory = face->memory;
FT_Error error;
hb_face_t *hb_face = hb_font_get_face( hb_font );
/*The set of all feature tags in the font*/
hb_set_t *feature_tags = hb_set_create();
hb_set_t *type_3_lookup_indicies = hb_set_create();
hb_buffer_t *codepoint_buffer = hb_buffer_create();
hb_codepoint_t *type_3_alternate_glyphs_buffer;
if ( !hb_set_allocation_successful( feature_tags ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
if ( !hb_buffer_allocation_successful( codepoint_buffer ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
if ( !hb_set_allocation_successful ( type_3_lookup_indicies ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
/*populate feature_tags using the output of hb_ot_layout_table_get_feature_tags*/
FT_Bool feature_list_done = 0;
unsigned int start_offset = 0;
while ( !feature_list_done )
{
unsigned int feature_count = 20;
hb_tag_t tags[20];
hb_ot_layout_table_get_feature_tags ( hb_face,
HB_OT_TAG_GSUB,
start_offset,
&feature_count,
tags );
start_offset += 20;
if ( feature_count < 20 )
{
feature_list_done = 1;
}
for ( int i = 0; i < feature_count; i++ )
{
hb_set_add( feature_tags, tags[i] );
}
if ( !hb_set_allocation_successful( feature_tags ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
}
/*make a buffer only consisting of the given codepoint*/
if ( !hb_buffer_pre_allocate( codepoint_buffer, 1 ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
hb_buffer_set_direction ( codepoint_buffer,
HB_DIRECTION_LTR );
hb_buffer_add( codepoint_buffer, codepoint, 0 );
/*The array of features that will be used by the recursive part
it will have at most as many entries as there are features, so
make the length = length of feature_tags*/
hb_feature_t *feature_buffer;
if (( error = FT_NEW_ARRAY( feature_buffer, hb_set_get_population( feature_tags ) ) ))
{
goto Exit;
}
error = af_all_glyph_variants_helper( hb_font,
codepoint_buffer,
feature_tags,
feature_buffer,
0,
result );
if ( error )
{
goto Exit;
}
/*
Add the alternative glyph forms that come from features using
type 3 lookups.
This file from gtk was very useful in figuring out my approach:
https://github.com/val-verde/gtk/blob/212e85e92628eda9f9650e5cc8d88c44062642ae/gtk/gtkfontchooserwidget.c#L2085
*/
/*list of features containing type 3 lookups:*/
hb_tag_t feature_list[] = {
HB_TAG('s','a','l','t'),
HB_TAG('s','w','s','h'),
HB_TAG('n','a','l','t'),
HB_TAG_NONE
};
hb_ot_layout_collect_lookups ( hb_face,
HB_OT_TAG_GSUB,
NULL,
NULL,
feature_list,
type_3_lookup_indicies);
if ( !hb_set_allocation_successful( type_3_lookup_indicies ) )
{
error = FT_Err_Out_Of_Memory;
goto Exit;
}
if (( error = FT_NEW_ARRAY( type_3_alternate_glyphs_buffer, 100 ) ))
{
goto Exit;
}
hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID;
FT_UInt base_glyph_index = FT_Get_Char_Index( face, codepoint );
if ( base_glyph_index != 0 ) {
while ( hb_set_next( type_3_lookup_indicies, &lookup_index ) )
{
unsigned alternate_count = 100;
hb_ot_layout_lookup_get_glyph_alternates
( hb_face,
lookup_index,
base_glyph_index,
0,
&alternate_count,
type_3_alternate_glyphs_buffer );
for ( unsigned i = 0; i < alternate_count; i++ )
{
hb_set_add( result, type_3_alternate_glyphs_buffer[i] );
}
}
}
Exit:
hb_set_destroy( feature_tags );
hb_buffer_destroy( codepoint_buffer );
FT_FREE( feature_buffer );
FT_FREE( type_3_alternate_glyphs_buffer );
return error;
}
#endif /*FT_CONFIG_OPTION_USE_HARFBUZZ*/
FT_LOCAL_DEF( FT_Error )
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 */
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 ) ) )
{
*map = NULL;
goto Exit;
}
if ( FT_NEW( *map ) )
{
goto Exit;
}
FT_Long capacity = 10;
( *map )->length = 0;
if ( FT_NEW_ARRAY( ( *map )->entries, capacity) )
{
goto Exit;
}
#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 *result_set = hb_set_create();
if ( !hb_set_allocation_successful( result_set ) )
{
error = FT_Err_Out_Of_Memory;
goto harfbuzz_path_Exit;
}
/*find alll glyph variants of the codepoints, then make an entry from
the glyph to the codepoint for each one*/
for ( FT_Long i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
{
FT_UInt32 codepoint = adjustment_database[i].codepoint;
error = af_all_glyph_variants( face,
hb_font,
codepoint,
result_set );
if ( error )
{
goto harfbuzz_path_Exit;
}
hb_codepoint_t glyph = HB_SET_VALUE_INVALID;
while ( hb_set_next( result_set, &glyph ) )
{
error = af_reverse_character_map_expand( *map, &capacity, memory );
if ( error ) {
goto harfbuzz_path_Exit;
}
FT_Long insert_point = ( *map )->length;
( *map )->length++;
( *map )->entries[insert_point].glyph_index = glyph;
( *map )->entries[insert_point].codepoint = codepoint;
}
hb_set_clear( result_set );
}
harfbuzz_path_Exit:
hb_set_destroy( result_set );
if ( error )
{
goto Exit;
}
#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
#ifdef FT_DEBUG_LEVEL_TRACE
int failed_lookups = 0;
#endif
for ( FT_Int i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
{
FT_UInt32 codepoint = adjustment_database[i].codepoint;
FT_Int glyph = FT_Get_Char_Index( face, codepoint );
if ( glyph == 0 )
{
#ifdef FT_DEBUG_LEVEL_TRACE
failed_lookups++;
#endif
continue;
}
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;
}
#endif /*FT_CONFIG_OPTION_USE_HARFBUZZ*/
ft_qsort(
( *map )->entries,
( *map )->length,
sizeof( AF_ReverseMapEntry ),
af_reverse_character_map_entry_compare
);
FT_TRACE4(( " reverse character map built successfully"\
" with %d entries\n", (*map)->length ));
Exit:
face->charmap = old_charmap;
if ( error )
{
FT_TRACE4(( " error while building reverse character map. Using blank map.\n" ));
if ( *map )
{
FT_FREE( ( *map )->entries );
}
FT_FREE( *map );
return error;
}
return FT_Err_Ok;
}
FT_LOCAL_DEF( FT_Error )
af_reverse_character_map_done( AF_ReverseCharacterMap map, FT_Memory memory ) {
FT_FREE( map->entries );
return FT_Err_Ok;
}

52
src/autofit/afadjust.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef AFADJUST_H_
#define AFADJUST_H_
#include <freetype/fttypes.h>
#include "aftypes.h"
#include "afglobal.h"
FT_BEGIN_HEADER
/*The type of adjustment that should be done to prevent cases where 2 parts of a character*/
/*stacked vertically merge, even though they should be separate*/
typedef enum AF_VerticalSeparationAdjustmentType_
{
/*This means that the hinter should find the topmost contour and push it up until its lowest point is 1 pixel*/
/*above the highest point not part of that contour.*/
AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP,
/*This is the opposite direction. The hinter should find the bottommost contour*/
/*And push it down until there is a 1 pixel gap*/
AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN,
AF_VERTICAL_ADJUSTMENT_NONE
/*others will be needed, such as umlats, where there are 2 contours which should be moved together*/
} AF_VerticalSeparationAdjustmentType;
typedef struct AF_AdjustmentDatabaseEntry_
{
FT_UInt32 codepoint;
AF_VerticalSeparationAdjustmentType vertical_separation_adjustment_type;
FT_Bool apply_tilde;
} AF_AdjustmentDatabaseEntry;
FT_LOCAL(AF_VerticalSeparationAdjustmentType)
af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index );
FT_LOCAL( FT_Bool )
af_lookup_tilde_correction_type( AF_ReverseCharacterMap map, FT_Int glyph_index );
FT_LOCAL( FT_UInt32 )
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( AF_ReverseCharacterMap *map, AF_FaceGlobals globals );
/*free the reverse character map*/
FT_LOCAL( FT_Error )
af_reverse_character_map_done( AF_ReverseCharacterMap map, FT_Memory memory );
FT_END_HEADER
#endif

View File

@ -22,6 +22,7 @@
#include "afglobal.h"
#include "aflatin.h"
#include "aferrors.h"
#include "afadjust.h"
/**************************************************************************
@ -227,7 +228,6 @@
dummy->units_per_em / 100 );
axis->width_count = num_widths;
}
Exit:
for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
{
@ -1153,6 +1153,8 @@
goto Exit;
}
af_latin_metrics_check_digits( metrics, face );
af_reverse_character_map_new( &metrics->root.reverse_charmap, metrics->root.globals );
}
Exit:
@ -1502,6 +1504,11 @@
af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
}
FT_CALLBACK_DEF( void )
af_latin_metrics_done( AF_StyleMetrics metrics_ ) {
AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
af_reverse_character_map_done( metrics->root.reverse_charmap, metrics->root.globals->face->memory );
}
/* Extract standard_width from writing system/script specific */
/* metrics class. */
@ -2738,6 +2745,516 @@
}
#undef FT_COMPONENT
#define FT_COMPONENT afadjust
static void
af_move_contour_vertically( AF_Point contour,
FT_Int movement )
{
AF_Point point = contour;
AF_Point first_point = point;
if ( point != NULL )
{
do
{
point->y += movement;
point = point->next;
} while ( point != first_point );
}
}
static FT_Int
af_find_highest_contour( AF_GlyphHints hints ) {
FT_Int highest_contour = -1;
FT_Pos highest_min_y = 0;
FT_Pos current_min_y = 0;
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point point = hints->contours[contour];
AF_Point first_point = point;
if ( point == NULL )
{
continue;
}
current_min_y = point->y;
do
{
if ( point->y < current_min_y )
{
current_min_y = point->y;
}
point = point->next;
} while ( point != first_point );
if ( highest_contour == -1 || current_min_y > highest_min_y )
{
highest_min_y = current_min_y;
highest_contour = contour;
}
}
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;
for ( FT_Int i = 0; i < axis->num_segments; i++ )
{
AF_Segment seg = &segments[i];
FT_Bool remove = 0;
AF_Point p = seg->first;
while ( 1 )
{
if ( p == point )
{
remove = 1;
break;
}
if (p == seg->last) {
break;
}
p = p->next;
}
if ( remove )
{
/* 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 */
}
}
}
/*remove all segments containing points on the tilde contour*/
static void
af_latin_remove_tilde_points_from_edges( AF_GlyphHints hints,
FT_Int 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 )
{
FT_TRACE4(("%p", p));
af_remove_segments_containing_point( hints, p );
}
} while ( p != first_point );
}
/*
The tilde unflatenning algorithm sometimes goes too far and makes an
unusually high tilde, where decreasing the ppem will increase the height
instead of a steady decrease in height as less pixels are used.
The n tilde on times new roman with forced autofitting on,
16.5-18 ppem font size exhibits this behaviour.
*/
void
af_latin_stretch_tildes( AF_GlyphHints hints,
FT_Int glyph_index )
{
FT_Int highest_contour = af_find_highest_contour( hints );
AF_Point p = hints->contours[highest_contour];
AF_Point first_point = p;
FT_Pos min_y, max_y;
min_y = max_y = p->y;
FT_Short min_fy, max_fy;
min_fy = max_fy = p->fy;
do
{
p = p->next;
if ( p->y < min_y )
{
min_y = p->y;
}
if ( p->y > max_y )
{
max_y = p->y;
}
if ( p->fy < min_fy )
{
min_fy = p->fy;
}
if ( p->fy > max_fy )
{
max_fy = p->fy;
}
}
while ( p != first_point );
FT_Pos min_measurement = 32000;
FT_UInt measurements_taken = 0;
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 )
{
/* 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 )
{
prevOn = prevOn->prev;
}
while ( nextOn->flags & AF_FLAG_CONTROL )
{
nextOn = nextOn->next;
}
FT_Pos measurement;
if ( nextOn->y > p->y && prevOn->y > p->y )
{
measurement = p->y - min_y;
}
else if ( nextOn->y < p->y && prevOn->y < p->y )
{
measurement = max_y - p->y;
}
else
{
continue;
}
if (measurement < min_measurement)
{
min_measurement = measurement;
}
measurements_taken++;
}
}
while ( p != first_point );
FT_Pos height = max_y - min_y;
FT_Pos target_height = min_measurement + 64;
if ( height >= target_height )
{
return;
}
p = first_point;
do
{
p = p->next;
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 );
FT_Pos new_min_y, new_max_y;
new_min_y = new_max_y = first_point->y;
p = first_point;
do {
p = p->next;
if ( p->y < new_min_y )
{
new_min_y = p->y;
}
if ( p->y > new_max_y )
{
new_max_y = p->y;
}
}
while ( p != first_point );
}
/*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;
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
af_glyph_hints_apply_vertical_separation_adjustments( AF_GlyphHints hints,
AF_Dimension dim,
FT_Int glyph_index,
AF_ReverseCharacterMap reverse_charmap )
{
if ( dim != AF_DIMENSION_VERT )
{
return;
}
if ( af_lookup_vertical_seperation_type( reverse_charmap, glyph_index ) == AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP
&& hints->num_contours >= 2 )
{
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: Applying vertical adjustment: AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP\n" ));
/* Figure out which contout is the higher one by finding the one */
/* with the highest minimum y value */
FT_Int highest_contour = -1;
FT_Pos highest_min_y = 0;
FT_Pos current_min_y = 0;
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point point = hints->contours[contour];
AF_Point first_point = point;
if ( point == NULL )
{
continue;
}
current_min_y = point->y;
do
{
if ( point->y < current_min_y )
{
current_min_y = point->y;
}
point = point->next;
} while ( point != first_point );
if ( highest_contour == -1 || current_min_y > highest_min_y )
{
highest_min_y = current_min_y;
highest_contour = contour;
}
}
/* 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 */
FT_Int adjustment_amount = 0;
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
{
if ( contour == highest_contour )
{
continue;
}
AF_Point point = hints->contours[contour];
AF_Point first_point = point;
if ( point == NULL )
{
continue;
}
FT_Pos max_y = point->y;
do
{
if ( point->y > max_y )
{
max_y = point->y;
}
point = point->next;
} while ( point != first_point );
if ( max_y >= highest_min_y - 64 )
{
adjustment_amount = 64 - ( highest_min_y - max_y );
}
}
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
&& hints->num_contours >= 2 )
{
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: Applying vertical adjustment: AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN\n" ));
/*Find lowest contour*/
FT_Int lowest_contour = -1;
FT_Pos lowest_max_y = 0;
FT_Pos current_max_y = 0;
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point point = hints->contours[contour];
AF_Point first_point = point;
if ( point == NULL )
{
continue;
}
current_max_y = point->y;
do
{
if ( point->y > current_max_y )
{
current_max_y = point->y;
}
point = point->next;
} while ( point != first_point );
if ( lowest_contour == -1 || current_max_y < lowest_max_y )
{
lowest_max_y = current_max_y;
lowest_contour = contour;
}
}
FT_Int adjustment_amount = 0;
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
{
if ( contour == lowest_contour )
{
continue;
}
AF_Point point = hints->contours[contour];
AF_Point first_point = point;
if ( point == NULL )
{
continue;
}
FT_Pos min_y = point->y;
do
{
if ( point->y < min_y )
{
min_y = point->y;
}
point = point->next;
} while ( point != first_point );
if ( min_y <= lowest_max_y - 64 )
{
adjustment_amount = 64 - ( min_y - lowest_max_y );
}
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);
}
}
}
else
{
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: No vertical adjustment needed\n" ));
}
}
#undef FT_COMPONENT
#define FT_COMPONENT aflatin
/* Compute the snapped width of a given stem, ignoring very thin ones. */
/* There is a lot of voodoo in this function; changing the hard-coded */
/* parameters influence the whole hinting process. */
@ -3547,9 +4064,7 @@
#endif
}
/* Apply the complete hinting algorithm to a latin glyph. */
static FT_Error
af_latin_hints_apply( FT_UInt glyph_index,
AF_GlyphHints hints,
@ -3582,11 +4097,18 @@
if ( AF_HINTS_DO_VERTICAL( hints ) )
{
FT_Bool is_tilde = af_lookup_tilde_correction_type( metrics->root.reverse_charmap, glyph_index );
if ( is_tilde ) {
af_latin_stretch_tildes( hints, glyph_index );
}
axis = &metrics->axis[AF_DIMENSION_VERT];
error = af_latin_hints_detect_features( hints,
axis->width_count,
axis->widths,
AF_DIMENSION_VERT );
if ( is_tilde ) {
af_latin_remove_tilde_points_from_edges( hints, glyph_index );
}
if ( error )
goto Exit;
@ -3605,6 +4127,7 @@
af_glyph_hints_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 );
af_glyph_hints_apply_vertical_separation_adjustments(hints, (AF_Dimension) dim, glyph_index, metrics->root.reverse_charmap);
}
}
@ -3633,12 +4156,11 @@
(AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, /* style_metrics_init */
(AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, /* style_metrics_scale */
(AF_WritingSystem_DoneMetricsFunc) NULL, /* style_metrics_done */
(AF_WritingSystem_DoneMetricsFunc) af_latin_metrics_done, /* style_metrics_done */
(AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, /* style_metrics_getstdw */
(AF_WritingSystem_InitHintsFunc) af_latin_hints_init, /* style_hints_init */
(AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply /* style_hints_apply */
)
/* END */

View File

@ -410,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;
@ -417,6 +421,7 @@ extern void* af_debug_hints_;
FT_Bool digits_have_same_width;
AF_FaceGlobals globals; /* to access properties */
AF_ReverseCharacterMap reverse_charmap;
} AF_StyleMetricsRec;

View File

@ -30,6 +30,7 @@
#include "afmodule.c"
#include "afranges.c"
#include "afshaper.c"
#include "afadjust.c"
/* END */

View File

@ -1358,22 +1358,7 @@
driver );
}
/**************************************************************************
*
* @Function:
* find_unicode_charmap
*
* @Description:
* This function finds a Unicode charmap, if there is one.
* And if there is more than one, it tries to favour the more
* extensive one, i.e., one that supports UCS-4 against those which
* are limited to the BMP (said UCS-2 encoding.)
*
* This function is called from open_face() (just below), and also
* from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ).
*/
static FT_Error
FT_Error
find_unicode_charmap( FT_Face face )
{
FT_CharMap* first;