unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Titus von der Malsburg <malsburg@posteo.de>
To: emacs-devel@gnu.org
Subject: [PATCH] Implement functions for measuring fonts and max chars per line
Date: Mon, 22 Dec 2014 15:39:56 +0100	[thread overview]
Message-ID: <87k31j3g03.fsf@posteo.de> (raw)

[-- Attachment #1: Type: text/plain, Size: 7344 bytes --]


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



[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 472 bytes --]

             reply	other threads:[~2014-12-22 14:39 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-12-22 14:39 Titus von der Malsburg [this message]
2014-12-22 22:25 ` [PATCH] Implement functions for measuring fonts and max chars per line Titus von der Malsburg
2014-12-23 12:52   ` Titus von der Malsburg
2015-01-17 18:13     ` Titus von der Malsburg
2015-01-17 18:43       ` Eli Zaretskii
2015-01-17 20:38       ` Perry E. Metzger
2015-01-17 20:41         ` Eli Zaretskii
2015-01-17 22:21           ` Perry E. Metzger
2015-01-17 22:24             ` David Kastrup
2015-01-18  0:08               ` Perry E. Metzger
2015-01-18  0:11               ` Perry E. Metzger
2015-01-18  1:01                 ` Michael Heerdegen
2015-01-18  3:42             ` Eli Zaretskii
2015-01-18  4:08               ` Perry E. Metzger
2015-01-18 15:37                 ` Eli Zaretskii
2015-01-18 22:26                   ` Perry E. Metzger
2015-01-19  0:38                     ` Drew Adams
2015-01-19  2:56                       ` Perry E. Metzger
2015-01-19  3:41                         ` Eli Zaretskii
2015-01-19 13:31                           ` Perry E. Metzger
2015-01-19 16:21                             ` Eli Zaretskii
2015-01-19 21:49                               ` Perry E. Metzger
2015-01-19  2:01                     ` Michael Heerdegen
2015-01-19  3:37                     ` Eli Zaretskii
2015-03-21 10:33       ` Eli Zaretskii

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=87k31j3g03.fsf@posteo.de \
    --to=malsburg@posteo.de \
    --cc=emacs-devel@gnu.org \
    /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).