Compare commits

...

7 Commits

Author SHA1 Message Date
Craig White
afa11fc16a add additional grid aligning step to tilde unflattening to fix issues 2024-04-13 23:04:08 -04:00
Craig White
984ff89185 Attempted for for tilde flattening 2024-03-07 19:44:42 -05:00
Craig White
49005b96a2 [autofit] Add GSUB table handling to reverse character map generation
* src/autofit/afadjust.c If harfbuzz is enabled, the reverse character
map generation will now consider GSUB entries when looking for
glyphs that correspond to a codepoint
2023-11-03 02:09:46 -04:00
Craig White
ba0b1a57e3 [autofit] add tilde unflattening algorithm
* src/autofit/aflatin.c add tilde unflattening algorithm, applied
based on adjustment database and reverse character map
* src/autofit/aflatin.c fix adjustment database entry for n with tilde
2023-11-03 01:34:57 -04:00
Craig White
41bb41de9a [autofit] Implement vertical separation adjustment
* include/freetype/internal/fttrace.h add "afadjust" trace function
definition, to be used for all adjustment database code.
* src/autofit/afadjust.c fix null derefence when reverse character map
is null.
* src/autofit/aflatin.c contour positions are now adjusted vertically
based on the entries in the adjustment database and reverse character map
src/autofit/aftypes.h add reverse character map to AF_StyleMetricsRec
2023-11-02 23:41:40 -04:00
Craig White
b9095238d2 [autofit] add functions for creating and using reverse character maps
as well as looking up entries in the adjustment database.

* include/freetype/internal/ftobjs.h (find_unicode_charmap) Move documentation
and function prototype from ftobjs.c to this file so that it can be called
from afadjust.c
* src/base/ftobjs.c (find_unicode_charmap) make this function non-static
* src/autofit/afadjust.c Add functions for looking up vertical adjustment
type, tilde correction type, creating and deleting reverse character maps
* src/autofit/afadjust.h Add headers for the above functions.  Correct
definition of AF_AdjustmentDatabaseEntry_ to include tilde correction
type
* src/autofit/aftypes.h Add prototype of AF_ReverseCharacterMap
* src/autofit/autofit.c Add include of afadjust.c
2023-10-31 23:20:01 -04:00
Craig White
573649c1ae [autofit] Add files necessary for adjustment database
* src/autofit/afadjust.h header file for adjustment database functions
* src/autofit/afadjust.c will contain implementations of the
adjustment database functions
2023-10-31 15:35:34 -04:00
9 changed files with 1437 additions and 21 deletions

View File

@ -430,9 +430,8 @@ FT_BEGIN_HEADER
* Do not `#undef` these macros here since the build system might define
* them for certain configurations only.
*/
/* #define FT_DEBUG_LEVEL_ERROR */
/* #define FT_DEBUG_LEVEL_TRACE */
#define FT_DEBUG_LEVEL_ERROR
#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) */

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

@ -0,0 +1,725 @@
#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
/*
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. This should be one of the enum constants in AF_VerticalSeparationAdjustmentType
- 1 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}, /*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, 1}, /*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}
};
/*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;
/* 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_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 )
{
if ( map == NULL ) {
return 0;
}
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;
}
#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 )
{
*map = NULL;
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 );
*map = NULL;
return error;
}
return FT_Err_Ok;
}
FT_LOCAL_DEF( FT_Error )
af_reverse_character_map_done( AF_ReverseCharacterMap map, FT_Memory memory ) {
if ( map != NULL ) {
FT_FREE( map->entries );
}
FT_FREE( map );
return FT_Err_Ok;
}

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

@ -0,0 +1,54 @@
#ifndef AFADJUST_H_
#define AFADJUST_H_
#include <freetype/fttypes.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
} 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 );
struct AF_ReverseCharacterMap_;
typedef struct AF_ReverseCharacterMap_ *AF_ReverseCharacterMap;
FT_END_HEADER
#endif

View File

@ -22,7 +22,7 @@
#include "afglobal.h"
#include "aflatin.h"
#include "aferrors.h"
#include "afadjust.h"
/**************************************************************************
*
@ -1155,6 +1155,8 @@
af_latin_metrics_check_digits( metrics, face );
}
af_reverse_character_map_new( &metrics->root.reverse_charmap, metrics->root.globals );
Exit:
face->charmap = oldmap;
return error;
@ -1484,6 +1486,11 @@
}
}
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 );
}
/* Scale global values in both directions. */
@ -2737,6 +2744,580 @@
return width;
}
#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 unflattening 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 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 );
//touch all points
p = first_point;
do
{
p = p->next;
if ( !(p->flags & AF_FLAG_CONTROL) )
p->flags |= AF_FLAG_TOUCH_Y;
}
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;
}
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 );
}
/*
As part of af_latin_stretch_tildes, all points in the tilde
are marked as touched, so the existing grid fitting will leave
the tilde misaligned with the grid.
This function moves the tilde contour down to be grid fitted.
We assume that if moving the tilde down would cause it to touch or
overlap another countour, the vertical adjustment step will fix it
Because the vertical adjustment step comes after all other grid fitting
steps, the top edge of the contour under the tilde is usually aligned with
a horizontal gridline. The vertical gap enforced by the vertical adjustment
is exactly one pixel, so if the top edge of the contour below the tilde is on a
gridline, the resulting tilde contour will also be grid aligned.
But in cases where the gap is already big enough so that the vertical
adjustment does nothing, this function ensures that even without the
intervention of the vertical adjustment step, the tilde will be
grid aligned.
*/
void
af_latin_align_tildes( AF_GlyphHints hints ) {
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;
do
{
p = p->next;
if ( p->y < min_y )
{
min_y = p->y;
}
if ( p->y > max_y )
{
max_y = p->y;
}
}
while ( p != first_point );
//FT_Pos mid_y = ( min_y + max_y ) / 2;
FT_Pos min_y_rounded = FT_PIX_ROUND( min_y );
FT_Pos delta = min_y_rounded - min_y;
do
{
p = p->next;
p->y += delta;
}
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 )
{
FT_TRACE4(("Enter af_glyph_hints_apply_vertical_separation_adjustments\n"));
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 );
}
}
FT_TRACE4(( " Calculated adjustment amount %d\n", adjustment_amount ));
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" ));
}
FT_TRACE4(("Exit af_glyph_hints_apply_vertical_separation_adjustments\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 */
@ -3547,6 +4128,33 @@
#endif
}
/*Print the height of the topmost contour for debugging purposes.
TODO: remove this once the tilde unflattening works.*/
static void traceheight(FT_UInt num, AF_GlyphHints hints) {
AF_Point p = hints->contours[af_find_highest_contour(hints)];
AF_Point first_point = p;
FT_Pos min_y, max_y;
min_y = max_y = p->y;
do {
p = p->next;
if ( !(p->flags & AF_FLAG_CONTROL) ) {
if ( p->y < min_y ) {
min_y = p->y;
}
if ( p->y > max_y ) {
max_y = p->y;
}
}
} while ( p != first_point );
FT_Pos height = max_y - min_y;
FT_TRACE4(( "height %d: %d\n", num, height ));
}
/* Apply the complete hinting algorithm to a latin glyph. */
@ -3582,11 +4190,21 @@
if ( AF_HINTS_DO_VERTICAL( hints ) )
{
FT_Bool is_tilde = af_lookup_tilde_correction_type( metrics->root.reverse_charmap, glyph_index );
if ( is_tilde ) {
traceheight(0, hints);
af_latin_stretch_tildes( hints );
af_latin_align_tildes( hints );
traceheight(1, hints);
}
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;
@ -3602,9 +4220,15 @@
( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
{
af_latin_hint_edges( hints, (AF_Dimension)dim );
traceheight(2, hints);
af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
traceheight(3, hints);
af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
traceheight(4, hints);
af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
traceheight(5, hints);
af_glyph_hints_apply_vertical_separation_adjustments(hints, (AF_Dimension) dim, glyph_index, metrics->root.reverse_charmap);
traceheight(6, hints);
}
}
@ -3633,7 +4257,7 @@
(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 */

View File

@ -406,6 +406,12 @@ extern void* af_debug_hints_;
typedef struct AF_FaceGlobalsRec_* AF_FaceGlobals;
/*stores a mapping from glyphs to unicode codepoints
see afadjust.c for details */
struct AF_ReverseCharacterMap_;
typedef struct AF_ReverseCharacterMap_ *AF_ReverseCharacterMap;
/* This is the main structure that combines everything. Autofit modules */
/* specific to writing systems derive their structures from it, for */
/* example `AF_LatinMetrics'. */
@ -417,6 +423,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;