Package: emacs;
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.
View this message in rfc822 format
From: Eli Zaretskii <eliz <at> gnu.org> To: Jim Porter <jporterbugs <at> gmail.com> Cc: shipmints <at> gmail.com, 78621 <at> debbugs.gnu.org Subject: bug#78621: Pixelwise display specified spaces less precise than min-width Date: Fri, 30 May 2025 07:39:46 +0300
> 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).
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.