Subject: Re: bug#79285: [Patch] support :font-features in face

From: Binbin YE <[email-masked]>
Date: Tue, 9 Sep 2025 00:26:11 +0900

On Wed, Sep 3, 2025 at 9:10 PM Eli Zaretskii <[email-masked]> wrote:

>
> That's the idea, yes.  It would mean to have a function in hbfont.c
> that is the subset of hbfont_shape, and which accepts a single
> character (not a Lisp string) and a font, and then constructs the
> hb_buffer and submits that to hb_shape_full.
>
> But please test if this should give good results by simulating it, as
> follows:
>
>  . make composition-function-table whose cells for several characters
>    match only that one character, and see how a string of such
>    characters is rendered using a font with relevant OpenType features
>  . then compare that with rendering when composition-function-table
>    has the same rule in the cell of each of those characters, matching
>    any sequence of these characters (as in "[abcdefg]+")
>
> If applying stylistic sets by rendering text one character at a time
> produces different results from rendering them all as a single string,
> then this idea is not workable, and we will need to use the (slower
> and more complex) composite.c machinery instead.
>
> If the idea does work, then presumably a change in
> get_glyph_face_and_encoding for characters that have this special
> face attribute will be all that's needed, perhaps together with some
> flag in the 'struct it' to make that faster.  Details later, when we
> know whether the idea works or not.
>
>
I compared the result between

#+begin_src elisp
  (set-char-table-range composition-function-table
    ?!
    '(["\\(!==\\)" 0 font-shape-gstring]))
#+end_src

and

#+begin_src elisp
  (set-char-table-range composition-function-table
    ?!
    '(["\\(!\\)" 0 font-shape-gstring]))

  (set-char-table-range composition-function-table
    ?=
    '(["\\(=\\)" 0 font-shape-gstring]))
#+end_src

They are different, only matching the sequence produces the desired result
for multi-character ligatures.

I read the hbfont.c code and the hb buffer is cleared every time handling
the shaping. I think it makes sense that it
should not store the state of the Emacs buffer in hb buffer, and HarfBuzz
needs to know the whole sequence to shape
according to their document.

I did some research on how other programs make use of HarfBuzz. They
typically put an entire paragraph or put a line
into the shaping function. It is quite an interesting way for Emacs to
detect a sequence first and specifically shape
that sequence using HarfBuzz. It might be historical reason but it seems a
lot more work needs to be done in composite.c
or we need to figure out something better.