Hi, I produced a patch that adds convenient capabilities for measuring font sizes and for calculating how many characters fit on a line given a window and a font. This is in response to bug#19395 and builds on Eli Zaretskii's recent extension of `font-info' (commit b1978229162b0d4c3b14d8ad8bff383eb3511969). I'm a first-time submitter and would appreciate some feedback and additional information. Question 1: The file CONTRIBUTING says that I should ask on the list for the procedure regarding the copyright assignment. I currently live in the US but I think I do not have resident status. Question 2: It there a 1-to-1 relationship of commit messages and ChangeLog entries? The file CONTRIBUTING makes it sound like that but I see that some entries in ChangeLog are quite heavy which makes me suspect that they may represent several commits. Description of the new features: The background for this work is that I need a reliable way to determine how many characters I can fit on a line in a given font and window. This information is needed for presenting data in table format, for instance, when displaying search results in Helm. `window-width' looks like a solution for that but there are two problems: it assumes the frame's default font when doing the calculations and it does not account for the column reserved for the continuation glyph. (If either one of the fringes is zero, a column is reserved but not when both fringes are non-zero.) The solution to this problem is the new function `window-max-chars-per-line' which optionally accepts a window and font argument. For measuring the width of a character in the given window and font, this function uses another new function, `window-font-width'. For completeness, I also added `window-font-height'. The latter function is different from the existing function `default-font-height' in simple.el because it takes the window and font into account. `default-font-height' assumes the default font (possibly remapped in the current buffer). `window-font-height' and `window-font-width' both take an optional window argument because the relevant buffer may be displayed in several frames at the same time and different frames may use different fonts. So the result depends not just on the face but also on the window. For completeness, I also added a function `default-font-width' in simple.el which was previously missing. Many thanks to Eli for providing crucial input while I worked on this. All errors are obviously my fault. Titus The patch: Implement functions for measuring fonts and max chars per line * simple.el (default-font-width): new function. * (default-font-height): elaborate doc-string * window.el (window-font-width): new function (window-font-height): new function (window-max-chars-per-line): new function --- lisp/simple.el | 24 +++++++++++++++++++++++- lisp/window.el | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/lisp/simple.el b/lisp/simple.el index 0fcd5db..72dcbb2 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -5377,7 +5377,10 @@ lines." (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 current buffer's default face font. + +If the default font is remapped (see `face-remapping-alist'), the +function returns the width of the remapped face." (let ((default-font (face-font 'default))) (cond ((and (display-multi-font-p) @@ -5388,6 +5391,25 @@ lines." (aref (font-info default-font) 3)) (t (frame-char-height))))) +(defun default-font-width () + "Return the width in pixels of the current buffer's default face font. + +If the default font is remapped (see `face-remapping-alist'), the +function returns the width of the remapped face." + (let ((default-font (face-font 'default))) + (cond + ((and (display-multi-font-p) + ;; 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))) + (let* ((info (font-info (face-font 'default))) + (width (aref info 11))) + (if (> width 0) + width + (aref info 10)))) + (t (frame-char-width))))) + (defun default-line-height () "Return the pixel height of current buffer's default-face text line. diff --git a/lisp/window.el b/lisp/window.el index c95b0d6..990f953 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1830,6 +1830,57 @@ optional argument PIXELWISE is passed to the functions." (window-body-width window pixelwise) (window-body-height window pixelwise))) +(defun window-font-width (&optional window face) + "Return average character width for the font of FACE used in WINDOW. +WINDOW must be a live window and defaults to the selected one. + +If FACE is nil or omitted, the default face is used. If FACE is +remapped (see `face-remapping-alist'), the function returns the +information for the remapped face." + (with-selected-window (window-normalize-window window t) + (let* ((face (if face face 'default)) + (info (font-info (face-font face))) + (width (aref info 11))) + (if (> width 0) + width + (aref info 10))))) + +(defun window-font-height (&optional window face) + "Return character height for the font of FACE used in WINDOW. +WINDOW must be a live window and defaults to the selected one. + +If FACE is nil or omitted, the default face is used. If FACE is +remapped (see `face-remapping-alist'), the function returns the +information for the remapped face." + (with-selected-window (window-normalize-window window t) + (let* ((face (if face face 'default)) + (info (font-info (face-font face)))) + (aref info 3)))) + +(defun window-max-chars-per-line (&optional window face) + "Return the number of characters that can be displayed on one line in WINDOW. +WINDOW must be a live window and defaults to the selected one. + +The character width of FACE is used for the calculation. If FACE +is nil or omitted, the default face is used. If FACE is +remapped (see `face-remapping-alist'), the function uses the +remapped face. + +This function is different from `window-body-width' in two +ways. First, it accounts for the portions of the line reserved +for the continuation glyph. Second, it accounts for the size of +the font." + (with-selected-window (window-normalize-window window t) + (let* ((window-width (window-body-width window t)) + (font-width (window-font-width window face)) + (ncols (/ window-width font-width))) + (if (and (display-graphic-p) + overflow-newline-into-fringe + (/= (frame-parameter nil 'left-fringe) 0) + (/= (frame-parameter nil 'right-fringe) 0)) + ncols + (1- ncols))))) + (defun window-current-scroll-bars (&optional window) "Return the current scroll bar types for WINDOW. WINDOW must be a live window and defaults to the selected one. -- 1.9.1