Subject: Re: bug#59347: 29.0.50; `:family` face setting ignored

From: Eli Zaretskii <[email-masked]>
Date: Tue, 06 Dec 2022 16:22:05 +0200

> Date: Mon, 05 Dec 2022 23:30:48 +0000
> From: Gregory Heytings <[email-masked]>
> cc: [email-masked], [email-masked]
> 
> 
> I did not deserve such a public reprimand.  I do not care at all about 
> being right or wrong personally, the only thing I care about here is 
> improving Emacs.  I try to be as careful as possible, both in the code I 
> write and in the messages I post.  In particular, I avoid posting anything 
> that is intentionally insulting, ironical, or even simply humorous.
> 
> I already spent an unreasonable amount of time on this bug (I'd say easily 
> around 30 hours), although I have no personal interest in seeing it 
> resolved: I did not report it (or any of the bugs to which it is related), 
> and it does not and cannot affect me (I use the same fixed-pitch font 
> everywhere).
> 
> Nonetheless, I'll try to answer your questions:

Thank you.  I hope we can now finish up dealing with this issue.

> > where and how should Emacs get the indication that only variable-pitch 
> > fonts are acceptable in that case?
> 
> It is not Emacs that decides which fonts correspond to a "family = Sans 
> Serif" specification, it is the font driver.  In font_list_entities, Emacs 
> passes a font specification to a font driver, which returns a list of 
> fonts matching that specification.

That is also what I know, so we agree on this point.

I will note right here that Emacs has no way of knowing whether the fonts
returned by the font driver are or aren't variable-pitch.  In fact, AFAIR it
is a tricky and not very reliable to try deducing that from the font data
Emacs records about each font (see 'font-info').  We just blindly trust the
font driver to give us the appropriate list of fonts.  IOW, for Emacs the
family is just a meaningless string.

> If the specification passed to font_list_entities contains a non-nil
> width, weight or slant, that list is immediately filtered and all fonts
> that do not match these width, weight or slant exactly are removed from
> the list.

And you are saying that this filtering is wrong, yes?

> > why do you consider the family attribute of a face be more important 
> > than other attributes?  if not all the attributes of a spec are "equal" 
> > in their importance, which attributes are more important, and why?
> 
> Indeed, the attributes are not equal, in fact none of the attributes are 
> ever equal in their importance.  The family is the most important one, 
> followed by the foundry, the registry, the additional style (in that 
> order, see the loop at the end of font_find_for_lface in which Emacs tries 
> to make each of these attributes less specific in turn, starting with the 
> least important one, namely the additional style), followed by the width, 
> height (or size), weight, slant (in the order specified by the variable 
> face-font-selection-order).

That is not the relative importance of interest in the context of this
discussion, because Emacs already does look for a suitable font in the order
of the importance you describe.

My question was not about this basic relative importance, it was about
something else: when none of the fonts of the given FAMILY fits the font
spec, why do you consider keeping the family to be more important than
keeping the weight?

And another question: if we are to follow face-font-selection-order, to
observe the relative importance of the attributes as set by the user, then
why did your patch only consider relaxing the weight (which is in the
penultimate place in the order of importance), and not the slant (which is
the least important attribute, in the default order we use)?

> It is also in that loop (at the end of font_find_for_lface) that 
> face-font-family-alternatives are used.  If the generic "Sans Serif", 
> "Monospace" and "Monospace Serif" families that Emacs uses are not a 
> recognized by the font driver (IOW, if font_list_entities returns an empty 
> result for these families), Emacs falls back to some hard-coded, less 
> generic, family names.

I'm not sure I agree with this part of your description.  The code looks up
face-font-family-alternatives _before_ the loop in font_find_for_lface,
i.e., _before_ font_list_entities is called.  Where exactly do you see what
you describe above?

> > and if bold is fine when semi-bold was requested, what about other 
> > weights, like ultra-light -- are they also okay? if not, why not?
> 
> Yes, ultra-light is also okay.  If a program requests a font in the Sans 
> Serif family with a semi-bold weight, and the only available font on a 
> given system in that family is a ultra-light one, it's the best possible 
> match for that font specification.  It's up to the user to install a font 
> in the Sans Serif family which has a semi-bold variant on their system, if 
> they need a font in the Sans Serif family with a semi-bold variant (or to 
> install another font that is closer to semi-bold than ultra-light, e.g. 
> one with a bold variant).
> 
> >
> > what are the criteria here and with other similar attributes?
> >
> 
> The family, foundry, registry and additional style attributes are passed 
> "as is" to the font driver, which returns a list of fonts matching these 
> attributes.  The width, weight and/or slant are converted to numerical 
> values (with font-{width,weight,slant}-table), and font_score, called by 
> font_sort_entities, called by font_select_entity, which is applied on the 
> list of fonts returned by font_list_entities, selects the best match in 
> that list (according the the preferences in face-font-selection-order). 
> If the width, weight and/or slant were already passed to 
> font_list_entities, the list of fonts passed to font_select_entity 
> contains only fonts that match these width, weight and/or slant, and that 
> mechanism is bypassed.

IOW, you want to disable the filtering of candidate fonts in
font_list_entities, and instead consider _all_ the candidates, selecting the
best match for the numerical attributes: width, height, weight, and slant.
And you don't want to relax the non-numerical attributes (family, foundry,
registry, adstyle) unless there's really no font, of any
width/height/weight/slant, installed for the specified
family/foundry/registry/adstyle.  Is that right?

If that is what you want us to do, then I must ask at least about the
height: is it really reasonable to prefer _any_ height from the given
family, even if it's radically different from what was requested?

Also, the patch you suggested to install in
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=59347#77 doesn't do the above,
it is basically a minor semi-kludgey change of the existing code, which only
considers 'normal' weight when 'medium' was requested.  Why didn't you
submit a patch that follows your description and your critique to the
logical conclusion?

The rest of what I write below is based on the assumption that my
understanding of your critique of the current code is as I describe above;
if it is wrong, please ignore what's below, and please help me understand
what is it that you are actually proposing and I misunderstood.

So I see the following issues with your proposal (which AFAIU is different
from the patch you actually posted):

 . we will examine much more fonts than we do now: the current code only
   examines matching fonts and returns the first one that satisfies the
   spec; your proposal will require us to examine all of them, in order to
   find the best match out of many
 . your logic, which says that the family is so much more important than the
   other attributes is not necessarily correct in all the cases where this
   code is executed: I can easily imagine cases where the requested weight
   is so important that no other "close" weight will do, and the caller
   really wants to get an empty list rather than a deviant font

So I can only agree to installing the patch along the lines of the above
logic, i.e. to make the code relax the numerical attributes trying to keep
the family, on the following conditions:

 . we add an additional loop, like the one in font_find_for_lface, after the
   original one, and in that additional loop implement the examination of
   candidates without filtering then by numerical attributes up front; that
   additional loop will run only if the one before it came up with no fonts
   that match the family
 . whether the additional loop will actually run should be controlled by a
   variable exposed to Lisp, so that if this change causes regressions, we
   could easily find out this is the reason, and users could work around the
   regressions without rebuilding Emacs

OK?  And note that my agreement is not to the patch you posted, but to a
more general change in the logic of examining the candidate fonts.  This is
how I understand what you think Emacs should do; if I misunderstood, please
correct me.

Thanks.