Subject: Re: bug#1808: 23.0.60; picture-mode not considering double-width characters alignment

From: Alan J Third <[email-masked]>
Date: Sun, 10 Jan 2016 20:11:45 +0000

Eli Zaretskii <[email-masked]> writes:

> The point is double-width characters, not necessarily CJK character,
> AFAIU.  There's a list of such characters in
> lisp/international/character.el (search for "full-width").
>
> Typing any of the characters for which the char-width-table entry
> holds 2 should exhibit the problem.

I hadn't realised there was such a thing, I had assumed it was two byte
unicode type characters. Now I know different. :)

> Yes, binding it to a key is the way to go.  And when inserting such a
> character, it looks like picture-mode does TRT: you will see in
> picture-insert that it looks at the character width.  But I think the
> bug report is not about inserting double-width characters, it's about
> replacing them with a single-width character.  If I type a
> double-width character, the alignment is kept reasonably well
> ("reasonably" because the double-width characters don't always take up
> exactly twice the number of pixels of a single-width character, the
> exact ration depends on the font being used for the double-width
> character).  To keep the alignment, picture-insert replaces 2
> single-width characters with the double-width one.  However, if I then
> replace it with a single-width character, the alignment is destroyed.
> And I think this bug report is about that latter use case.

It turns out that insertion sometimes does TRT, but not in the case
where you have a single-width char followed by a double-width, and you
try to overwrite the single-width char with a double-width. Both the
single and double-width chars are overwritten, and everything to the
right moves left one column, therefore going out of alignment.

I've written a small patch that I think fixes all these issues.

It works out how many columns the characters that are about to be
over-written take up, then deletes them as before, and finally inserts a
number of spaces to fill the gap (if they're needed). Then the old code
inserts the new character.

Spaces seemed the like the safest option, but it could be easily changed.

diff -c /Users/alan/src/emacs/picture.el /Users/alan/src/emacs/new-picture.el
*** /Users/alan/src/emacs/picture.el	2016-01-10 18:26:02.000000000 +0000
--- /Users/alan/src/emacs/new-picture.el	2016-01-10 19:51:53.000000000 +0000
***************
*** 272,278 ****
  	(or (eolp)
  	    (let ((pos (point)))
  	      (move-to-column col t)
! 	      (delete-region pos (point)))))
        (insert ch)
        (forward-char -1)
        (picture-move))))
--- 272,282 ----
  	(or (eolp)
  	    (let ((pos (point)))
  	      (move-to-column col t)
! 	      (let ((old-width (string-width (buffer-substring pos (point)))))
! 		(delete-region pos (point))
! 		(when (> old-width width)
! 		  (insert-char ?  (- old-width width))
! 		  (goto-char pos))))))
        (insert ch)
        (forward-char -1)
        (picture-move))))

Diff finished.  Sun Jan 10 19:51:57 2016

-- 
Alan Third