Subject: 30.1; Unexpected Results from window-text-pixel-size
From: AKIYAMA Kouhei <[email-masked]>
To: [email-masked]
Date: Mon, 24 Feb 2025 16:16:45 +0900
List-Id: <debbugs-submit.debbugs.gnu.org>

Dear Emacs Developers,

Thank you for developing and maintaining Emacs. I have encountered
some unexpected behavior in the `window-text-pixel-size` function and
would like to share my findings in case they are helpful.

I tested this on Emacs 30.1 for MS-Windows, launched with the
following commands:

wget https://ftp.gnu.org/gnu/emacs/windows/emacs-30/emacs-30.1.zip
unzip emacs-30.1.zip
cd bin
./emacs -Q

* Issue 1: Zero Width Returned in Certain Cases

Depending on the values of `header-line-format` and `truncate-lines`,
`window-text-pixel-size` sometimes returns a width of zero. This can
be reproduced as follows:

;; --------------------------------------------------------
;; 1. Display the header line in the scratch buffer.
(setq header-line-format "header")

;; 2. Evaluate the following code in the scratch buffer:
(require 'cl-lib)
(with-temp-buffer
  ;; Set buffer-local variables:
  (setq truncate-lines t) ;; Without this, the problem will not occur
  ;; (setq header-line-format "") ;; If uncomment this, the problem will not occur

  ;; Insert text
  (insert "  -rw-rw-rw-  1 ***** none  8541546 18-02-01 17:43 01 - test test test test test.mp3
  -rw-rw-rw-  1 ***** none 10519534 18-02-01 17:42 01 - test test test.mp3
")

  ;; Get width before file names
  (save-window-excursion ;; Same method as `shr-pixel-column'
    (set-window-dedicated-p nil nil)
    (set-window-buffer nil (current-buffer))

    (goto-char (point-min))
    (cl-loop while (re-search-forward "01 - " nil t)
             collect (window-text-pixel-size
                      nil
                      (line-beginning-position)
                      (match-beginning 0) 100000))))

;; Result:
((408 . 16) (0 . 16))

;; Expected
((408 . 16) (408 . 16))
;; --------------------------------------------------------

* Issue 2: Negative Width Returned When Full-Width Characters Are Present

If the buffer contains full-width characters, `window-text-pixel-size`
sometimes returns negative widths. This can be reproduced as follows:

;; --------------------------------------------------------
;; 1. Evaluate the following code in the scratch buffer:
;; Note: あ = \u3042 (HIRAGANA LETTER A)
(require 'cl-lib)
(with-temp-buffer
  (insert "aaaaaaaaaaaaaaaaaaaaaaaa\n"
          "あああbbbbbbbbbbbbbbbbbbbb\n"
          "aaaaaaaaaaaaaaaaaaaaaaaa\n"
          "あああbbbbbbbbbbbbbbbbbbbb\n"
          "aaaaaaaaaaaaaaaaaaaaaaaa\n"
          "あああbbbbbbbbbbbbbbbbbbbb\n")
  (save-window-excursion
    (set-window-buffer nil (current-buffer))
    (goto-char (point-min))
    (cl-loop until (eobp)
             if (eolp)
             concat "\n"
             else
             concat (format " %s" (car (window-text-pixel-size
                                        nil (point) (1+ (point)))))
             do (forward-char))))

;; Result
" 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 14 14 14 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 -32 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 14 14 14 8 8 -50 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 8 8 8 8 8 -80 8 8 8 8 8 8 8 8 8 8 8 8
 14 14 14 8 8 8 8 8 8 8 8 -98 8 8 8 8 8 8 8 8 8 8 8
"
;; A strange width is returned from the line after a full-width
;; character appears.

;; Expected
" 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 14 14 14 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 14 14 14 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 14 14 14 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
"
;; --------------------------------------------------------

* Workaround

Both issues occur when measuring the size of a partial text segment
within a buffer. However, I found that narrowing the buffer to the
target range before measuring avoids the problem:

;; --------------------------------------------------------
(with-temp-buffer
  (insert "aaaaaaaaaaaaaaaaaaaaaaaa\n"
          "あああbbbbbbbbbbbbbbbbbbbb\n"
          "aaaaaaaaaaaaaaaaaaaaaaaa\n"
          "あああbbbbbbbbbbbbbbbbbbbb\n"
          "aaaaaaaaaaaaaaaaaaaaaaaa\n"
          "あああbbbbbbbbbbbbbbbbbbbb\n")
  (save-window-excursion
    (set-window-buffer nil (current-buffer))
    (goto-char (point-min))
    (cl-loop until (eobp)
             if (eolp)
             concat "\n"
             else
             concat (format " %s"
                            ;; [Workaround]
                            (save-restriction
                              (narrow-to-region (point) (1+ (point)))
                              (car (window-text-pixel-size nil (point) (1+ (point))))))
             do (forward-char))))
;; --------------------------------------------------------

* Additional Context

I am developing a package that displays file details on the right side
of file names in Dired. This package needs to determine the width of
icons (inserted by `all-the-icons-dired` or `nerd-icons-dired`) and
thumbnails (inserted by `image-dired`). I use `window-text-pixel-size`
for this purpose.

Relevant code:
https://github.com/misohena/dired-details-r/blob/5510aae2fb0b2fb1c55ef2c471c84ccd65359f35/dired-details-r.el#L379

Since I have already implemented a workaround using narrowing, this
issue does not block my development. However, I wanted to report it in
case it is useful.

Best regards,
--
# This email was machine-translated from Japanese.
AKIYAMA Kouhei

--
In GNU Emacs 30.1 (build 2, x86_64-w64-mingw32) of 2025-02-24 built on
 AVALON
Windowing system distributor 'Microsoft Corp.', version 10.0.26100
System Description: Microsoft Windows 10 Enterprise (v10.0.2009.26100.3194)

Configured using:
 'configure --with-modules --without-dbus --with-native-compilation=aot
 --without-compress-install --with-tree-sitter CFLAGS=-O2
 prefix=/g/rel/install/emacs-30.1'

Configured features:
ACL GIF GMP GNUTLS HARFBUZZ JPEG LCMS2 LIBXML2 MODULES NATIVE_COMP
NOTIFY W32NOTIFY PDUMPER PNG RSVG SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER WEBP XPM ZLIB

Important settings:
  value of $LANG: ja_JP.CP932
  locale-coding-system: cp932

Major mode: Lisp Interaction

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr cl-extra help-mode warnings icons compile comint
ansi-osc ansi-color ring comp-run bytecomp byte-compile comp-common rx
emacsbug message mailcap yank-media puny dired dired-loaddefs rfc822 mml
mml-sec password-cache epa derived epg rfc6068 epg-config gnus-util
text-property-search time-date subr-x mm-decode mm-bodies mm-encode
mail-parse rfc2231 mailabbrev gmm-utils mailheader cl-loaddefs cl-lib
sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils
japan-util rmc iso-transl tooltip cconv eldoc paren electric uniquify
ediff-hook vc-hooks lisp-float-type elisp-mode mwheel touch-screen
dos-w32 ls-lisp disp-table term/w32-win w32-win w32-vars term/common-win
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock
font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq
simple cl-generic indonesian philippine cham georgian utf-8-lang
misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms
cp51932 hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese composite emoji-zwj charscript charprop case-table
epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button
loaddefs theme-loaddefs faces cus-face macroexp files window
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget keymap hashtable-print-readable backquote threads
w32notify w32 lcms2 multi-tty move-toolbar make-network-process
native-compile emacs)

Memory information:
((conses 16 81254 32625) (symbols 48 10237 0) (strings 32 21939 6240)
 (string-bytes 1 585829) (vectors 16 13241)
 (vector-slots 8 312318 15570) (floats 8 26 37)
 (intervals 56 3640 2771) (buffers 992 13))