From 86e5080e863001fb6b79f78774a5af3774ae62f2 Mon Sep 17 00:00:00 2001 From: Craig White Date: Sat, 26 Aug 2023 22:58:43 -0400 Subject: [PATCH] Add support for simple cases of GSUB lookups --- src/autofit/afadjust.c | 185 +++++++++++++++++++++++++++++++++++++---- src/autofit/afadjust.h | 9 +- src/autofit/aflatin.c | 2 +- src/autofit/aftypes.h | 5 +- 4 files changed, 175 insertions(+), 26 deletions(-) diff --git a/src/autofit/afadjust.c b/src/autofit/afadjust.c index 9a23da873..7c684be3e 100644 --- a/src/autofit/afadjust.c +++ b/src/autofit/afadjust.c @@ -8,6 +8,11 @@ #undef FT_COMPONENT #define FT_COMPONENT afadjust +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + #include + #include +#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*/ /* @@ -16,7 +21,7 @@ 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*/ @@ -133,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 ) { @@ -190,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 ) { @@ -225,19 +236,59 @@ af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index return 0; } -FT_LOCAL_DEF( FT_Error ) -af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memory memory ) +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( 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; } @@ -247,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) ) { @@ -268,19 +319,117 @@ af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memo #endif continue; } - if ( size == capacity ) - { - capacity += capacity / 2; - if ( FT_RENEW_ARRAY((*map)->entries, size, capacity) ) - { - goto Exit; - } + error = af_reverse_character_map_expand( *map, &capacity, memory ); + if ( error ) { + goto Exit; } - size++; + + ( *map )->length++; ( *map )->entries[i].glyph_index = glyph; ( *map )->entries[i].codepoint = codepoint; } - ( *map )->length = size; + + 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 ) + { + 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 ) + { + continue; + } + + if ( input_entry == NULL ) + { + input_entry = entry; + } + else + { + if ( !af_adjustment_database_entry_equals( input_entry, entry ) ) + { + goto end; + } + } + } + + + 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: ; + } + + 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; @@ -296,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; } diff --git a/src/autofit/afadjust.h b/src/autofit/afadjust.h index 179f4fd5e..3ac24ae83 100644 --- a/src/autofit/afadjust.h +++ b/src/autofit/afadjust.h @@ -1,7 +1,8 @@ #ifndef AFADJUST_H_ #define AFADJUST_H_ - #include +#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 ) diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 690f2ada3..ce5da7feb 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -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: diff --git a/src/autofit/aftypes.h b/src/autofit/aftypes.h index aca173567..f04e77826 100644 --- a/src/autofit/aftypes.h +++ b/src/autofit/aftypes.h @@ -39,7 +39,6 @@ #include #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;