GNU bug report logs -
#78621
Pixelwise display specified spaces less precise than min-width
Previous Next
Reported by: Ship Mints <shipmints <at> gmail.com>
Date: Wed, 28 May 2025 19:46:01 UTC
Severity: normal
Tags: notabug
Done: Ship Mints <shipmints <at> gmail.com>
Bug is archived. No further changes may be made.
To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 78621 in the body.
You can then email your comments to 78621 AT debbugs.gnu.org in the normal way.
Toggle the display of automated, internal messages from the tracker.
Report forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 19:46:01 GMT)
Full text and
rfc822 format available.
Acknowledgement sent
to
Ship Mints <shipmints <at> gmail.com>
:
New bug report received and forwarded. Copy sent to
bug-gnu-emacs <at> gnu.org
.
(Wed, 28 May 2025 19:46:02 GMT)
Full text and
rfc822 format available.
Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
This came up as I've been working on improving vtable to operate well in
text-scaled buffers and getting things to align on pixel boundaries is
impacted by this rendering issue.
You can see this in text-scaled buffers when attempting to pad a string to
a specific pixel boundary. If a pixelwise specified-width space is
inserted after a string with variable-pitch glyphs, it doesn't honor the
net pixel width. That same string padded using min-width with the same
number of pixels works fine.
I've attached a -Q reproducer which I've run on 30.1 and a recent master
with the same results on macOS. On master, I run as "src/emacs -Q -l
repro-variable-pitch-pixels.el".
The reproducer shows that a variable-pitch string padded to a specific
pixel width using string+specified space is unreliable vs. string with a
min-width display property. Fixed-pitch glyphs seem fine and everything
works at scale 0. There are three buffers created: scale 0, scale -1,
scale +1.
I tried to find where in xdisp.c or in ns*.m this would manifest, but it
wasn't immediately obvious. I don't have access to another platform this
week, so I do not know if this is NS specific or also manifests with other
toolkits.
-Stephane
[Message part 2 (text/html, inline)]
[repro-variable-pitch-pixels.el (application/octet-stream, attachment)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 20:15:02 GMT)
Full text and
rfc822 format available.
Message #8 received at 78621 <at> debbugs.gnu.org (full text, mbox):
On 5/28/2025 12:44 PM, Ship Mints wrote:
> You can see this in text-scaled buffers when attempting to pad a string
> to a specific pixel boundary. If a pixelwise specified-width space is
> inserted after a string with variable-pitch glyphs, it doesn't honor the
> net pixel width. That same string padded using min-width with the same
> number of pixels works fine.
Are you sure that your implementation of 'string-pixel-width-scaled' is
correct? From a brief look, I'd expect that you get some jitter when
dividing '(default-font-width)' by '(frame-char-width)', since both are
rounded to the nearest whole number.
If I instead replace that call with:
(string-pixel-width str (current-buffer))
then everything looks ok to me. As of Emacs 31, passing a buffer does this:
If BUFFER is non-nil, use the face remappings, alternative and default
properties from that buffer when determining the width.
That should handle all the necessary work to get the string width after
applying text-scale remappings. I added that argument so I could use it
in 'visual-wrap-prefix-mode' for a very similar issue you're seeing. See
commit f70a6ea0ea86ef461e40d20664a75a92d02679ea.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 20:41:01 GMT)
Full text and
rfc822 format available.
Message #11 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On Wed, May 28, 2025 at 4:14 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
> On 5/28/2025 12:44 PM, Ship Mints wrote:
> > You can see this in text-scaled buffers when attempting to pad a string
> > to a specific pixel boundary. If a pixelwise specified-width space is
> > inserted after a string with variable-pitch glyphs, it doesn't honor the
> > net pixel width. That same string padded using min-width with the same
> > number of pixels works fine.
>
> Are you sure that your implementation of 'string-pixel-width-scaled' is
> correct? From a brief look, I'd expect that you get some jitter when
> dividing '(default-font-width)' by '(frame-char-width)', since both are
> rounded to the nearest whole number.
>
> If I instead replace that call with:
>
> (string-pixel-width str (current-buffer))
>
> then everything looks ok to me. As of Emacs 31, passing a buffer does this:
>
> If BUFFER is non-nil, use the face remappings, alternative and default
> properties from that buffer when determining the width.
>
> That should handle all the necessary work to get the string width after
> applying text-scale remappings. I added that argument so I could use it
> in 'visual-wrap-prefix-mode' for a very similar issue you're seeing. See
> commit f70a6ea0ea86ef461e40d20664a75a92d02679ea.
>
Thanks for that. I think I'm using that in other places.
But... Did you run the repro in question and it worked for you, or are you
saying that this worked for your case?
AFAICT, the repro demonstrates a different issue. With the identical
string-pixel-width, padding using specified-width spaces vs. min-width
behave differently, so this isn't due to jitter as the widths used are the
same for both cases.
-Stephane
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 22:13:02 GMT)
Full text and
rfc822 format available.
Message #14 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On 5/28/2025 1:40 PM, Ship Mints wrote:
> But... Did you run the repro in question and it worked for you, or are
> you saying that this worked for your case?
I edited your repro to use that call (see the diff below), and it all
looks good to me, as shown in the attached screenshots.
--- before.el 2025-05-28 14:58:33.646686778 -0700
+++ after.el 2025-05-28 14:58:42.551136757 -0700
@@ -12,8 +12,7 @@
(let ((pad-to-width 150))
(cl-labels
((string-pixel-width-scaled (str)
- (ceiling (* (string-pixel-width str)
- (/ (float (default-font-width))
(frame-char-width)))))
+ (string-pixel-width str (current-buffer)))
(insert-pad-to-width (str face)
(let* ((s (propertize str 'face face))
(pad-width (- pad-to-width
[before.png (image/png, attachment)]
[after.png (image/png, attachment)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 22:17:02 GMT)
Full text and
rfc822 format available.
Message #17 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On Wed, May 28, 2025 at 6:12 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
> On 5/28/2025 1:40 PM, Ship Mints wrote:
> > But... Did you run the repro in question and it worked for you, or are
> > you saying that this worked for your case?
>
> I edited your repro to use that call (see the diff below), and it all
> looks good to me, as shown in the attached screenshots.
>
> --- before.el 2025-05-28 14:58:33.646686778 -0700
> +++ after.el 2025-05-28 14:58:42.551136757 -0700
> @@ -12,8 +12,7 @@
> (let ((pad-to-width 150))
> (cl-labels
> ((string-pixel-width-scaled (str)
> - (ceiling (* (string-pixel-width str)
> - (/ (float (default-font-width))
> (frame-char-width)))))
> + (string-pixel-width str (current-buffer)))
> (insert-pad-to-width (str face)
> (let* ((s (propertize str 'face face))
> (pad-width (- pad-to-width
>
Interesting. The results got worse for me, not better.
What platform did you test on?
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 22:18:02 GMT)
Full text and
rfc822 format available.
Message #20 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On Wed, May 28, 2025 at 6:15 PM Ship Mints <shipmints <at> gmail.com> wrote:
> On Wed, May 28, 2025 at 6:12 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
>
>> On 5/28/2025 1:40 PM, Ship Mints wrote:
>> > But... Did you run the repro in question and it worked for you, or are
>> > you saying that this worked for your case?
>>
>> I edited your repro to use that call (see the diff below), and it all
>> looks good to me, as shown in the attached screenshots.
>>
>> --- before.el 2025-05-28 14:58:33.646686778 -0700
>> +++ after.el 2025-05-28 14:58:42.551136757 -0700
>> @@ -12,8 +12,7 @@
>> (let ((pad-to-width 150))
>> (cl-labels
>> ((string-pixel-width-scaled (str)
>> - (ceiling (* (string-pixel-width str)
>> - (/ (float (default-font-width))
>> (frame-char-width)))))
>> + (string-pixel-width str (current-buffer)))
>> (insert-pad-to-width (str face)
>> (let* ((s (propertize str 'face face))
>> (pad-width (- pad-to-width
>>
>
> Interesting. The results got worse for me, not better.
>
> What platform did you test on?
>
In any case, it still doesn't explain the root of the potential bug with
:width being different than :min-width despite the inputs being identical.
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 22:42:02 GMT)
Full text and
rfc822 format available.
Message #23 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On Wed, May 28, 2025 at 6:17 PM Ship Mints <shipmints <at> gmail.com> wrote:
> On Wed, May 28, 2025 at 6:15 PM Ship Mints <shipmints <at> gmail.com> wrote:
>
>> On Wed, May 28, 2025 at 6:12 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
>>
>>> On 5/28/2025 1:40 PM, Ship Mints wrote:
>>> > But... Did you run the repro in question and it worked for you, or are
>>> > you saying that this worked for your case?
>>>
>>> I edited your repro to use that call (see the diff below), and it all
>>> looks good to me, as shown in the attached screenshots.
>>>
>>> --- before.el 2025-05-28 14:58:33.646686778 -0700
>>> +++ after.el 2025-05-28 14:58:42.551136757 -0700
>>> @@ -12,8 +12,7 @@
>>> (let ((pad-to-width 150))
>>> (cl-labels
>>> ((string-pixel-width-scaled (str)
>>> - (ceiling (* (string-pixel-width str)
>>> - (/ (float (default-font-width))
>>> (frame-char-width)))))
>>> + (string-pixel-width str (current-buffer)))
>>> (insert-pad-to-width (str face)
>>> (let* ((s (propertize str 'face face))
>>> (pad-width (- pad-to-width
>>>
>>
>> Interesting. The results got worse for me, not better.
>>
>> What platform did you test on?
>>
>
> In any case, it still doesn't explain the root of the potential bug with
> :width being different than :min-width despite the inputs being identical.
>
Okay I applied the one-liner correctly this time and now I see good results
on my mac laptop.
My contention still stands, though, that with identical inputs, 'display
'space :width and 'display 'min-width should produce identical results.
Does that make sense?
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 22:44:02 GMT)
Full text and
rfc822 format available.
Message #26 received at 78621 <at> debbugs.gnu.org (full text, mbox):
On 5/28/2025 3:15 PM, Ship Mints wrote:
> Interesting. The results got worse for me, not better.
>
> What platform did you test on?
GNU/Linux.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Wed, 28 May 2025 23:08:04 GMT)
Full text and
rfc822 format available.
Message #29 received at 78621 <at> debbugs.gnu.org (full text, mbox):
On 5/28/2025 3:40 PM, Ship Mints wrote:
> Okay I applied the one-liner correctly this time and now I see good
> results on my mac laptop.
>
> My contention still stands, though, that with identical inputs, 'display
> 'space :width and 'display 'min-width should produce identical results.
> Does that make sense?
From my read of this bug, the issue was originally that these snippets
should have the same display width:
(concat "some text"
(propertize " " 'display
`(space :width (,(- width width-of-some-text)))))
(propertize "some text" 'display `(min-width ((,width))))
I agree with that, but only if 'width-of-some-text' is computed correctly.
For variable-pitch text, each character cell (I hope I'm using the right
term here) in the display is a different width, rounded to the nearest
integer, which means that computing the pixel-width as in your original
reproducer accumulates small rounding errors for every character.
That's not an issue for fixed-pitch text, since in that case you can
reduce this expression as follows:
(* (string-pixel-width str)
(/ (float (default-font-width)) (frame-char-width)))
;; For simple cases, the pixel width is just the length times the
;; frame's char width.
(* (length str) (frame-char-width)
(/ (float (default-font-width)) (frame-char-width)))
;; Cancel out (frame-char-width).
(* (length str) (float (default-font-width)))
;; Remove unnecessary float call.
(* (length str) (default-font-width))
Thanks to '(frame-char-width)' canceling out, that expression doesn't
accumulate rounding errors like it would for variable-pitch text.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 06:08:02 GMT)
Full text and
rfc822 format available.
Message #32 received at 78621 <at> debbugs.gnu.org (full text, mbox):
> Cc: 78621 <at> debbugs.gnu.org
> From: Ship Mints <shipmints <at> gmail.com>
> Date: Wed, 28 May 2025 16:40:26 -0400
>
> On Wed, May 28, 2025 at 4:14 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
>
> On 5/28/2025 12:44 PM, Ship Mints wrote:
> > You can see this in text-scaled buffers when attempting to pad a string
> > to a specific pixel boundary. If a pixelwise specified-width space is
> > inserted after a string with variable-pitch glyphs, it doesn't honor the
> > net pixel width. That same string padded using min-width with the same
> > number of pixels works fine.
>
> Are you sure that your implementation of 'string-pixel-width-scaled' is
> correct? From a brief look, I'd expect that you get some jitter when
> dividing '(default-font-width)' by '(frame-char-width)', since both are
> rounded to the nearest whole number.
>
> If I instead replace that call with:
>
> (string-pixel-width str (current-buffer))
>
> then everything looks ok to me. As of Emacs 31, passing a buffer does this:
>
> If BUFFER is non-nil, use the face remappings, alternative and default
> properties from that buffer when determining the width.
>
> That should handle all the necessary work to get the string width after
> applying text-scale remappings. I added that argument so I could use it
> in 'visual-wrap-prefix-mode' for a very similar issue you're seeing. See
> commit f70a6ea0ea86ef461e40d20664a75a92d02679ea.
>
> Thanks for that. I think I'm using that in other places.
>
> But... Did you run the repro in question and it worked for you, or are you saying that this worked for your
> case?
>
> AFAICT, the repro demonstrates a different issue. With the identical string-pixel-width, padding using
> specified-width spaces vs. min-width behave differently, so this isn't due to jitter as the widths used are the
> same for both cases.
I tried your recipe in Emacs 31. The variable-pitch text indeed
doesn't align, but the reason seems to be that the WIDTH value of
'(space :width WIDTH)' display property is miscalculated. In my case
the :min-width spec has the value (150), i.e. 150 pixels, as expected,
whereas in the '(space :width WIDTH)' spec WIDTH is (66) while the
width of the text "f:0123456789" on display is 72 pixels (measured
with posn-at-point). So the sum is 72 + 66 = 138 pixels, not 150.
The question is why is the value miscalculated. Perhaps
string-pixel-width-scaled doesn't do its job reliably with
variable-pitch fonts? I didn't have time to explore deeper, since you
guys seem to be looking into that already. Let me know if you find
something unexpected or need my further help.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 06:12:02 GMT)
Full text and
rfc822 format available.
Message #35 received at 78621 <at> debbugs.gnu.org (full text, mbox):
> Cc: 78621 <at> debbugs.gnu.org
> Date: Wed, 28 May 2025 15:42:56 -0700
> From: Jim Porter <jporterbugs <at> gmail.com>
>
> On 5/28/2025 3:15 PM, Ship Mints wrote:
> > Interesting. The results got worse for me, not better.
> >
> > What platform did you test on?
>
> GNU/Linux.
I tested on MS-Windows, but I don't think the platform matters. The
only platform-dependent part is which font is used for the
variable-pitch face; the rest of the code involved in this is
platform-independent.
Once again, the problem is not in :width vs :min-width, the problem is
in the pixel-width values given to these display specs; see my other
message in this thread.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 06:20:01 GMT)
Full text and
rfc822 format available.
Message #38 received at 78621 <at> debbugs.gnu.org (full text, mbox):
> Cc: 78621 <at> debbugs.gnu.org
> Date: Wed, 28 May 2025 16:07:48 -0700
> From: Jim Porter <jporterbugs <at> gmail.com>
>
> From my read of this bug, the issue was originally that these snippets
> should have the same display width:
>
> (concat "some text"
> (propertize " " 'display
> `(space :width (,(- width width-of-some-text)))))
>
> (propertize "some text" 'display `(min-width ((,width))))
>
> I agree with that, but only if 'width-of-some-text' is computed correctly.
>
> For variable-pitch text, each character cell (I hope I'm using the right
> term here) in the display is a different width, rounded to the nearest
> integer, which means that computing the pixel-width as in your original
> reproducer accumulates small rounding errors for every character.
I don't follow this argument. string-pixel-width uses the display
code, so it should return the exact same value in pixels as what the
actual display produces when the same characters are shown on the
screen. So whatever rounding happens (which I don't think it does,
since font glyphs have integer advance width), it happens the same in
both cases and should yield the same values. Or what am I missing?
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 16:39:01 GMT)
Full text and
rfc822 format available.
Message #41 received at 78621 <at> debbugs.gnu.org (full text, mbox):
On 5/28/2025 11:18 PM, Eli Zaretskii wrote:
> I don't follow this argument. string-pixel-width uses the display
> code, so it should return the exact same value in pixels as what the
> actual display produces when the same characters are shown on the
> screen. So whatever rounding happens (which I don't think it does,
> since font glyphs have integer advance width), it happens the same in
> both cases and should yield the same values. Or what am I missing?
The short version is that we should pass the current buffer to
'string-pixel-width' so that we can just use the display code to compute
the string's width with all the remappings applied, rather than having
the display code compute it without remappings and then trying to guess
what the remapping would do to the width.
Here's a longer version if it helps:
The problem is that 'text-scale-increase' modifies the text size by a
particular ratio; when zooming out by one step, that defaults to 1 /
text-scale-mode-step, or 5/6.
My fixed-pitch font is 8 pixels wide at the default size, so zooming out
one step means the pixel width per char is 8 * 5/6 = 6.666. Since the
advance width is an integer, we round up to 7 pixels. The original code
works for fixed-pitch fonts since '(default-font-width)' returns 7 in
this case, so the original expression:
(ceiling (* (string-pixel-width str)
(/ (float (default-font-width)) (frame-char-width))))
is equivalent (only in this simple case) to:
(* (string-width str) (default-font-width))
Hopefully that all makes sense.
For variable-pitch fonts, this is more complex. Our zoom ratio is still
5/6, but we apply that ratio per-character, rounding each time. For the
string "hi there", the pixel widths of each character are:
Scale h i _ t h e r e Total
0 8 4 4 5 8 8 5 8 50
-1 7 3 4 4 7 7 5 7 44
Since the error introduced by rounding each character is a bit
different, we get a different result between the width from the display
engine compared to the approximation in the first expression above where
we round only once at the very end.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 16:42:01 GMT)
Full text and
rfc822 format available.
Message #44 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On Thu, May 29, 2025 at 2:07 AM Eli Zaretskii <eliz <at> gnu.org> wrote:
> > Cc: 78621 <at> debbugs.gnu.org
> > From: Ship Mints <shipmints <at> gmail.com>
> > Date: Wed, 28 May 2025 16:40:26 -0400
> >
> > On Wed, May 28, 2025 at 4:14 PM Jim Porter <jporterbugs <at> gmail.com>
> wrote:
> >
> > On 5/28/2025 12:44 PM, Ship Mints wrote:
> > > You can see this in text-scaled buffers when attempting to pad a
> string
> > > to a specific pixel boundary. If a pixelwise specified-width space
> is
> > > inserted after a string with variable-pitch glyphs, it doesn't honor
> the
> > > net pixel width. That same string padded using min-width with the
> same
> > > number of pixels works fine.
> >
> > Are you sure that your implementation of 'string-pixel-width-scaled' is
> > correct? From a brief look, I'd expect that you get some jitter when
> > dividing '(default-font-width)' by '(frame-char-width)', since both are
> > rounded to the nearest whole number.
> >
> > If I instead replace that call with:
> >
> > (string-pixel-width str (current-buffer))
> >
> > then everything looks ok to me. As of Emacs 31, passing a buffer does
> this:
> >
> > If BUFFER is non-nil, use the face remappings, alternative and
> default
> > properties from that buffer when determining the width.
> >
> > That should handle all the necessary work to get the string width after
> > applying text-scale remappings. I added that argument so I could use it
> > in 'visual-wrap-prefix-mode' for a very similar issue you're seeing.
> See
> > commit f70a6ea0ea86ef461e40d20664a75a92d02679ea.
> >
> > Thanks for that. I think I'm using that in other places.
> >
> > But... Did you run the repro in question and it worked for you, or are
> you saying that this worked for your
> > case?
> >
> > AFAICT, the repro demonstrates a different issue. With the identical
> string-pixel-width, padding using
> > specified-width spaces vs. min-width behave differently, so this isn't
> due to jitter as the widths used are the
> > same for both cases.
>
> I tried your recipe in Emacs 31. The variable-pitch text indeed
> doesn't align, but the reason seems to be that the WIDTH value of
> '(space :width WIDTH)' display property is miscalculated. In my case
> the :min-width spec has the value (150), i.e. 150 pixels, as expected,
> whereas in the '(space :width WIDTH)' spec WIDTH is (66) while the
> width of the text "f:0123456789" on display is 72 pixels (measured
> with posn-at-point). So the sum is 72 + 66 = 138 pixels, not 150.
>
> The question is why is the value miscalculated. Perhaps
> string-pixel-width-scaled doesn't do its job reliably with
> variable-pitch fonts? I didn't have time to explore deeper, since you
> guys seem to be looking into that already. Let me know if you find
> something unexpected or need my further help.
>
Indeed the two methods used in core Emacs to adjust widths in text-scaled
buffers do not work reliably with variable-pitch fonts.
(/ (float (default-font-width)) (frame-char-width))
(expt text-scale-mode-step text-scale-mode-amount)
I will now rely solely on 'string-pixel-width' specifying the reference
buffer from which the display engine can derive the text scale.
In the end, this was an experiment gone wonky, a nonbug, and now solved.
Thanks, all,
-Stephane
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 16:43:02 GMT)
Full text and
rfc822 format available.
Message #47 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On Thu, May 29, 2025 at 12:38 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
> On 5/28/2025 11:18 PM, Eli Zaretskii wrote:
> > I don't follow this argument. string-pixel-width uses the display
> > code, so it should return the exact same value in pixels as what the
> > actual display produces when the same characters are shown on the
> > screen. So whatever rounding happens (which I don't think it does,
> > since font glyphs have integer advance width), it happens the same in
> > both cases and should yield the same values. Or what am I missing?
>
> The short version is that we should pass the current buffer to
> 'string-pixel-width' so that we can just use the display code to compute
> the string's width with all the remappings applied, rather than having
> the display code compute it without remappings and then trying to guess
> what the remapping would do to the width.
>
> Here's a longer version if it helps:
>
> The problem is that 'text-scale-increase' modifies the text size by a
> particular ratio; when zooming out by one step, that defaults to 1 /
> text-scale-mode-step, or 5/6.
>
> My fixed-pitch font is 8 pixels wide at the default size, so zooming out
> one step means the pixel width per char is 8 * 5/6 = 6.666. Since the
> advance width is an integer, we round up to 7 pixels. The original code
> works for fixed-pitch fonts since '(default-font-width)' returns 7 in
> this case, so the original expression:
>
> (ceiling (* (string-pixel-width str)
> (/ (float (default-font-width)) (frame-char-width))))
>
> is equivalent (only in this simple case) to:
>
> (* (string-width str) (default-font-width))
>
> Hopefully that all makes sense.
>
> For variable-pitch fonts, this is more complex. Our zoom ratio is still
> 5/6, but we apply that ratio per-character, rounding each time. For the
> string "hi there", the pixel widths of each character are:
>
> Scale h i _ t h e r e Total
> 0 8 4 4 5 8 8 5 8 50
> -1 7 3 4 4 7 7 5 7 44
>
> Since the error introduced by rounding each character is a bit
> different, we get a different result between the width from the display
> engine compared to the approximation in the first expression above where
> we round only once at the very end.
>
Yes, indeed. No more estimation. Just use the display engine directly.
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 16:51:01 GMT)
Full text and
rfc822 format available.
Message #50 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
tags 78621 notabug
close 78621
On Thu, May 29, 2025 at 12:41 PM Ship Mints <shipmints <at> gmail.com> wrote:
> On Thu, May 29, 2025 at 12:38 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
>
>> On 5/28/2025 11:18 PM, Eli Zaretskii wrote:
>> > I don't follow this argument. string-pixel-width uses the display
>> > code, so it should return the exact same value in pixels as what the
>> > actual display produces when the same characters are shown on the
>> > screen. So whatever rounding happens (which I don't think it does,
>> > since font glyphs have integer advance width), it happens the same in
>> > both cases and should yield the same values. Or what am I missing?
>>
>> The short version is that we should pass the current buffer to
>> 'string-pixel-width' so that we can just use the display code to compute
>> the string's width with all the remappings applied, rather than having
>> the display code compute it without remappings and then trying to guess
>> what the remapping would do to the width.
>>
>> Here's a longer version if it helps:
>>
>> The problem is that 'text-scale-increase' modifies the text size by a
>> particular ratio; when zooming out by one step, that defaults to 1 /
>> text-scale-mode-step, or 5/6.
>>
>> My fixed-pitch font is 8 pixels wide at the default size, so zooming out
>> one step means the pixel width per char is 8 * 5/6 = 6.666. Since the
>> advance width is an integer, we round up to 7 pixels. The original code
>> works for fixed-pitch fonts since '(default-font-width)' returns 7 in
>> this case, so the original expression:
>>
>> (ceiling (* (string-pixel-width str)
>> (/ (float (default-font-width)) (frame-char-width))))
>>
>> is equivalent (only in this simple case) to:
>>
>> (* (string-width str) (default-font-width))
>>
>> Hopefully that all makes sense.
>>
>> For variable-pitch fonts, this is more complex. Our zoom ratio is still
>> 5/6, but we apply that ratio per-character, rounding each time. For the
>> string "hi there", the pixel widths of each character are:
>>
>> Scale h i _ t h e r e Total
>> 0 8 4 4 5 8 8 5 8 50
>> -1 7 3 4 4 7 7 5 7 44
>>
>> Since the error introduced by rounding each character is a bit
>> different, we get a different result between the width from the display
>> engine compared to the approximation in the first expression above where
>> we round only once at the very end.
>>
>
> Yes, indeed. No more estimation. Just use the display engine directly.
>
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 17:15:01 GMT)
Full text and
rfc822 format available.
Message #53 received at 78621 <at> debbugs.gnu.org (full text, mbox):
> tags 78621 notabug
> close 78621
This works only when you add
Bcc: control <at> debbugs.gnu.org
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Thu, 29 May 2025 17:24:02 GMT)
Full text and
rfc822 format available.
Message #56 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
tags 78621 notabug
close 78621
On Thu, May 29, 2025 at 12:50 PM Ship Mints <shipmints <at> gmail.com> wrote:
> tags 78621 notabug
> close 78621
>
> On Thu, May 29, 2025 at 12:41 PM Ship Mints <shipmints <at> gmail.com> wrote:
>
>> On Thu, May 29, 2025 at 12:38 PM Jim Porter <jporterbugs <at> gmail.com>
>> wrote:
>>
>>> On 5/28/2025 11:18 PM, Eli Zaretskii wrote:
>>> > I don't follow this argument. string-pixel-width uses the display
>>> > code, so it should return the exact same value in pixels as what the
>>> > actual display produces when the same characters are shown on the
>>> > screen. So whatever rounding happens (which I don't think it does,
>>> > since font glyphs have integer advance width), it happens the same in
>>> > both cases and should yield the same values. Or what am I missing?
>>>
>>> The short version is that we should pass the current buffer to
>>> 'string-pixel-width' so that we can just use the display code to compute
>>> the string's width with all the remappings applied, rather than having
>>> the display code compute it without remappings and then trying to guess
>>> what the remapping would do to the width.
>>>
>>> Here's a longer version if it helps:
>>>
>>> The problem is that 'text-scale-increase' modifies the text size by a
>>> particular ratio; when zooming out by one step, that defaults to 1 /
>>> text-scale-mode-step, or 5/6.
>>>
>>> My fixed-pitch font is 8 pixels wide at the default size, so zooming out
>>> one step means the pixel width per char is 8 * 5/6 = 6.666. Since the
>>> advance width is an integer, we round up to 7 pixels. The original code
>>> works for fixed-pitch fonts since '(default-font-width)' returns 7 in
>>> this case, so the original expression:
>>>
>>> (ceiling (* (string-pixel-width str)
>>> (/ (float (default-font-width)) (frame-char-width))))
>>>
>>> is equivalent (only in this simple case) to:
>>>
>>> (* (string-width str) (default-font-width))
>>>
>>> Hopefully that all makes sense.
>>>
>>> For variable-pitch fonts, this is more complex. Our zoom ratio is still
>>> 5/6, but we apply that ratio per-character, rounding each time. For the
>>> string "hi there", the pixel widths of each character are:
>>>
>>> Scale h i _ t h e r e Total
>>> 0 8 4 4 5 8 8 5 8 50
>>> -1 7 3 4 4 7 7 5 7 44
>>>
>>> Since the error introduced by rounding each character is a bit
>>> different, we get a different result between the width from the display
>>> engine compared to the approximation in the first expression above where
>>> we round only once at the very end.
>>>
>>
>> Yes, indeed. No more estimation. Just use the display engine directly.
>>
>
[Message part 2 (text/html, inline)]
Added tag(s) notabug.
Request was from
Ship Mints <shipmints <at> gmail.com>
to
control <at> debbugs.gnu.org
.
(Thu, 29 May 2025 17:24:02 GMT)
Full text and
rfc822 format available.
bug closed, send any further explanations to
78621 <at> debbugs.gnu.org and Ship Mints <shipmints <at> gmail.com>
Request was from
Ship Mints <shipmints <at> gmail.com>
to
control <at> debbugs.gnu.org
.
(Thu, 29 May 2025 17:24:02 GMT)
Full text and
rfc822 format available.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Fri, 30 May 2025 04:40:01 GMT)
Full text and
rfc822 format available.
Message #63 received at 78621 <at> debbugs.gnu.org (full text, mbox):
> Date: Thu, 29 May 2025 09:38:15 -0700
> Cc: shipmints <at> gmail.com, 78621 <at> debbugs.gnu.org
> From: Jim Porter <jporterbugs <at> gmail.com>
>
> On 5/28/2025 11:18 PM, Eli Zaretskii wrote:
> > I don't follow this argument. string-pixel-width uses the display
> > code, so it should return the exact same value in pixels as what the
> > actual display produces when the same characters are shown on the
> > screen. So whatever rounding happens (which I don't think it does,
> > since font glyphs have integer advance width), it happens the same in
> > both cases and should yield the same values. Or what am I missing?
>
> The short version is that we should pass the current buffer to
> 'string-pixel-width' so that we can just use the display code to compute
> the string's width with all the remappings applied, rather than having
> the display code compute it without remappings and then trying to guess
> what the remapping would do to the width.
If the string's text is already in a buffer, we have
buffer-text-pixel-size and window-text-pixel-size for that purpose.
If the text is not in any buffer, then string-pixel-width should do
the job, and do it well. In particular, it strives to turn on/off all
the features that affect display as they are in the original buffer
(which can be passed to it as an optional argument), and applies all
the buffer-local settings that could matter. This includes face
remapping (which is how text-scaling is implemented).
So I still don't understand the nature of the problem. It is possible
that string-pixel-width should take some additional settings into
consideration, but in that case, we need to identify which settings it
currently ignores, and add that to the function to fix it. However,
your argument is not specific to some setting that isn't taken into
consideration, it is a general one, and that I don't understand, for
the simple reason that string-pixel-width uses the exact code used for
displaying text.
> The problem is that 'text-scale-increase' modifies the text size by a
> particular ratio; when zooming out by one step, that defaults to 1 /
> text-scale-mode-step, or 5/6.
text-scale-increase does that by using face-remapping-alist, which
string-pixel-width accounts for (at least is supposed to).
> My fixed-pitch font is 8 pixels wide at the default size, so zooming out
> one step means the pixel width per char is 8 * 5/6 = 6.666.
I don't think this is true. What face-remapping-alist does is it asks
Emacs to find a variant of the font with smaller or larger size.
Emacs then asks the font back-end to find a font that best fits that
size, and uses that. The size of the font is not necessarily what you
compute above, it is the best fit for that number.
But this is a tangent, see below.
> Since the advance width is an integer, we round up to 7 pixels. The
> original code works for fixed-pitch fonts since
> '(default-font-width)' returns 7 in this case, so the original
> expression:
>
> (ceiling (* (string-pixel-width str)
> (/ (float (default-font-width)) (frame-char-width))))
>
> is equivalent (only in this simple case) to:
>
> (* (string-width str) (default-font-width))
>
> Hopefully that all makes sense.
>
> For variable-pitch fonts, this is more complex. Our zoom ratio is still
> 5/6, but we apply that ratio per-character, rounding each time. For the
> string "hi there", the pixel widths of each character are:
>
> Scale h i _ t h e r e Total
> 0 8 4 4 5 8 8 5 8 50
> -1 7 3 4 4 7 7 5 7 44
>
> Since the error introduced by rounding each character is a bit
> different, we get a different result between the width from the display
> engine compared to the approximation in the first expression above where
> we round only once at the very end.
Why do you say that the result is different from the actual display?
Do you perhaps assume that string-pixel-width adds the character
widths "by hand", and thus accrues those round-off errors? If so,
that's not what string-pixel-width does. It actually computes the
layout of the string's text as it would be shown on display, using the
exact same code from the display engine used for actual display. IOW,
the pixel-width of the text is measured by the display layout code,
including asking the (scaled-up or scaled-down) font for the metrics
of the glyph of each character in the string, and computing the sum of
those metrics exactly like we do for displaying them. So any
round-off, such as it is, should be identical both on display and in
the results of string-pixel-width. Take a look at
window-text-pixel-size (the primitive which string-pixel-width calls)
to see how it does its job by calling the display-engine functions.
The only way I can understand your argument is that the original
recipe didn't pass the buffer to string-pixel-width (that additional
argument is new in Emacs 31), which might be the reason why it
attempted to account for text-scaling "by hand". But that's not how
this issue should be solved, which is why we added that argument in
Emacs 31.
If I'm missing something in the above reasoning, please point that
out, because I still think string-pixel-width should do its job in
this situation (barring any bugs in it that we should identify and
fix).
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Fri, 30 May 2025 20:02:02 GMT)
Full text and
rfc822 format available.
Message #66 received at 78621 <at> debbugs.gnu.org (full text, mbox):
On 5/29/2025 9:39 PM, Eli Zaretskii wrote:
> If the string's text is already in a buffer, we have
> buffer-text-pixel-size and window-text-pixel-size for that purpose.
> If the text is not in any buffer, then string-pixel-width should do
> the job, and do it well. In particular, it strives to turn on/off all
> the features that affect display as they are in the original buffer
> (which can be passed to it as an optional argument), and applies all
> the buffer-local settings that could matter. This includes face
> remapping (which is how text-scaling is implemented).
>
> So I still don't understand the nature of the problem.
The problem was simply that the original script to reproduce the issue
didn't pass the optional buffer argument to 'string-pixel-width', so the
relevant buffer-local settings (face remapping) weren't used. Instead,
the original script tried to approximate the remapping using its own
calculations which weren't quite the same as the display engine.
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Fri, 30 May 2025 20:17:01 GMT)
Full text and
rfc822 format available.
Message #69 received at 78621 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On Fri, May 30, 2025 at 4:01 PM Jim Porter <jporterbugs <at> gmail.com> wrote:
> On 5/29/2025 9:39 PM, Eli Zaretskii wrote:
> > If the string's text is already in a buffer, we have
> > buffer-text-pixel-size and window-text-pixel-size for that purpose.
> > If the text is not in any buffer, then string-pixel-width should do
> > the job, and do it well. In particular, it strives to turn on/off all
> > the features that affect display as they are in the original buffer
> > (which can be passed to it as an optional argument), and applies all
> > the buffer-local settings that could matter. This includes face
> > remapping (which is how text-scaling is implemented).
> >
> > So I still don't understand the nature of the problem.
>
> The problem was simply that the original script to reproduce the issue
> didn't pass the optional buffer argument to 'string-pixel-width', so the
> relevant buffer-local settings (face remapping) weren't used. Instead,
> the original script tried to approximate the remapping using its own
> calculations which weren't quite the same as the display engine.
>
Indeed. To be fair, I was trying to make this work for Emacs < 31 but I
won't bother anymore. The new features and all the bug fixes in vtable (a
lot of them), will be Emacs 31, IMO.
[Message part 2 (text/html, inline)]
Information forwarded
to
bug-gnu-emacs <at> gnu.org
:
bug#78621
; Package
emacs
.
(Sat, 31 May 2025 06:57:04 GMT)
Full text and
rfc822 format available.
Message #72 received at 78621 <at> debbugs.gnu.org (full text, mbox):
> Date: Fri, 30 May 2025 13:01:30 -0700
> Cc: shipmints <at> gmail.com, 78621 <at> debbugs.gnu.org
> From: Jim Porter <jporterbugs <at> gmail.com>
>
> On 5/29/2025 9:39 PM, Eli Zaretskii wrote:
> > If the string's text is already in a buffer, we have
> > buffer-text-pixel-size and window-text-pixel-size for that purpose.
> > If the text is not in any buffer, then string-pixel-width should do
> > the job, and do it well. In particular, it strives to turn on/off all
> > the features that affect display as they are in the original buffer
> > (which can be passed to it as an optional argument), and applies all
> > the buffer-local settings that could matter. This includes face
> > remapping (which is how text-scaling is implemented).
> >
> > So I still don't understand the nature of the problem.
>
> The problem was simply that the original script to reproduce the issue
> didn't pass the optional buffer argument to 'string-pixel-width', so the
> relevant buffer-local settings (face remapping) weren't used. Instead,
> the original script tried to approximate the remapping using its own
> calculations which weren't quite the same as the display engine.
Thanks, now everything is clear.
bug archived.
Request was from
Debbugs Internal Request <help-debbugs <at> gnu.org>
to
internal_control <at> debbugs.gnu.org
.
(Sat, 28 Jun 2025 11:24:11 GMT)
Full text and
rfc822 format available.
This bug report was last modified 49 days ago.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.