Edit

kc3-lang/freetype/src/autofit/afadjust.c

Branch :

  • Show log

    Commit

  • Author : Behdad Esfahbod
    Date : 2025-04-22 06:20:22
    Hash : 1019b1c2
    Message : * src/autofit/afadjust.c (af_get_glyph_alternates): Fix endless loop.

  • src/autofit/afadjust.c
  • /****************************************************************************
     *
     * afadjust.c
     *
     *   Auto-fitter routines to adjust components based on charcode (body).
     *
     * Copyright (C) 2023-2024 by
     * David Turner, Robert Wilhelm, and Werner Lemberg.
     *
     * Written by Craig White <gerzytet@gmail.com>.
     *
     * This file is part of the FreeType project, and may only be used,
     * modified, and distributed under the terms of the FreeType project
     * license, LICENSE.TXT.  By continuing to use, modify, or distribute
     * this file you indicate that you have read the license and
     * understand and accept it fully.
     *
     */
    
    #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 ascending Unicode code
        points.  The table entries are 3 numbers consisting of:
    
        - Unicode code point.
        - The vertical adjustment type.  This should be one of the enum
          constants in `AF_VerticalSeparationAdjustmentType`.
        - Value 1 if the topmost contour is a tilde and should be prevented from
          flattening, and 0 otherwise.
      */
      FT_LOCAL_ARRAY_DEF( AF_AdjustmentDatabaseEntry )
      adjustment_database[] =
      {
        { 0x21,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ! */
        { 0x3F,  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 */
    #if 0
        /* XXX TODO */
        { 0x7E,  AF_VERTICAL_ADJUSTMENT_NONE, 1 }, /* ~ */
    #endif
    
        { 0xA1,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ¡ */
        { 0xA6,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ¦ */
        { 0xAA,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ª */
        { 0xBA,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* º */
        { 0xBF,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ¿ */
    
        { 0xC0,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* À */
        { 0xC1,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Á */
        { 0xC2,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Â */
        { 0xC3,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* Ã */
        { 0xC4,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ä */
        { 0xC5,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Å */
        { 0xC8,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* È */
        { 0xC9,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* É */
        { 0xCA,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ê */
        { 0xCB,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ë */
        { 0xCC,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ì */
        { 0xCD,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Í */
        { 0xCE,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Î */
        { 0xCF,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ï */
    
        { 0xD1,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* Ñ */
        { 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, 1 }, /* Õ */
        { 0xD6,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ö */
        { 0xD9,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ù */
        { 0xDA,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ú */
        { 0xDB,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Û */
        { 0xDC,  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 }, /* ã */
        { 0xE4,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ä */
        { 0xE5,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* å */
        { 0xE8,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* è */
        { 0xE9,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* é */
        { 0xEA,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ê */
        { 0xEB,  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 }, /* î */
        { 0xEF,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ï */
    
        { 0xF1,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* ñ */
        { 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 }, /* õ */
        { 0xF6,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ö */
        { 0xF9,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ù */
        { 0xFA,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ú */
        { 0xFB,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* û */
        { 0xFC,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ü */
        { 0xFD,  AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ý */
        { 0xFF,  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 }, /* Ć */
        { 0x107, 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 }, /* ė */
        { 0x11A, 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 }, /* ġ */
        { 0x122, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 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, 1 }, /* Ĩ */
        { 0x129, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* ĩ */
        { 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 }, /* İ */
        { 0x133, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ij */
        { 0x134, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ĵ */
        { 0x135, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ĵ */
        { 0x136, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 0 }, /* Ķ */
        { 0x137, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 0 }, /* ķ */
        { 0x139, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ĺ */
        { 0x13A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ĺ */
        { 0x13B, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 0 }, /* Ļ */
        { 0x13C, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 0 }, /* ļ */
    
        { 0x143, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ń */
        { 0x144, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ń */
        { 0x145, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 0 }, /* Ņ */
        { 0x146, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 0 }, /* ņ */
        { 0x147, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ň */
        { 0x148, 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 }, /* ŏ */
    
        { 0x150, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ő */
        { 0x151, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ő */
        { 0x154, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ŕ */
        { 0x155, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ŕ */
        { 0x156, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 0 }, /* Ŗ */
        { 0x157, AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN, 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, 1 }, /* Ũ */
        { 0x169, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 1 }, /* ũ */
        { 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 }, /* ŭ */
        { 0x16E, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ů */
        { 0x16F, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* ů */
    
        { 0x170, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP, 0 }, /* Ű */
        { 0x171, 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 }, /* ŷ */
        { 0x178, 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( 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;
      }
    
    
      /* `qsort` compare function for reverse character map. */
      FT_COMPARE_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
                   : entry_a.codepoint < entry_b.codepoint
                       ? -1
                       : entry_a.codepoint > entry_b.codepoint
                           ? 1
                           : 0;
      }
    
    
      FT_LOCAL_DEF( const AF_ReverseMapEntry* )
      af_reverse_character_map_lookup( AF_ReverseCharacterMap  map,
                                       FT_Int                  glyph_index )
      {
        FT_Int   low, high;
        FT_Long  length;
    
    
        if ( !map )
          return NULL;
    
        length = map->length;
    
        /* Binary search for reverse character map entry. */
        low  = 0;
        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 if (low != mid)
            /* We want the first occurrence of `glyph_index` */
            /* (i.e., the one with the lowest array index).  */
            high = mid;
          else
            return &map->entries[mid];
        }
    
        return NULL;
      }
    
    
      /* Prepare to add one more entry to the reverse character map.   */
      /* This is a helper function for `af_reverse_character_map_new`. */
      static 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
    #  if HB_VERSION_ATLEAST( 7, 2, 0 )
    
      /*
        Find all glyphs that a code point could turn into from the OpenType
        'GSUB' table.
    
        The algorithm first gets the glyph index from the code point (using the
        'cmap' table) and puts it into the result set.  It then calls function
        `hb_ot_layout_lookup_get_glyph_alternates` on each OpenType lookup (only
        handling 'SingleSubst' and 'AlternateSubst' lookups, ignoring all other
        types) to check which ones map the glyph in question onto something
        different.  These alternate glyph indices are then added to the result
        set.
    
        If there are results, `hb_ot_layout_lookup_get_glyph_alternates` is
        tried again on each of them to find out whether these glyphs in turn
        have also alternates, which are eventually added to the result set, too.
        This gets repeated in a loop until no more additional glyphs are found.
    
        Example:
    
          Suppose we have the following lookups in the GSUB table:
    
            L1: a -> b
            L2: b -> c
            L3: d -> e
    
          The algorithm takes the following steps to find all variants of 'a'.
    
          - Add 'a' to the result.
          - Check lookup L1 for 'a', yielding {b}.
          - Check lookups L2 and L3 for 'a', yielding nothing.
          => Add 'b' to the result list, try again.
            - Check lookup L1 for 'b', yielding nothing.
            - Check lookup L2 for 'b', yielding {c}.
            - Check lookup L3 for 'b', yielding nothing.
            => Add 'c' to the result list, try again.
              - Check lookups L1 to L3 for 'c', yielding nothing.
              => Done.
      */
    
    
      /* The chunk size used for retrieving results of */
      /* `hb_ot_layout_lookup_get_glyph_alternates`.   */
    #define ALTERNATE_CHUNK  20
    
    
      /* Get all alternates for a given glyph index. */
      static void
      af_get_glyph_alternates_helper( hb_face_t      *hb_face,
                                      hb_codepoint_t  glyph,
                                      hb_set_t       *gsub_lookups,
                                      hb_set_t       *result )
      {
        hb_codepoint_t  lookup_index = HB_SET_VALUE_INVALID;
    
    
        /* Iterate over all lookups. */
        while ( hb_set_next( gsub_lookups, &lookup_index ) )
        {
          FT_Bool       lookup_done  = FALSE;
          unsigned int  start_offset = 0;
    
    
          while ( !lookup_done )
          {
            unsigned int    alternates_count = ALTERNATE_CHUNK;
            hb_codepoint_t  alternates[ALTERNATE_CHUNK];
    
            unsigned int  n;
    
    
            (void)hb_ot_layout_lookup_get_glyph_alternates( hb_face,
                                                            lookup_index,
                                                            glyph,
                                                            start_offset,
                                                            &alternates_count,
                                                            alternates );
    
            start_offset += ALTERNATE_CHUNK;
            if ( alternates_count < ALTERNATE_CHUNK )
              lookup_done = TRUE;
    
            for ( n = 0; n < alternates_count; n++ )
              hb_set_add( result, alternates[n] );
          }
        }
      }
    
    
      /* Get all alternates (including alternates of alternates) */
      /* for a given glyph index.                                */
      static void
      af_get_glyph_alternates( hb_font_t      *hb_font,
                               hb_codepoint_t  glyph,
                               hb_set_t       *gsub_lookups,
                               hb_set_t       *result )
      {
        hb_face_t  *hb_face       = hb_font_get_face( hb_font );
        hb_set_t   *helper_result = hb_set_create();
    
    
        /* Seed `helper_result` with `glyph` itself, then get all possible */
        /* values.  Note that we can't use `hb_set_next` to control the    */
        /* loop because we modify `helper_result` during iteration.        */
        hb_set_add( helper_result, glyph );
        while ( !hb_set_is_empty( helper_result ) )
        {
          hb_codepoint_t  elem;
    
    
          /* Always get the smallest element of the set. */
          elem = HB_SET_VALUE_INVALID;
          hb_set_next( helper_result, &elem );
    
          /* Don't process already handled glyphs again. */
          if ( !hb_set_has( result, elem ) )
          {
            /* This call updates the glyph set in `helper_result`. */
            af_get_glyph_alternates_helper( hb_face,
                                            elem,
                                            gsub_lookups,
                                            helper_result );
            hb_set_add( result, elem );
          }
    
          hb_set_del( helper_result, elem );
        }
    
        hb_set_destroy( helper_result );
      }
    
    #  endif /* HB_VERSION_ATLEAST */
    #endif /* FT_CONFIG_OPTION_USE_HARFBUZZ */
    
    
      FT_LOCAL_DEF( FT_Error )
      af_reverse_character_map_new( AF_ReverseCharacterMap  *map,
                                    AF_FaceGlobals           globals )
      {
        FT_Error  error;
    
        FT_Face    face   = globals->face;
        FT_Memory  memory = face->memory;
    
        FT_CharMap  old_charmap;
    
        FT_Long  capacity;
    
    
        /* 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" ));
    
        /* Back up `face->charmap` because `find_unicode_charmap` sets it. */
        old_charmap = face->charmap;
    
        if ( ( error = find_unicode_charmap( face ) ) )
          goto Exit;
    
        *map = NULL;
        if ( FT_NEW( *map ) )
          goto Exit;
    
        /* Start with a capacity of 10 entries. */
        capacity         = 10;
        ( *map )->length = 0;
    
        if ( FT_NEW_ARRAY( ( *map )->entries, capacity ) )
          goto Exit;
    
    #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
    #  if HB_VERSION_ATLEAST( 7, 2, 0 )
    
        {
          /* No need to check whether HarfBuzz has allocation issues; */
          /* it continues to work in such cases and simply returns    */
          /* 'empty' objects that do nothing.                         */
    
          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();
          hb_set_t  *gsub_lookups = hb_set_create();
    
          FT_UInt32  codepoint;
          FT_UInt    glyph_index;
    
          FT_ULong  i;
    
          AF_ReverseMapEntry  *map_limit;
    
    
          /* Compute set of all GSUB lookups. */
          hb_ot_layout_collect_lookups( hb_face,
                                        HB_OT_TAG_GSUB,
                                        NULL, NULL, NULL,
                                        gsub_lookups );
    
          /* Find all glyph alternates of the code points in  */
          /* the adjustment database and put them into `map`. */
          for ( i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
          {
            FT_UInt  cmap_glyph;
    
            hb_codepoint_t  glyph;
    
    
            codepoint  = adjustment_database[i].codepoint;
            cmap_glyph = FT_Get_Char_Index( face, codepoint );
    
            af_get_glyph_alternates( hb_font,
                                     cmap_glyph,
                                     gsub_lookups,
                                     result_set );
    
            glyph = HB_SET_VALUE_INVALID;
            while ( hb_set_next( result_set, &glyph ) )
            {
              FT_Long  insert_point;
    
    
              error = af_reverse_character_map_expand( *map, &capacity, memory );
              if ( error )
                goto Exit;
    
              insert_point = ( *map )->length;
    
              ( *map )->length++;
              ( *map )->entries[insert_point].glyph_index = glyph;
              ( *map )->entries[insert_point].codepoint   = codepoint;
            }
    
            hb_set_clear( result_set );
          }
    
          hb_set_destroy( result_set );
          hb_set_destroy( gsub_lookups );
    
          ft_qsort( ( *map )->entries,
                    ( *map )->length,
                    sizeof ( AF_ReverseMapEntry ),
                    af_reverse_character_map_entry_compare );
    
          /* OpenType features like 'unic' map lowercase letter glyphs to    */
          /* uppercase forms (and vice versa), which could lead to the use   */
          /* of a wrong entry in the adjustment database.  For this reason   */
          /* we prioritize cmap entries.                                     */
          /*                                                                 */
          /* XXX Note, however, that this cannot cover all cases since there */
          /* might be contradictory entries for glyphs not in the cmap.  A   */
          /* possible solution might be to specially mark pairs of related   */
          /* lowercase and uppercase characters in the adjustment database   */
          /* that have diacritics on different vertical sides (for example,  */
          /* U+0122 'Ģ' and U+0123 'ģ').  The auto-hinter could then perform */
          /* a topological analysis to do the right thing.                   */
    
          codepoint = FT_Get_First_Char( face, &glyph_index );
          map_limit = ( *map )->entries + ( *map )->length;
          while ( glyph_index )
          {
            AF_ReverseMapEntry  *entry;
    
    
            entry = (AF_ReverseMapEntry*)
                      af_reverse_character_map_lookup( *map, glyph_index );
            if ( entry )
            {
              FT_Int  idx = entry->glyph_index;
    
    
              while ( entry < map_limit         &&
                      entry->glyph_index == idx )
              {
                entry->codepoint = codepoint;
                entry++;
              }
            }
    
            codepoint = FT_Get_Next_Char( face, codepoint, &glyph_index );
          }
        }
    
    #  endif /* HB_VERSION_ATLEAST */
    
    #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
    
        {
          FT_UInt  i;
          FT_Long  insert_point;
    
    
          for ( 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 )
              continue;
    
            error = af_reverse_character_map_expand( *map, &capacity, memory );
            if ( error )
              goto Exit;
    
            insert_point = ( *map )-> length;
    
            ( *map )->length++;
            ( *map )->entries[insert_point].glyph_index = glyph;
            ( *map )->entries[insert_point].codepoint   = codepoint;
          }
    
          ft_qsort( ( *map )->entries,
                    ( *map )->length,
                    sizeof ( AF_ReverseMapEntry ),
                    af_reverse_character_map_entry_compare );
        }
    
    #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
    
        FT_TRACE4(( "    reverse character map built successfully"
                    " with %ld entries\n", ( *map )->length ));
    
    #ifdef FT_DEBUG_LEVEL_TRACE
        {
          FT_Long  i;
    
    
          FT_TRACE7(( "       gidx   code    adj  tilde\n" ));
                   /* "      XXXXX  0xXXXX  XXXX   XXX" */
          FT_TRACE7(( "     ----------------------------\n" ));
    
          for ( i = 0; i < ( *map )->length; i++ )
          {
            FT_Long  glyph_index = ( *map )->entries[i].glyph_index;
            FT_Int   codepoint   = ( *map )->entries[i].codepoint;
    
            const AF_AdjustmentDatabaseEntry    *db_entry =
              af_adjustment_database_lookup( codepoint );
            AF_VerticalSeparationAdjustmentType  adj_type;
    
    
            if ( !db_entry )
              continue;
    
            adj_type = db_entry->vertical_separation_adjustment_type;
    
            FT_TRACE7(( "      %5ld  0x%04X  %4s   %3s\n",
                        glyph_index,
                        codepoint,
                        adj_type == AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN
                          ? "down"
                          : adj_type == AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP
                            ? "up"
                            : "",
                        db_entry->apply_tilde ? "yes" : "no" ));
          }
        }
    #endif
    
    
      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 )
          FT_FREE( map->entries );
        FT_FREE( map );
    
        return FT_Err_Ok;
      }
    
    
    /* END */