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

From: Gregory Heytings <[email-masked]>
Date: Mon, 05 Dec 2022 23:30:48 +0000

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:

>
> why do you think Emacs must return only variable-pitch fonts for a spec 
> that includes family = Sans Serif?
>

In emacs -Q, the only attribute specified in the variable-pitch face is 
its family: "Sans Serif".  There are three canonical, cross-platform, 
generic font families: "Sans Serif" and "Serif", whose names do not 
contain the word "proportional" or "variable pitch" but which contain only 
variable pitch fonts, and "Monospace", which (as its name implies, and as 
opposed to the Sans Serif and Serif families) contains only fixed pitch 
fonts.  Emacs (by default) only uses the Sans Serif and Monospace generic 
font families.

There is no standard way to make a distiction between monospace fonts with 
and without serifs.  For its fixed-pitch-serif face, which is inherited by 
Info-quoted and tex-verbatim, Emacs 25 invented another generic font 
family: "Monospace Serif".

>
> 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.  When Emacs passes the specification 
"family = Sans Serif" (or "family = Monospace") to the font driver, it 
receives a list of all available fonts in that family back.  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.

>
> 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).

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.  For example, if "family = Monospace" isn't 
recognized, Emacs tries "family = courier", and if "family = courier" 
isn't recongized either, Emacs tries "family = fixed".  As noted above, 
the "Monospace Serif" family is specific to Emacs, so for that generic 
family name Emacs will always fall back to the less generic family names 
corresponding to that generic family name in that variable.

>
> 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.

In the worst case, the one which is the cause of this bug, when 
font_list_entities is called for example with "family = Sans Serif, weight 
= medium", font_list_entities returns an empty list, even when there are 
plenty of fonts in the Sans Serif families that would be good candidates 
according to the scoring mechanism, and therefore Emacs falls back to 
using face-font-family-alternatives, which is only meant to be used on 
systems in which the generic font families are not recognized.  And in the 
worst of the worst cases, even using face-font-family-alternatives does 
not help (IOW, font_list_entities keeps returning an empty list), in which 
case Emacs falls back to using the default face.