0

I'd like to customize a bit mu4e in order to display nice utf-8 chars (or still better, colored emojis).

However, the problem is that the utf8 chars does not have the good width (even in the mono font), and may be bigger or smaller. So all the text after that char is shifted by a fraction of the normal width, which breaks the alignment. So I'm looking for a way to control spacing after displaying a big/small utf-8 char or picture. For example can I say something like:

  • "Display the utf-8 char ⚴ and leave space after until you arrive at the point that would be reached by replacing X by two characters with the default font width".
  • Or even better, would it be possible to say "Put the chars '⚓⚴❮' into at most 5 "normal" chars, fill with blank if needed, and then continue to write text as usual.
  • Or still better, "Put the chars '⚓⚴❮', fill with blank until you reach the next "block", and let me know how many spaces you used.

And if possible, I also want to do the same thing for images.

Thank you!

  • Perhaps you could elaborate on how the solution detailed in the discussion you have linked to relates/differs from this question, if you wish to avoid suggestions along similar lines? – phils Jun 21 '18 at 23:51
  • FWIW I don't have a solution, but I would guess that the compose-chars-after-function variable may be a worthwhile thing to look at. – phils Jun 21 '18 at 23:53
  • @phils Well the link I referred to tries to avoid the problem by finding fonts on which the error is not too visible (even by creating a new font!), but in my case it is really not enough, and djcb even concludes that it's a limitation of emacs. But I just found a solution that proove that it's not an emacs limitation, see below. And also, I did not get how to use compose-chars-after-function. – tobiasBora Jun 22 '18 at 1:21
1

I found a solution that basically uses this question to get the width of one char (based on font-get-glyphs), and then I use default-font-width to get the width of a column, and then I add a space using the display text property (I don't know why but I needed to create a new temporary variable to make sure it's a buffer-local variable, if you have a better code let me know).

;; https://emacs.stackexchange.com/questions/5495/how-can-i-determine-the-width-of-characters-on-the-screen
(defun my-char-width (char_str) 
  "Return the width in pixels of a character in the current
window's default font.  More precisely, this returns the
width of the letter ‘m’.  If the font is mono-spaced, this
will also be the width of all other printable characters."
  (let ((window (selected-window))
        (remapping face-remapping-alist))
    (with-temp-buffer
      (make-local-variable 'face-remapping-alist)
      (setq face-remapping-alist remapping)
      (set-window-buffer window (current-buffer))
      (insert char_str)
      (aref (aref (font-get-glyphs (font-at 1) 1 2) 0) 4))))

(defun insert_in_n_columns (cols_nb str)
  "Insert as much element of list_char as possible, until it goes
 out of n cols. 'Begin' is useless for the user."
  (cl-labels ((aux (rem_px rem_l)
           (if (null rem_l) ;; Empty list, fill with big space
               (progn
             ;; Cannot use the local variable...
             ;; Better way than using a tmp variable?
             (insert
              (propertize " "
                      'display
                      `(space . (:width (,rem_px))))))
             (progn
               ;; Get first element, remaining part,
               ;; and with of first elt
               (let* ((c (car rem_l))
                  (r (cdr rem_l))
                  (w (my-char-width c)))
             ;; If there is enough space for the char, insert it
             (if (<= w rem_px)
                 (progn
                   (insert c)
                   (aux (- rem_px w) r))
               (progn
                 (if (not (eq rem_px 0))
                 ;; Else fill with the good space
                 (progn
                   (insert
                    (propertize " "
                        'display
                        `(space . (:width (,rem_px)))))
                   )))))))))
    (aux (* cols_nb (default-font-width)) (string-to-list str))))

;; Demo:
;; (insert_in_n_columns 4 "ab⏚")
;; (insert_in_n_columns 4 "ab⚴d")
;; (insert_in_n_columns 4 "abc⏚d")

Demo:

enter image description here enter image description here

NB: the same idea can be used with images.

  • 1
    Instead of '(space . (:width (tmp_rem_px))) use `(space . (:width (,rem_px))) ? The function argument will be out of scope for subsequent text property evaluation, so you want to evaluate it while it is in scope. Your 'tmp' variable remains in scope (because it's not really temporary), but would also be clobbered by each call, so might be unreliable in practice? – phils Jun 22 '18 at 1:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.