freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Git][freetype/freetype][gsoc-craig-2023-final] [autofit] Add GSUB table


From: Craig White (@gerzytet)
Subject: [Git][freetype/freetype][gsoc-craig-2023-final] [autofit] Add GSUB table handling to reverse character map generation
Date: Fri, 03 Nov 2023 06:11:31 +0000

Craig White pushed to branch gsoc-craig-2023-final at FreeType / FreeType

Commits:

  • 49005b96
    by Craig White at 2023-11-03T02:09:46-04:00
    [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
    

1 changed file:

Changes:

  • src/autofit/afadjust.c
    ... ... @@ -22,8 +22,6 @@ adjustment_database[] =
    22 22
         {0x69,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* i */
    
    23 23
         {0x6A,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* j */
    
    24 24
         {0xA1,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Exclamation Mark*/
    
    25
    -    {0xBF,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /* j */
    
    26
    -    {0xA1,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Exclamation Mark*/
    
    27 25
         {0xBF,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*Inverted Question Mark*/
    
    28 26
         {0xC0,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with grave*/
    
    29 27
         {0xC1,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0}, /*A with acute*/
    
    ... ... @@ -271,6 +269,318 @@ af_reverse_character_map_expand( AF_ReverseCharacterMap map, FT_Long *capacity,
    271 269
         return FT_Err_Ok;
    
    272 270
     }
    
    273 271
     
    
    272
    +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    
    273
    +/*
    
    274
    +Recursive algorithm to find all glyphs that a codepoint could turn into from the GSUB table.
    
    275
    +
    
    276
    +buffer: a buffer containing only the input codepoint
    
    277
    +feature_tag_pool: the current list of features under consideration
    
    278
    +current_features: the current list of features being applied
    
    279
    +num_features: length of current_features
    
    280
    +result: the set of glyphs that the input codepoint can map to.
    
    281
    +
    
    282
    +The algorithm works by running the hb_ot_shape_glyphs_closure function on different lists of features
    
    283
    +to see which features will map to glyph onto something different.  This functions returns
    
    284
    +the result of transforming a glyph using a list of features as well as all intermediate
    
    285
    +forms if the glyph was transformed multiple times.
    
    286
    +With no features enabled, hb_ot_shape_glyphs_closure will only return the glyph given by cmap.
    
    287
    +This character will be the first to be placed into the results set.
    
    288
    +Next, the algorithm will test the same lookup enabline one feature at a time
    
    289
    +and see if any of those features change the result.
    
    290
    +If any new glyph variants are found this way, they are added to the results set
    
    291
    +and the algorithm will recurce, trying that feature in combination will every other feature
    
    292
    +to look for further glyph variants.
    
    293
    +
    
    294
    +example:
    
    295
    +suppose we have the following features in the GSUB table:
    
    296
    +f1:
    
    297
    +a -> b
    
    298
    +
    
    299
    +f2:
    
    300
    +b -> c
    
    301
    +
    
    302
    +f3:
    
    303
    +d -> e
    
    304
    +
    
    305
    +The algorithm will take the following steps to find all variants of "a":
    
    306
    +- a is added to the results
    
    307
    +- lookup with feature list {f1}, yielding {a, b}. b is added to the results list and the algorithm recurses
    
    308
    +- - lookup with feature list {f1, f2}, yielding {a, b, c}.  c is added to the results list and the algorithm recurses
    
    309
    +- - - lookup with feature list {f1, f2, f3} yielding {a, b, c}.  No new glyphs
    
    310
    +- - lookup with feature list {f1, f3}, yielding {a, b}.  No new glyphs
    
    311
    +- lookup with feature list {f2}, yielding {a}. No new glyphs
    
    312
    +- lookup with feature list {f3}, yielding {a}. No new glyphs
    
    313
    +
    
    314
    +*/
    
    315
    +FT_LOCAL_DEF( FT_Error )
    
    316
    +af_all_glyph_variants_helper( hb_font_t *font,
    
    317
    +                              hb_buffer_t *buffer,
    
    318
    +                              hb_set_t *feature_tag_pool,
    
    319
    +                              hb_feature_t *current_features,
    
    320
    +                              FT_UInt32 num_features,
    
    321
    +                              hb_set_t* result )
    
    322
    +{
    
    323
    +    FT_Error error;
    
    324
    +    /*get the list of glyphs that are created by only transforming based on the
    
    325
    +    features in current_features*/
    
    326
    +    hb_set_t *baseline_glyphs = NULL, *new_glyphs = NULL;
    
    327
    +    baseline_glyphs = hb_set_create();
    
    328
    +    if ( !hb_set_allocation_successful( baseline_glyphs ) )
    
    329
    +    {
    
    330
    +        error = FT_Err_Out_Of_Memory;
    
    331
    +        goto Exit;
    
    332
    +    }
    
    333
    +
    
    334
    +
    
    335
    +
    
    336
    +    hb_ot_shape_glyphs_closure ( font,
    
    337
    +                                 buffer,
    
    338
    +                                 current_features,
    
    339
    +                                 num_features,
    
    340
    +                                 baseline_glyphs );
    
    341
    +    if ( !hb_set_allocation_successful( baseline_glyphs ) )
    
    342
    +    {
    
    343
    +        error = FT_Err_Out_Of_Memory;
    
    344
    +        goto Exit;
    
    345
    +    }
    
    346
    +
    
    347
    +    /*Add these baseline glyphs to the results.  The baseline glyphs
    
    348
    +    will contain at minimum the glyph specified by CMAP*/
    
    349
    +    hb_set_union( result, baseline_glyphs );
    
    350
    +    if ( !hb_set_allocation_successful( result ) )
    
    351
    +    {
    
    352
    +        error = FT_Err_Out_Of_Memory;
    
    353
    +        goto Exit;
    
    354
    +    }
    
    355
    +    if ( hb_set_get_population( feature_tag_pool ) == 0 )
    
    356
    +    {
    
    357
    +        error = FT_Err_Out_Of_Memory;
    
    358
    +        goto Exit;
    
    359
    +    }
    
    360
    +
    
    361
    +    /*setup to try adding different features to current_features
    
    362
    +    to see if any of them have an effect of the glyphs we get from
    
    363
    +    hb_ot_shape_glyphs_closure*/
    
    364
    +    current_features[num_features].start = HB_FEATURE_GLOBAL_START;
    
    365
    +    current_features[num_features].end = HB_FEATURE_GLOBAL_END;
    
    366
    +    current_features[num_features].value = 1; /*set the feature to enabled*/
    
    367
    +    /*quote from docs about value attribute:
    
    368
    +    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.
    
    369
    +    this algorithm does not handle these lookup type 3 cases fully*/
    
    370
    +
    
    371
    +    new_glyphs = hb_set_create();
    
    372
    +    if ( !hb_set_allocation_successful( new_glyphs ) )
    
    373
    +    {
    
    374
    +        error = FT_Err_Out_Of_Memory;
    
    375
    +        goto Exit;
    
    376
    +    }
    
    377
    +
    
    378
    +    hb_tag_t feature_tag = HB_SET_VALUE_INVALID;
    
    379
    +    while ( hb_set_next( feature_tag_pool, &feature_tag ) )
    
    380
    +    {
    
    381
    +        hb_set_clear( new_glyphs );
    
    382
    +        current_features[num_features].tag = feature_tag;
    
    383
    +        hb_ot_shape_glyphs_closure ( font,
    
    384
    +                                     buffer,
    
    385
    +                                     current_features,
    
    386
    +                                     num_features + 1,
    
    387
    +                                     new_glyphs );
    
    388
    +        if ( !hb_set_allocation_successful( new_glyphs ) )
    
    389
    +        {
    
    390
    +            error = FT_Err_Out_Of_Memory;
    
    391
    +            goto Exit;
    
    392
    +        }
    
    393
    +
    
    394
    +        hb_set_subtract( new_glyphs, result );
    
    395
    +        /*new_glyphs now contains all glyphs that appeared in the result
    
    396
    +        of hb_ot_shape_glyphs_closure that haven't already been accounted for in the result.
    
    397
    +        If this contains any glyphs, we also need to try this feature
    
    398
    +        in combination will other features by recursing
    
    399
    +        */
    
    400
    +        if ( hb_set_get_population( new_glyphs ) != 0 )
    
    401
    +        {
    
    402
    +            /*remove this feature from the feature pool so that
    
    403
    +            the later recursion won't try it*/
    
    404
    +            hb_set_del( feature_tag_pool, feature_tag );
    
    405
    +            error = af_all_glyph_variants_helper( font,
    
    406
    +                                                  buffer,
    
    407
    +                                                  feature_tag_pool,
    
    408
    +                                                  current_features,
    
    409
    +                                                  num_features + 1,
    
    410
    +                                                  result );
    
    411
    +            if ( error )
    
    412
    +            {
    
    413
    +                goto Exit;
    
    414
    +            }
    
    415
    +
    
    416
    +            /*add back the feature we removed*/
    
    417
    +            hb_set_add( feature_tag_pool, feature_tag );
    
    418
    +            if ( !hb_set_allocation_successful( feature_tag_pool ) ) {
    
    419
    +                return FT_Err_Out_Of_Memory;
    
    420
    +            }
    
    421
    +        } /* if( !hb_set_is_subset( glyphs, result ) ) */
    
    422
    +
    
    423
    +    } /*while ( hb_set_next( feature_tag_pool, &feature_tag ) )*/
    
    424
    +Exit:
    
    425
    +    hb_set_destroy( baseline_glyphs );
    
    426
    +    hb_set_destroy( new_glyphs );
    
    427
    +    return FT_Err_Ok;
    
    428
    +}
    
    429
    +
    
    430
    +FT_LOCAL_DEF( FT_Error )
    
    431
    +af_all_glyph_variants( FT_Face face,
    
    432
    +                       hb_font_t *hb_font,
    
    433
    +                       FT_UInt32 codepoint,
    
    434
    +                       hb_set_t* result )
    
    435
    +{
    
    436
    +    FT_Memory memory = face->memory;
    
    437
    +    FT_Error error;
    
    438
    +    hb_face_t *hb_face = hb_font_get_face( hb_font );
    
    439
    +
    
    440
    +    /*The set of all feature tags in the font*/
    
    441
    +    hb_set_t *feature_tags = hb_set_create();
    
    442
    +    hb_set_t *type_3_lookup_indicies = hb_set_create();
    
    443
    +    hb_buffer_t *codepoint_buffer = hb_buffer_create();
    
    444
    +    hb_codepoint_t *type_3_alternate_glyphs_buffer;
    
    445
    +    if ( !hb_set_allocation_successful( feature_tags ) )
    
    446
    +    {
    
    447
    +        error = FT_Err_Out_Of_Memory;
    
    448
    +        goto Exit;
    
    449
    +    }
    
    450
    +    if ( !hb_buffer_allocation_successful( codepoint_buffer ) )
    
    451
    +    {
    
    452
    +        error = FT_Err_Out_Of_Memory;
    
    453
    +        goto Exit;
    
    454
    +    }
    
    455
    +    if ( !hb_set_allocation_successful ( type_3_lookup_indicies ) )
    
    456
    +    {
    
    457
    +        error = FT_Err_Out_Of_Memory;
    
    458
    +        goto Exit;
    
    459
    +    }
    
    460
    +
    
    461
    +    /*populate feature_tags using the output of hb_ot_layout_table_get_feature_tags*/
    
    462
    +    FT_Bool feature_list_done = 0;
    
    463
    +    unsigned int start_offset = 0;
    
    464
    +    while ( !feature_list_done )
    
    465
    +    {
    
    466
    +        unsigned int feature_count = 20;
    
    467
    +        hb_tag_t tags[20];
    
    468
    +        hb_ot_layout_table_get_feature_tags ( hb_face,
    
    469
    +                                              HB_OT_TAG_GSUB,
    
    470
    +                                              start_offset,
    
    471
    +                                              &feature_count,
    
    472
    +                                              tags );
    
    473
    +        start_offset += 20;
    
    474
    +        if ( feature_count < 20 )
    
    475
    +        {
    
    476
    +            feature_list_done = 1;
    
    477
    +        }
    
    478
    +        for ( int i = 0; i < feature_count; i++ )
    
    479
    +        {
    
    480
    +            hb_set_add( feature_tags, tags[i] );
    
    481
    +        }
    
    482
    +        if ( !hb_set_allocation_successful( feature_tags ) )
    
    483
    +        {
    
    484
    +            error = FT_Err_Out_Of_Memory;
    
    485
    +            goto Exit;
    
    486
    +        }
    
    487
    +    }
    
    488
    +
    
    489
    +    /*make a buffer only consisting of the given codepoint*/
    
    490
    +    if ( !hb_buffer_pre_allocate( codepoint_buffer, 1 ) )
    
    491
    +    {
    
    492
    +        error = FT_Err_Out_Of_Memory;
    
    493
    +        goto Exit;
    
    494
    +    }
    
    495
    +    hb_buffer_set_direction ( codepoint_buffer,
    
    496
    +                              HB_DIRECTION_LTR );
    
    497
    +    hb_buffer_add( codepoint_buffer, codepoint, 0 );
    
    498
    +
    
    499
    +    /*The array of features that will be used by the recursive part
    
    500
    +    it will have at most as many entries as there are features, so
    
    501
    +    make the length = length of feature_tags*/
    
    502
    +    hb_feature_t *feature_buffer;
    
    503
    +    if (( error = FT_NEW_ARRAY( feature_buffer, hb_set_get_population( feature_tags ) ) ))
    
    504
    +    {
    
    505
    +        goto Exit;
    
    506
    +    }
    
    507
    +
    
    508
    +    error = af_all_glyph_variants_helper( hb_font,
    
    509
    +                                          codepoint_buffer,
    
    510
    +                                          feature_tags,
    
    511
    +                                          feature_buffer,
    
    512
    +                                          0,
    
    513
    +                                          result );
    
    514
    +    if ( error )
    
    515
    +    {
    
    516
    +        goto Exit;
    
    517
    +    }
    
    518
    +
    
    519
    +    /*
    
    520
    +    Add the alternative glyph forms that come from features using
    
    521
    +    type 3 lookups.
    
    522
    +    This file from gtk was very useful in figuring out my approach:
    
    523
    +    https://github.com/val-verde/gtk/blob/212e85e92628eda9f9650e5cc8d88c44062642ae/gtk/gtkfontchooserwidget.c#L2085
    
    524
    +    */
    
    525
    +    /*list of features containing type 3 lookups:*/
    
    526
    +    hb_tag_t feature_list[] = {
    
    527
    +        HB_TAG('s','a','l','t'),
    
    528
    +        HB_TAG('s','w','s','h'),
    
    529
    +        HB_TAG('n','a','l','t'),
    
    530
    +        HB_TAG_NONE
    
    531
    +    };
    
    532
    +
    
    533
    +
    
    534
    +    hb_ot_layout_collect_lookups ( hb_face,
    
    535
    +                                   HB_OT_TAG_GSUB,
    
    536
    +                                   NULL,
    
    537
    +                                   NULL,
    
    538
    +                                   feature_list,
    
    539
    +                                   type_3_lookup_indicies);
    
    540
    +
    
    541
    +    if ( !hb_set_allocation_successful( type_3_lookup_indicies ) )
    
    542
    +    {
    
    543
    +        error = FT_Err_Out_Of_Memory;
    
    544
    +        goto Exit;
    
    545
    +    }
    
    546
    +
    
    547
    +    if (( error = FT_NEW_ARRAY( type_3_alternate_glyphs_buffer, 100 ) ))
    
    548
    +    {
    
    549
    +        goto Exit;
    
    550
    +    }
    
    551
    +    hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID;
    
    552
    +    FT_UInt base_glyph_index = FT_Get_Char_Index( face, codepoint );
    
    553
    +    if ( base_glyph_index != 0 ) {
    
    554
    +        while ( hb_set_next( type_3_lookup_indicies, &lookup_index ) )
    
    555
    +        {
    
    556
    +            unsigned alternate_count = 100;
    
    557
    +
    
    558
    +            hb_ot_layout_lookup_get_glyph_alternates
    
    559
    +                                        ( hb_face,
    
    560
    +                                          lookup_index,
    
    561
    +                                          base_glyph_index,
    
    562
    +                                          0,
    
    563
    +                                          &alternate_count,
    
    564
    +                                          type_3_alternate_glyphs_buffer );
    
    565
    +
    
    566
    +            for ( unsigned i = 0; i < alternate_count; i++ )
    
    567
    +            {
    
    568
    +                hb_set_add( result, type_3_alternate_glyphs_buffer[i] );
    
    569
    +            }
    
    570
    +        }
    
    571
    +    }
    
    572
    +
    
    573
    +
    
    574
    +
    
    575
    +Exit:
    
    576
    +    hb_set_destroy( feature_tags );
    
    577
    +    hb_buffer_destroy( codepoint_buffer );
    
    578
    +    FT_FREE( feature_buffer );
    
    579
    +    FT_FREE( type_3_alternate_glyphs_buffer );
    
    580
    +    return error;
    
    581
    +}
    
    582
    +#endif /*FT_CONFIG_OPTION_USE_HARFBUZZ*/
    
    583
    +
    
    274 584
     FT_LOCAL_DEF( FT_Error )
    
    275 585
     af_reverse_character_map_new( AF_ReverseCharacterMap *map, AF_FaceGlobals globals )
    
    276 586
     {
    
    ... ... @@ -303,6 +613,54 @@ af_reverse_character_map_new( AF_ReverseCharacterMap *map, AF_FaceGlobals global
    303 613
         {
    
    304 614
             goto Exit;
    
    305 615
         }
    
    616
    +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    
    617
    +    hb_font_t *hb_font = globals->hb_font;
    
    618
    +    /*hb_face_t *hb_face = hb_font_get_face( hb_font );*/
    
    619
    +    hb_set_t *result_set = hb_set_create();
    
    620
    +    if ( !hb_set_allocation_successful( result_set ) )
    
    621
    +    {
    
    622
    +        error = FT_Err_Out_Of_Memory;
    
    623
    +        goto harfbuzz_path_Exit;
    
    624
    +    }
    
    625
    +
    
    626
    +    /*find alll glyph variants of the codepoints, then make an entry from
    
    627
    +    the glyph to the codepoint for each one*/
    
    628
    +    for ( FT_Long i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
    
    629
    +    {
    
    630
    +        FT_UInt32 codepoint = adjustment_database[i].codepoint;
    
    631
    +
    
    632
    +        error = af_all_glyph_variants( face,
    
    633
    +                                       hb_font,
    
    634
    +                                       codepoint,
    
    635
    +                                       result_set );
    
    636
    +        if ( error )
    
    637
    +        {
    
    638
    +            goto harfbuzz_path_Exit;
    
    639
    +        }
    
    640
    +
    
    641
    +        hb_codepoint_t glyph = HB_SET_VALUE_INVALID;
    
    642
    +        while ( hb_set_next( result_set, &glyph ) )
    
    643
    +        {
    
    644
    +            error = af_reverse_character_map_expand( *map, &capacity, memory );
    
    645
    +            if ( error ) {
    
    646
    +                goto harfbuzz_path_Exit;
    
    647
    +            }
    
    648
    +
    
    649
    +            FT_Long insert_point = ( *map )->length;
    
    650
    +            ( *map )->length++;
    
    651
    +            ( *map )->entries[insert_point].glyph_index = glyph;
    
    652
    +            ( *map )->entries[insert_point].codepoint = codepoint;
    
    653
    +        }
    
    654
    +
    
    655
    +        hb_set_clear( result_set );
    
    656
    +    }
    
    657
    +harfbuzz_path_Exit:
    
    658
    +    hb_set_destroy( result_set );
    
    659
    +    if ( error )
    
    660
    +    {
    
    661
    +        goto Exit;
    
    662
    +    }
    
    663
    +#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
    
    306 664
     
    
    307 665
     #ifdef FT_DEBUG_LEVEL_TRACE
    
    308 666
         int failed_lookups = 0;
    
    ... ... @@ -328,6 +686,8 @@ af_reverse_character_map_new( AF_ReverseCharacterMap *map, AF_FaceGlobals global
    328 686
             ( *map )->entries[i].codepoint = codepoint;
    
    329 687
         }
    
    330 688
     
    
    689
    +#endif /*FT_CONFIG_OPTION_USE_HARFBUZZ*/
    
    690
    +
    
    331 691
         ft_qsort(
    
    332 692
             ( *map )->entries,
    
    333 693
             ( *map )->length,
    


  • reply via email to

    [Prev in Thread] Current Thread [Next in Thread]