|
From: | Gregory Heytings |
Subject: | bug#59347: 29.0.50; `:family` face setting ignored |
Date: | Wed, 07 Dec 2022 23:19:57 +0000 |
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.
In a sense the family name is just a meaningless string indeed. The names "Serif", "Sans Serif" and "Monospace" are just a convention. And indeed, Emacs trusts the font driver, why wouldn't it? And what else could it do? The font driver itself calculates the "monospace" or "proportional" property based on the actual glyph widths in each font (at least that's what Fontconfig does). When all glyphs have the same width, the font is considered monospace and its 'spacing' property is set to 100. Otherwise the font is considered proportional and its 'spacing' property is set to 0. And if for some reason Fontconfig does not set that property correctly for a font, it can be forced by the user.
I don't understand what you mean by "try deducing that from the font data Emacs records about each font". We could double-check that when we want a fixed-pitch font max-width == average-width == space-width, and when we want a variable-pitch font max-width != average-width != space-width, but what would be the benefit of doing that?
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.
The problem is precisely that currently it doesn't do that, when faces such as variable-pitch are realized. The font list returned by font_list_entities called in font_find_for_lface (called by font_load_for_lface, called by realize_gui_face) is erroneously limited to fonts which have the exact same width/slant/weight attributes as the default face (which is _not_ the font that is being realized). If the default face has for example weight = medium, and Emacs realizes the variable-pitch face whose only non-nil attribute (in emacs -Q) is "family = Sans Serif", and if there are no fonts in the Sans Serif family with a weight equal to medium, font_list_entities returns an empty list. Which is wrong.
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?
I don't understand your question. If we agree that there is an order of importance in the attributes of a font spec, and that the family is the most important one, it seems clear to me that keeping the family is more important than keeping the weight. What am I missing? By the way, that's what Emacs already does, in most cases (the exception being the current bug). If (in emacs -Q) you
(set-face-attribute 'variable-pitch nil :weight medium)and if (1) there are no fonts in the Sans Serif family with a medium weight on your system but (2) there are fonts in the Monospace family with a medium weight, Emacs will not suddenly select a fixed pitch font for the variable-pitch face.
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)?
That's not what the last patch I had sent did (see https://debbugs.gnu.org/cgi/bugreport.cgi?bug=59347#164). In that patch all these attributes (weight/slant/width) were set to nil.
However, after spending a few more hours on this, I concluded that the fix I proposed in that patch was placed a bit too low in the abstraction layers, and that it is safer to place it where it belongs, namely in realize_gui_face. font_find_for_lface has other callers, which may depend in subtle ways on its current behavior. I attach that new, and hopefully final, patch.
I checked in particular it with the recipes of bug#37473, bug#57555, bug#59347 and bug#59371, and with some variants. All seem to work correctly.
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?
The code in font_find_for_lface just above the final loop that looks up face-font-family-alternatives only populates the 'family' array. Nothing "concrete" happens in font_find_for_lface before the final loop: the code only populates the 'family', 'foundry', 'registry' and 'adstyle' arrays, as well as the 'work' font spec.
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.
Yes, that's still what I want to do, but in realize_gui_face and not in font_find_for_lface anymore.
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.
It is not necessary to change anything there, because the loop at the end of font_find_for_lface already relaxes these non-numerical attributes when necessary: if there is no font of any width/height/weight/slant for the specified family/foundry/registry/adstyle, Emacs tries to set adstyle to nil (if it isn't already), then to set registry to nil (if it isn't already), then to set foundry to nil (if it isn't already). And if doing that had no effect, Emacs tries the alternative family names listed in face-font-family-alternatives.
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?
The height (or size) attribute already receives a special treatment in font_find_for_lface, where it is already set to nil before being passed to font_list_entities.
Unset-the-weight-slant-width-in-the-spec-when-realiz.patch
Description: Text Data
[Prev in Thread] | Current Thread | [Next in Thread] |