From: Gregory Heytings <gregory@heytings.org>
To: Eli Zaretskii <eliz@gnu.org>
Cc: Po Lu <luangruo@yahoo.com>,
monnier@iro.umontreal.ca, 59347@debbugs.gnu.org
Subject: bug#59347: 29.0.50; `:family` face setting ignored
Date: Mon, 12 Dec 2022 00:57:49 +0000 [thread overview]
Message-ID: <ec23ed3e95fa8ff23d0e@heytings.org> (raw)
In-Reply-To: <1a7e3acf3578520feda7@heytings.org>
Amazing. I thought this exhausting discussion was over, but no, Po Lu
came and "improved" the code that was agreed upon only a couple of hours
after it was pushed, disregarding this entire discussion, the docstring of
the variable that the commit introduced, the commit message, and without
asking any questions. How can that be right, how can that be acceptable?
Po Lu's commit said "there is no reason any user should have to think
about bitmasks" even though the docstring said "there is no reason to
change that value except for debugging purposes." (Of course, that
sentence was also removed from the "improved" version of the docstring.)
It is on purpose that that variable was a bitmask and not a list, it is on
purpose that that variable was used in a macro and not in a function:
there is no reason that each face realization in each Emacs instance
should spend unnecessary CPU cycles (a function call and traversing a
list) on a variable that should never (or hardly ever) be changed, and
that was introduced only to help debugging other potential problems in
that subtle area of Emacs' code.
The name of the variable was changed, and the docstring was "improved",
and became completely wrong. It is simply untrue that this is a list of
attributes that Emacs will "ignore when realizing a face", or in fact that
this changes anything fundamental in the way Emacs realizes faces.
Here is again, in every possible detail, but this time in a single post,
the analysis of this subtle bug, and the rationale of the patch that was
agreed upon. I explain this bug with an concrete example, with the
'variable-pitch' face and a font with a 'medium' weight. That example is
only meant to illustrate the bug: the same reasoning applies for other
faces and other font attributes (slant or width).
(1) The first (and perhaps most important) thing to note is that, before
or after this bug fix,
(set-face-attribute 'variable-pitch nil :weight 'medium)
does _not_ change the font that is used for the 'variable-pitch' face,
unless the font that Emacs would have selected for the 'variable-pitch'
face without that call to set-face-attribute happens too have a 'medium'
variant. Fonts with an explicit 'medium' variant are rare, so most likely
(that is, on most computers) after this call to set-face-attribute, the
weight of the font for the 'variable-pitch' face will still be 'regular'
(or 'normal'). IOW, when the weight/slant/width attribute of a face is
set, Emacs tries to find a good match among the available fonts, and does
not consider (and never considered) these attributes as strict
requirements, _even if they are explicitly specified in the face itself_.
Clearly, that should remain the case when these attributes are
_unspecified_ in a face.
(2) Now suppose that a user has, in their init file, the following line:
(set-face-attribute 'default nil :font "Source Code Pro" :weight 'medium)
That "Source Code Pro" font has more variants than most fonts, and in
particular it has a variant with a 'medium' weight, which is slightly
thicker than the 'regular' weight (the difference is probably only visible
on HiDPI screens). That apparently anecdotal detail is important: it
implies that the weight of the default face is set to 'medium' (see
below).
The 'variable-pitch' face is defined as follows in emacs -Q:
Family: Sans Serif
Foundry: unspecified
Width: unspecified
Height: unspecified
Weight: unspecified
Slant: unspecified
Note that, apart from the family, all other attributes are unspecified in
that face (which means implicitly that it should be similar, with respect
to its width/height/weight/slant, to the default face).
What happens when a buffer with a text with the 'variable-pitch' face is
displayed is this:
(2.1) face_at_buffer_position is called, and finds (with
Fget_text_property) that the face is 'variable-pitch'. A few lines below,
the following code:
/* Begin with attributes from the default face. */
memcpy (attrs, default_face->lface, sizeof(attrs));
copies the attributes of the default face into the Lisp_Object attrs. The
last statement of that function is:
return lookup_face (f, attrs);
which means that lookup_face is called with 'attrs' set the attributes of
the default face, merged with the attributes of the 'variable-pitch' face
which are, except for the family, unspecified. At that point, 'attrs' also
contains, in its 'font' slot, a font-spec corresponding to the default
face. Therefore lookup_face is called with attrs[weight] = 'medium' and
attrs[font][weight] = 'medium'. (Note that we now have _two_ copies of the
same information ("the weight of the default face is 'medium'") in the
same Lisp_Object.)
(2.2) lookup_face calls realize_face without changing its 'attr'
parameter, which means that it calls realize_face with attr[weight] =
'medium' and attrs[font][weight] = 'medium'.
(2.3) realize_face calls realize_gui_face without changing its 'attrs'
parameter, which means that it calls realize_gui_face with attrs[weight] =
'medium' and attrs[font][weight] = 'medium'.
(2.4) In realize_gui_face, the conditional 'if (! FONT_OBJECT_P
(attrs[LFACE_FONT_INDEX]))' is taken, because the 'font' slot of 'attrs'
contains a font-spec, not a font object (see 2.1).
(2.5) realize_gui_face calls copy_font_spec, which copies the font-spec
from the 'font' slot of 'attrs' into the Lisp_Object 'spec'. Therefore
spec[weight] = 'medium'. I'm explaining the bug, so for now I suppose
that the code that unsets weight in 'spec' isn't present.
(2.6) realize_gui_face calls font_load_for_lface, with 'attrs' (in which
attrs[weight] = 'medium' and attrs[font][weight] = 'medium'), and with
'spec' (in which spec[weight] = 'medium'). (Yes, there are now _three_
copies of the same information.)
(2.7) font_load_for_lface calls font_find_for_lface, with 'attrs' and
'spec' unmodified. Therefore attrs[weight] = 'medium',
attrs[font][weight] = 'medium', and spec[weight] = 'medium'.
(2.8) font_find_for_lface calls copy_font_spec, and puts a copy of the
font-spec 'spec' into the Lisp_Object 'work'. The weight slot of 'work'
is not modified in font_find_for_lface.
(2.9) font_find_for_lface calls (in the final loop) font_list_entities
with 'work'. Therefore font_list_entities is called with work[weight] =
'medium'. And, given that the 'variable-pitch' face is being realized, we
also have work[family] = Sans Serif.
(2.10) font_list_entities checks the weight slot of its 'spec' parameter,
it is set to 'medium', therefore need_filtering is set to true.
font_list_entities also populates the Lisp_Object 'scratch_font_spec' with
some of the attributes coming from its 'spec' parameter, in particular the
family. Note also that the weight slot of 'scratch_font_spec' is set to
nil. Therefore the 'list' function of the font driver is called with
scratch_font_spec[family] = Sans Serif and scratch_font_spec[weight] =
nil. This returns a list of candidate fonts from the Sans Serif family,
of any weight (given that scratch_font_spec[weight] = nil).
(2.11) needs_filtering is true, therefore font_delete_unmatched is called
with 'spec' (remember that spec[weight] = 'medium'). This removes all
fonts, from the list returned by the 'list' function, that do not have an
explicit 'medium' variant. As noted above, fonts with a 'medium' variant
are comparatively rare. On computers on which there are no fonts in the
Sans Serif family with an explicit 'medium' variant, font_delete_unmatch
returns an _empty_ list. Therefore font_list_entities returns an _empty_
list, even if there are fonts on the computer with a weight that is close
to the 'medium' weight, such as a 'regular' weight (see weight_table).
(2.12) Therefore, in font_find_for_lface, the call to font_select_entity,
which is supposed to "select the best match for PIXEL_SIZE and attributes
in ATTRS if there are several candidates", is bypassed. Remember that
attrs[weight] = 'medium', so if font_list_entities had for example
returned a list with one or more fonts in the Sans Serif family with a
'regular' weight, one of them would have been selected as the best match,
because attrs[weight] = 'medium'.
(2.13) The fact that attrs[weight] = 'medium' (and attrs[font][weight] =
'medium') in font_find_for_lface shows clearly that the 'medium' weight is
not at all "ignored" by Emacs when realizing the 'variable-pitch' face.
It is (or rather, should be) used to find a font that is similar, with
respect to its width/height/weight/slant, to the font of the default face.
Note that this is exactly what happens in case (1) above: when the weight
of the font of the default face is not 'medium', setting the weight of the
'variable-pitch' face to 'medium' does _not_ change the font of the
'variable-pitch' face, instead Emacs selects the best possible match among
the available fonts.
(2.14) Therefore font_find_for_lface tries to relax the
family/foundry/registry/adstyle attributes in 'work', until it finds a
font with a weight = 'medium'. All other fonts are rejected, which is
clearly wrong: the 'variable-pitch' face that is being realized does _not_
specify any weight. It is only a coincidence that the 'default' face has
a 'medium' variant, and that none of the available fonts in the Sans Serif
family have a 'medium' variant.
(2.15) In the case that triggered this bug, a font that is not in the Sans
Serif family but that happens to have a 'medium' variant is selected by
Emacs, such as a non-antialiased font, or a monospace font. In the worst
of the worst cases, even after trying to relax all
family/foundry/registry/adstyle attributes, no fonts with a 'medium'
variant has been found, and the 'variable-pitch' face becomes identical to
the default face.
With the bug fix, in (2.5), spec[weight] is set to nil. Therefore in
(2.10), need_filtering remains false, in (2.11) font_delete_unmatch is not
called and font_list_entities does not return an empty list, but instead
returns (as it should) a list of candidate fonts in the Sans Serif family,
and in (2.12) font_select_entity does the job it is supposed to do, and
selects the best candidate among those fonts and selects the font that is
as close as possible to attrs[weight] = 'medium'. Which is again exactly
what happens in case (1) above, when the height of the default face does
not happen to be 'medium'.
next prev parent reply other threads:[~2022-12-12 0:57 UTC|newest]
Thread overview: 123+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-18 4:57 bug#59347: 29.0.50; `:family` face setting ignored Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-18 12:37 ` Eli Zaretskii
2022-11-18 14:59 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-18 15:13 ` Eli Zaretskii
2022-11-18 15:25 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-18 16:54 ` Eli Zaretskii
2022-11-18 17:21 ` Eli Zaretskii
2022-11-18 20:00 ` Yuan Fu
2022-11-18 20:12 ` Yuan Fu
2022-11-18 21:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 7:21 ` Eli Zaretskii
2022-11-18 19:46 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-18 19:58 ` Eli Zaretskii
2022-11-18 20:55 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 7:15 ` Eli Zaretskii
2022-11-19 14:55 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 15:31 ` Eli Zaretskii
2022-11-19 16:01 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 16:16 ` Eli Zaretskii
2022-11-20 13:57 ` Gregory Heytings
2022-11-20 14:59 ` Eli Zaretskii
2022-11-20 15:35 ` Gregory Heytings
2022-11-20 15:54 ` Eli Zaretskii
2022-11-20 16:59 ` Gregory Heytings
2022-11-20 17:29 ` Eli Zaretskii
2022-11-20 17:43 ` Gregory Heytings
2022-11-20 17:58 ` Eli Zaretskii
2022-11-20 18:11 ` Gregory Heytings
2022-11-20 18:19 ` Eli Zaretskii
2022-11-20 19:45 ` Gregory Heytings
2022-11-20 20:01 ` Eli Zaretskii
2022-11-20 20:08 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-20 20:45 ` Gregory Heytings
2022-11-21 12:27 ` Eli Zaretskii
2022-11-20 18:30 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-20 18:53 ` Eli Zaretskii
2022-11-20 18:31 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-20 18:54 ` Eli Zaretskii
2022-11-20 21:49 ` Gregory Heytings
2022-11-21 12:51 ` Eli Zaretskii
2022-11-21 14:48 ` Gregory Heytings
2022-11-21 15:08 ` Eli Zaretskii
2022-11-21 23:34 ` Gregory Heytings
2022-11-22 0:34 ` Gregory Heytings
2022-11-22 3:05 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-22 7:59 ` Gregory Heytings
2022-11-22 13:38 ` Eli Zaretskii
2022-11-22 13:46 ` Gregory Heytings
2022-11-22 13:16 ` Eli Zaretskii
2022-11-22 13:38 ` Gregory Heytings
2022-11-22 14:38 ` Eli Zaretskii
2022-11-22 14:45 ` Gregory Heytings
2022-11-22 14:53 ` Eli Zaretskii
2022-11-22 15:41 ` Gregory Heytings
2022-11-22 17:44 ` Eli Zaretskii
2022-11-22 20:52 ` Gregory Heytings
2022-11-22 20:47 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-23 8:13 ` Gregory Heytings
2022-11-30 10:03 ` Gregory Heytings
2022-11-30 14:00 ` Eli Zaretskii
2022-11-30 15:38 ` Gregory Heytings
2022-12-04 14:21 ` Eli Zaretskii
2022-12-05 23:30 ` Gregory Heytings
2022-12-06 14:22 ` Eli Zaretskii
2022-12-07 11:00 ` Gregory Heytings
[not found] ` <d99c6016-3b32-1116-9ef1-43fe40a71a4@heytings.org>
2022-12-07 11:02 ` Gregory Heytings
2022-12-07 23:19 ` Gregory Heytings
2022-12-08 0:27 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-08 1:07 ` Gregory Heytings
2022-12-08 8:16 ` Eli Zaretskii
2022-12-08 14:59 ` Gregory Heytings
2022-12-08 15:13 ` Eli Zaretskii
[not found] ` <e1b79bb2-a3c5-2677-57d8-fb6db43dfd9@heytings.org>
2022-12-08 16:27 ` Gregory Heytings
2022-12-08 14:12 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-08 15:33 ` Gregory Heytings
2022-12-08 17:29 ` Drew Adams
2022-12-08 17:44 ` Eli Zaretskii
2022-12-08 5:32 ` Yuan Fu
2022-12-08 8:09 ` Eli Zaretskii
2022-12-08 14:17 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-08 14:49 ` Eli Zaretskii
2022-12-08 15:24 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-08 15:45 ` Eli Zaretskii
2022-12-08 8:03 ` Eli Zaretskii
2022-12-08 12:53 ` Gregory Heytings
2022-12-08 14:16 ` Eli Zaretskii
2022-12-08 15:17 ` Gregory Heytings
2022-12-08 15:42 ` Eli Zaretskii
2022-12-10 22:51 ` Gregory Heytings
2022-12-12 0:57 ` Gregory Heytings [this message]
2022-12-12 1:49 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-12 8:54 ` Gregory Heytings
2022-12-12 10:33 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-12 10:51 ` Gregory Heytings
2022-12-12 11:18 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-12 11:38 ` Gregory Heytings
2022-12-12 12:47 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-12 15:30 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-12 15:45 ` Eli Zaretskii
2022-12-12 15:07 ` Eli Zaretskii
2022-12-12 16:12 ` Gregory Heytings
2022-12-12 17:10 ` Eli Zaretskii
2022-12-12 21:28 ` Gregory Heytings
2022-12-13 11:58 ` Eli Zaretskii
2022-12-13 1:16 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-13 15:40 ` Eli Zaretskii
2022-12-04 14:23 ` Eli Zaretskii
2022-11-22 6:42 ` Gregory Heytings
2022-11-22 8:01 ` Gregory Heytings
2022-11-22 13:27 ` Eli Zaretskii
2022-11-22 12:38 ` Eli Zaretskii
2022-11-22 12:43 ` Gregory Heytings
2022-11-22 14:29 ` Eli Zaretskii
2022-11-22 14:39 ` Gregory Heytings
2022-11-22 14:52 ` Eli Zaretskii
2022-11-22 15:17 ` Gregory Heytings
2022-11-20 18:16 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-20 19:46 ` Gregory Heytings
2022-11-19 0:20 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 0:28 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 4:37 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 6:01 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-11-19 14:17 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ec23ed3e95fa8ff23d0e@heytings.org \
--to=gregory@heytings.org \
--cc=59347@debbugs.gnu.org \
--cc=eliz@gnu.org \
--cc=luangruo@yahoo.com \
--cc=monnier@iro.umontreal.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).