What is a default font?

classic Classic list List threaded Threaded
22 messages Options
12
Reply | Threaded
Open this post in threaded view
|

What is a default font?

martin rudalics
I just noticed that my implementation of 'window-default-font-height'
is likely wrong.  The fault is all mine but I think that, in a sense,
'default-font-height', 'default-font-width', 'default-line-height' and
'face-font' are to blame as well.

In particular, the 'face-font' doc-string should tell that it returns
a value for the current buffer if FRAME is nil or omitted and a value
for FRAME if it is non-nil, disregarding the current buffer in that
case entirely.  If that's the correct interpretation of that function.
So far I can't make heads or tails of it.

The other functions are IMO ill-specified for the case where the
current buffer does not appear on the selected frame.  Using values
returned by 'frame-parameter', 'frame-char-height' and
'frame-char-width' in that case can be misleading and present simple
guesses at the best.

Personally, I would prefer two types of functions.  One for getting
the default face font of a buffer alone (including remapping) and one
for the default face font of a frame.  But I don't know enough about
fonts, their remapping and their relationship to frames to tell what
really makes sense here.

martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
> From: martin rudalics <[hidden email]>
> Date: Tue, 3 Dec 2019 19:36:41 +0100
>
> I just noticed that my implementation of 'window-default-font-height'
> is likely wrong.  The fault is all mine but I think that, in a sense,
> 'default-font-height', 'default-font-width', 'default-line-height' and
> 'face-font' are to blame as well.
>
> In particular, the 'face-font' doc-string should tell that it returns
> a value for the current buffer if FRAME is nil or omitted and a value
> for FRAME if it is non-nil, disregarding the current buffer in that
> case entirely.  If that's the correct interpretation of that function.

That is not entirely correct.  When called with FRAME nil or omitted,
face-font returns the value for the selected frame.

> The other functions are IMO ill-specified for the case where the
> current buffer does not appear on the selected frame.  Using values
> returned by 'frame-parameter', 'frame-char-height' and
> 'frame-char-width' in that case can be misleading and present simple
> guesses at the best.
>
> Personally, I would prefer two types of functions.  One for getting
> the default face font of a buffer alone (including remapping) and one
> for the default face font of a frame.  But I don't know enough about
> fonts, their remapping and their relationship to frames to tell what
> really makes sense here.

I don't think I understand what you need, or why.  There's no "face
for a buffer"; only face-remapping is buffer-local.  frame-char-height
take the value from the specified frame, not from a buffer.  It is
meaningless to ask about a font of a buffer without specifying the
frame on which it is, or will be, displayed.

You will see that functions which use the likes of default-font-height
are interested in the selected window on the selected frame.

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
 > I don't think I understand what you need, or why.  There's no "face
 > for a buffer"; only face-remapping is buffer-local.  frame-char-height
 > take the value from the specified frame, not from a buffer.  It is
 > meaningless to ask about a font of a buffer without specifying the
 > frame on which it is, or will be, displayed.
 >
 > You will see that functions which use the likes of default-font-height
 > are interested in the selected window on the selected frame.

But in that case the first line of the doc-string of
'default-font-height'

   Return the height in pixels of the current buffer's default face font.

is totally misleading because, as you noted above, asking for such a
value is meaningless when the current buffer is not displayed in the
selected window.  Please try to fix these doc-strings so they tell
what the functions actually do and not how they are used.

I would do that myself but I fail to see where 'face-font' asks about
the font property of a window.  It calls lookup_named_face with the
first argument NULL so that function should "consider only frame-level
face configuration" according to its comment.  Where does it take the
selected window and hence face remapping for that window's buffer into
account?  It's probably all deeply rooted in functions like
merge_face_ref but I'm completely lost there.

In either case, from what you said, the following problem apparently
cannot be resolved: If I have a buffer I want to display via
'display-buffer' with a specified number of lines, that buffer is not
yet displayed anywhere and some face remapping is in effect for that
buffer, I cannot specify how high the window showing that buffer
should be in terms of the remapped font's height.  Is that conclusion
correct?

Thanks, martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Štěpán Němec
On Wed, 4 Dec 2019 19:04:10 +0100
martin rudalics wrote:

>> You will see that functions which use the likes of default-font-height
>> are interested in the selected window on the selected frame.
>
> But in that case the first line of the doc-string of
> 'default-font-height'
>
>   Return the height in pixels of the current buffer's default face font.
>
> is totally misleading because, as you noted above, asking for such a
> value is meaningless when the current buffer is not displayed in the
> selected window.  Please try to fix these doc-strings so they tell
> what the functions actually do and not how they are used.
Indeed, FWIW I've been bitten by this recently, too, and have been
meaning to submit the patch attached, although now seeing this
discussion, more clarification (and from a more knowledgeable person)
might be needed. I simply corrected what seemed to be obvious errors,
but maybe it can at least serve as another evidence of the confusion.

--
Štěpán


From 4edb174e38b3fb61ea95f11a938e648fd789a2e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20N=C4=9Bmec?= <[hidden email]>
Date: Thu, 29 Aug 2019 20:06:47 +0200
Subject: [PATCH] Correct default-font-{height,width}, default-line-height doc
 strings

* doc/lispref/display.texi (Low-Level Font): Correct doc strings of
default-font-height and default-font-width.  They return information
for the selected frame, not the current buffer.

* lisp/simple.el (default-font-height, default-font-width,
default-line-height): Correct doc strings.
---
 doc/lispref/display.texi | 4 ++--
 lisp/simple.el           | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 1f7cc93c9c..6704c714e2 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -3934,12 +3934,12 @@ Low-Level Font
 
 @defun default-font-width
 This function returns the average width in pixels of the font used by
-the current buffer's default face.
+the selected frame's default face.
 @end defun
 
 @defun default-font-height
 This function returns the height in pixels of the font used by the
-current buffer's default face.
+selected frame's default face.
 @end defun
 
 @defun window-font-width &optional window face
diff --git a/lisp/simple.el b/lisp/simple.el
index 47ce0364d1..28a416fa2c 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -6183,7 +6183,7 @@ line-move-visual
 (declare-function font-info "font.c" (name &optional frame))
 
 (defun default-font-height ()
-  "Return the height in pixels of the current buffer's default face font.
+  "Return the height in pixels of the selected frame's default face font.
 
 If the default font is remapped (see `face-remapping-alist'), the
 function returns the height of the remapped face."
@@ -6198,7 +6198,7 @@ default-font-height
      (t (frame-char-height)))))
 
 (defun default-font-width ()
-  "Return the width in pixels of the current buffer's default face font.
+  "Return the width in pixels of the selected frame's default face font.
 
 If the default font is remapped (see `face-remapping-alist'), the
 function returns the width of the remapped face."
@@ -6217,9 +6217,9 @@ default-font-width
      (t (frame-char-width)))))
 
 (defun default-line-height ()
-  "Return the pixel height of current buffer's default-face text line.
+  "Return the pixel height of the selected frame's default-face text line.
 
-The value includes `line-spacing', if any, defined for the buffer
+The value includes `line-spacing', if any, defined for the current buffer
 or the frame."
   (let ((dfh (default-font-height))
  (lsp (if (display-graphic-p)
--
2.24.0

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Stefan Monnier
In reply to this post by martin rudalics
> In either case, from what you said, the following problem apparently
> cannot be resolved: If I have a buffer I want to display via
> 'display-buffer' with a specified number of lines, that buffer is not
> yet displayed anywhere and some face remapping is in effect for that
> buffer, I cannot specify how high the window showing that buffer
> should be in terms of the remapped font's height.

Yes, you can, but only if you know in which frame it will be displayed
(or you can approximate it by using a frame "similar" to the one that
will be used).


        Stefan


Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
In reply to this post by Štěpán Němec
 > Indeed, FWIW I've been bitten by this recently, too, and have been
 > meaning to submit the patch attached, although now seeing this
 > discussion, more clarification (and from a more knowledgeable person)
 > might be needed. I simply corrected what seemed to be obvious errors,
 > but maybe it can at least serve as another evidence of the confusion.

I think I got the idea now.  Many parts of the display engine don't
care about reality - this is one of them.  You have only to trick her
into believing that an arbitrary buffer is displayed in the selected
window of the selected frame and she will start to work on it.  So

(let ((buffer (current-buffer)))
   (with-selected-window window
     (with-current-buffer buffer
     ...

should get her telling us the default font, including remapping, for
the current buffer if it were displayed in WINDOW.  If WINDOW doesn't
exist yet, some workaround could be used to pass 'face-font' a t FRAME
argument.

But the innards of 'face-font' are, in fact, irrelevant: This function
just takes whatever has been set up by the caller, regardless of
whether the current buffer is shown in the selected window or not.
The problem is, that this concept contrasts with that of other Emacs
functions that require, for example, that a buffer is really a
window's buffer in order to work correctly.

And it strikes back when the display engine shows her innards, for
example, when she temporarily selects a window for drawing its mode
line and the user has no idea what the "really selected window" is.

martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
In reply to this post by Štěpán Němec
> From: Štěpán Němec <[hidden email]>
> Cc: Eli Zaretskii <[hidden email]>,  [hidden email]
> Date: Wed, 04 Dec 2019 20:38:26 +0100
>
>  @defun default-font-width
>  This function returns the average width in pixels of the font used by
> -the current buffer's default face.
> +the selected frame's default face.
>  @end defun

Thanks.  However, it is incorrect not to mention the current buffer at
all, because these functions are sensitive to face remapping, which is
a buffer-local feature.

I think Stefan and Martin in their followup messages described the
situation correctly.  I do agree that documentation could use some
clarification in this area.

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
In reply to this post by Stefan Monnier
 > Yes, you can, but only if you know in which frame it will be displayed
 > (or you can approximate it by using a frame "similar" to the one that
 > will be used).

I guess you're right.  (face-font 'default t) is a bummer, at least
here.  Probably because 'face-new-frame-defaults' is inherently
unspecified.  Does anyone know how to populate that?  Where is it
used?

martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
> Cc: Eli Zaretskii <[hidden email]>, [hidden email]
> From: martin rudalics <[hidden email]>
> Date: Thu, 5 Dec 2019 18:54:17 +0100
>
>  > Yes, you can, but only if you know in which frame it will be displayed
>  > (or you can approximate it by using a frame "similar" to the one that
>  > will be used).
>
> I guess you're right.  (face-font 'default t) is a bummer, at least
> here.  Probably because 'face-new-frame-defaults' is inherently
> unspecified.  Does anyone know how to populate that?  Where is it
> used?

Why are you bothered by that?  Why not (face-font 'default) instead?

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
 > Why are you bothered by that?  Why not (face-font 'default) instead?

Is that the default for a new frame?

martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
> Cc: [hidden email], [hidden email]
> From: martin rudalics <[hidden email]>
> Date: Thu, 5 Dec 2019 19:24:23 +0100
>
>  > Why are you bothered by that?  Why not (face-font 'default) instead?
>
> Is that the default for a new frame?

No.  For a frame that was not yet created there are too many unknowns
for face-font to be able to help you efficiently.  The best you can do
is assume the new frame will have the same faces as the selected one.

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
 > No.  For a frame that was not yet created there are too many unknowns
 > for face-font to be able to help you efficiently.  The best you can do
 > is assume the new frame will have the same faces as the selected one.

OK.  Couldn't you then fix the doc-string of 'face-font' accordingly?

Thanks, martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
> Cc: [hidden email], [hidden email]
> From: martin rudalics <[hidden email]>
> Date: Thu, 5 Dec 2019 19:59:11 +0100
>
>  > No.  For a frame that was not yet created there are too many unknowns
>  > for face-font to be able to help you efficiently.  The best you can do
>  > is assume the new frame will have the same faces as the selected one.
>
> OK.  Couldn't you then fix the doc-string of 'face-font' accordingly?

Will do.

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
I intend to rewrite 'window-default-font-height' as

(defun window-default-font-height (&optional window buffer)
   "Return height in pixels of BUFFER's default face font in WINDOW.
WINDOW must be a valid window and defaults to the selected one.
If BUFFER is a live buffer, it stands for itself.  Any other
value means to use WINDOW's buffer.  If neither WINDOW nor BUFFER
are live, signal an error.

The return value accounts for any remapping of the default face
font (see `face-remapping-alist') in BUFFER.  If no such
remapping has been specified for BUFFER, return the height of the
default face font for WINDOW's frame.

Note that if WINDOW specifes a live window and BUFFER specifies a
buffer different from the one currently shown in WINDOW, the
return value is calculated by pretending that BUFFER is displayed
in WINDOW."
   (let* ((window (window-normalize-window window))
          (frame (window-frame window))
          (buffer
           (cond
            ((buffer-live-p buffer) buffer)
            ((window-live-p window) (window-buffer window))
            (t (error "No live window and buffer specified"))))
          (default-font
            (if (window-live-p window)
                (with-selected-window window
                  (with-current-buffer buffer
                    (face-font 'default frame)))
              (with-selected-frame frame
                (with-current-buffer buffer
                  (face-font 'default frame))))))
     (cond
      ((not default-font)
       (frame-char-height frame))
      ((and (display-multi-font-p (frame-parameter frame 'display))
           (not (string-equal (frame-parameter frame 'font) default-font)))
       (aref (font-info default-font frame) 3))
      (t
       (frame-char-height frame)))))

If you think that my interpretation is correct, the doc-string of
'face-font' could maybe tell something similar.

And I'd still like to know what 'face-new-frame-defaults' is really
used for.

martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
> Cc: [hidden email], [hidden email]
> From: martin rudalics <[hidden email]>
> Date: Fri, 6 Dec 2019 09:35:46 +0100
>
>       ((and (display-multi-font-p (frame-parameter frame 'display))

display-multi-font-p can be passed the frame itself, you don't need to
pass the frame's display explicitly.

>   (not (string-equal (frame-parameter frame 'font) default-font)))
>        (aref (font-info default-font frame) 3))

Not sure I get the logic here: you use default-font if the frame's
font is NOT the default-font?

> And I'd still like to know what 'face-new-frame-defaults' is really
> used for.

The commentary at the beginning of xfaces.c says:

   There is also a global face alist `Vface_new_frame_defaults'.  Face
   definitions from this list are used to initialize faces of newly
   created frames.

If this is unclear, please ask more specific questions.

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
In reply to this post by Eli Zaretskii
> Date: Thu, 05 Dec 2019 21:13:56 +0200
> From: Eli Zaretskii <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> >  > No.  For a frame that was not yet created there are too many unknowns
> >  > for face-font to be able to help you efficiently.  The best you can do
> >  > is assume the new frame will have the same faces as the selected one.
> >
> > OK.  Couldn't you then fix the doc-string of 'face-font' accordingly?
>
> Will do.

Done.

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
In reply to this post by Eli Zaretskii
 >>   (not (string-equal (frame-parameter frame 'font) default-font)))
 >>         (aref (font-info default-font frame) 3))
 >
 > Not sure I get the logic here: you use default-font if the frame's
 > font is NOT the default-font?

I simply reused the specification from 'default-font-height' here with
FRAME specifying some frame, not necessarily the selected one.

           ;; Avoid calling font-info if the frame's default font was
           ;; not changed since the frame was created.  That's because
           ;; font-info is expensive for some fonts, see bug #14838.
           (not (string= (frame-parameter nil 'font) default-font)))
       (aref (font-info default-font) 3))

Do you see anything wrong with that?

 >> And I'd still like to know what 'face-new-frame-defaults' is really
 >> used for.
 >
 > The commentary at the beginning of xfaces.c says:
 >
 >     There is also a global face alist `Vface_new_frame_defaults'.  Face
 >     definitions from this list are used to initialize faces of newly
 >     created frames.
 >
 > If this is unclear, please ask more specific questions.

This variable has 258 x 21 elements all of them unspecified starting
like this here:

((custom-group-subtitle .
   [face unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified])
  (custom-group-tag .
   [face unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified])
  (custom-group-tag-1 .
   [face unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified])
  (custom-face-tag .
   [face unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified unspecified])
  ...

No matter what I do, the elements of each entry are to the best of my
knowledge nowhere changed and nowhere "really" used ('face-list' just
using the cars only).

The one potential use case I've seen (and obviously the use case that
would have interested me) is, when calling 'face-font' with the second
argument t in which case it calls lface_from_face_name with the first
argument NULL which then calls lface_from_face_name_no_resolve which
consults Vface_new_frame_defaults which, however, does never return
anything useful.  So the return value of 'face-font' is always nil
when the second argument is t.

So the specific question I ask is whether it makes sense to maintain
such a large variable that apparently never provides anything useful.

martin

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

martin rudalics
In reply to this post by Eli Zaretskii
 >>> OK.  Couldn't you then fix the doc-string of 'face-font' accordingly?
 >>
 >> Will do.
 >
 > Done.

Thanks.  But you left the doc-string of 'face-font' alone.  That
doc-string still says that

   If FRAME is t, report on the defaults for face FACE (for new frames).

which is wrong according to Stefan

   Yes, you can, but only if you know in which frame it will be displayed
   (or you can approximate it by using a frame "similar" to the one that
   will be used).

and you

   For a frame that was not yet created there are too many unknowns
   for face-font to be able to help you efficiently.  The best you can do
   is assume the new frame will have the same faces as the selected one.

So pretty please remove that line from the doc-string of 'face-font'
provided you still agree with what both of you said above.

What I'm still unsure about is the following paragraph in the Elisp
manual:

      If ‘face-remapping-alist’ is buffer-local, its local value takes
      effect only within that buffer.  If ‘face-remapping-alist’ includes
      faces applicable only to certain windows, by using the
      ‘(:filtered (:window PARAM VAL) SPEC)’, that face takes effect only
      in windows that match the filter conditions (*note Special
      Properties::).  To turn off face filtering temporarily, bind
      ‘face-filters-always-match’ to a non-‘nil’ value, then all face
      filters will match any window.

 From this I'd conclude that I might have to explicitly select a
specific window (and not just its frame) before calling any of the
functions 'default-font-height', 'face-font', ... in order to process
'face-remapping-alist' correctly.  Is that conclusion correct?  If so,
the descriptions of these functions should tell that.

Thanks, martin


Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
In reply to this post by martin rudalics
> Cc: [hidden email], [hidden email]
> From: martin rudalics <[hidden email]>
> Date: Sat, 7 Dec 2019 10:39:00 +0100
>
>  >>   (not (string-equal (frame-parameter frame 'font) default-font)))
>  >>         (aref (font-info default-font frame) 3))
>  >
>  > Not sure I get the logic here: you use default-font if the frame's
>  > font is NOT the default-font?
>
> I simply reused the specification from 'default-font-height' here with
> FRAME specifying some frame, not necessarily the selected one.
>
>   ;; Avoid calling font-info if the frame's default font was
>   ;; not changed since the frame was created.  That's because
>   ;; font-info is expensive for some fonts, see bug #14838.
>   (not (string= (frame-parameter nil 'font) default-font)))
>        (aref (font-info default-font) 3))

Please also "reuse" the comment there.  Assuming the function you are
working on is likely to be used frequently and from places that need
to work fast, that is, since this is just an optimization.

>  > The commentary at the beginning of xfaces.c says:
>  >
>  >     There is also a global face alist `Vface_new_frame_defaults'.  Face
>  >     definitions from this list are used to initialize faces of newly
>  >     created frames.
>  >
>  > If this is unclear, please ask more specific questions.
>
> This variable has 258 x 21 elements all of them unspecified starting
> like this here:

Right, that's what happens by default.

> So the specific question I ask is whether it makes sense to maintain
> such a large variable that apparently never provides anything useful.

It does provide some useful things:

 . It provides the list of faces to be created for each new frame.
 . If any Lisp calls internal-set-lisp-face-attribute with the 4th argument
   t, the corresponding attribute will be set in this list, thus
   providing a non-unspecified value for new frames.

Reply | Threaded
Open this post in threaded view
|

Re: What is a default font?

Eli Zaretskii
In reply to this post by martin rudalics
> Cc: [hidden email], [hidden email]
> From: martin rudalics <[hidden email]>
> Date: Sat, 7 Dec 2019 10:39:51 +0100
>
> Thanks.  But you left the doc-string of 'face-font' alone.  That
> doc-string still says that
>
>    If FRAME is t, report on the defaults for face FACE (for new frames).
>
> which is wrong according to Stefan
>
>    Yes, you can, but only if you know in which frame it will be displayed
>    (or you can approximate it by using a frame "similar" to the one that
>    will be used).
>
> and you
>
>    For a frame that was not yet created there are too many unknowns
>    for face-font to be able to help you efficiently.  The best you can do
>    is assume the new frame will have the same faces as the selected one.

This is a misunderstanding.  What I meant to say was that when a new
frame is created, we usually apply frame-parameters from all kinds of
sources, and that is what I referred to as "unknowns".  barring that,
the above line is accurate, albeit you may see it as useless in your
use case.

> What I'm still unsure about is the following paragraph in the Elisp
> manual:
>
>       If ‘face-remapping-alist’ is buffer-local, its local value takes
>       effect only within that buffer.  If ‘face-remapping-alist’ includes
>       faces applicable only to certain windows, by using the
>       ‘(:filtered (:window PARAM VAL) SPEC)’, that face takes effect only
>       in windows that match the filter conditions (*note Special
>       Properties::).  To turn off face filtering temporarily, bind
>       ‘face-filters-always-match’ to a non-‘nil’ value, then all face
>       filters will match any window.
>
>  From this I'd conclude that I might have to explicitly select a
> specific window (and not just its frame) before calling any of the
> functions 'default-font-height', 'face-font', ... in order to process
> 'face-remapping-alist' correctly.  Is that conclusion correct?  If so,
> the descriptions of these functions should tell that.

It depends on what you are going to use the results of
default-font-height etc. for.  And I'd consider it a bug, or at least
a misfeature, to have the default face filtered like that anyway.

12