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).
(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))
(setq face-remapping-alist remapping)
(set-window-buffer window (current-buffer))
(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
;; Cannot use the local variable...
;; Better way than using a tmp variable?
(propertize " "
`(space . (:width (,rem_px))))))
;; 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)
(aux (- rem_px w) r))
(if (not (eq rem_px 0))
;; Else fill with the good space
(propertize " "
`(space . (:width (,rem_px)))))
(aux (* cols_nb (default-font-width)) (string-to-list str))))
;; (insert_in_n_columns 4 "ab⏚")
;; (insert_in_n_columns 4 "ab⚴d")
;; (insert_in_n_columns 4 "abc⏚d")
NB: the same idea can be used with images.