Add an option to adjust the underline position. #490

Closed
opened 6 months ago by brocellous · 12 comments

I would like to place the underline one pixel lower for visual clarity. Attached pics of foot and termite to show the difference.

I would like to place the underline one pixel lower for visual clarity. Attached pics of foot and termite to show the difference.
Owner

Before deciding on that, I'd like to find out if foot should be doing something different out of the box.

  • Have you customized the underline position in termite?
  • Which font are you using?

The last one will allow me to check if the font provides metrics for underline positioning, or if foot had to estimate it.

Before deciding on that, I'd like to find out if foot should be doing something different out of the box. * Have you customized the underline position in termite? * Which font are you using? The last one will allow me to check if the font provides metrics for underline positioning, or if foot had to estimate it.
Poster

I haven't set any option in the termite config to adjust the underline – I don't think there is one.

In foot I've set "font = monospace:size=7.5"

$ fc-match monospace
DejaVuSansMono.ttf: "DejaVu Sans Mono" "Book"
$ foot
[...]
info: fcft.c:257: fcft: 2.3.3 +graphemes
info: fcft.c:267: fontconfig: 2.13.93
info: fcft.c:273: freetype: 2.10.4
info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono-Oblique.ttf: size=7.50pt/14px, dpi=140.00
info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono.ttf: size=7.50pt/14px, dpi=140.00
info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono-BoldOblique.ttf: size=7.50pt/14px, dpi=140.00
info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono-Bold.ttf: size=7.50pt/14px, dpi=140.00
info: terminal.c:649: cell width=9, height=17
[...]

In termite I set "font = monospace 11" and I'm not sure why the size is interpreted differently but any size=7.2 to size=7.7 in foot matches exactly.

I don't know which tables/metrics might be relevant in the font, but I tried it out on alacritty to see if maybe termite was being weird. Alacritty has the same underline placement as termite (with 1px space, image attached), and the same line placement as foot.

I haven't set any option in the termite config to adjust the underline – I don't think there is one. In foot I've set "font = monospace:size=7.5" ``` $ fc-match monospace DejaVuSansMono.ttf: "DejaVu Sans Mono" "Book" $ foot [...] info: fcft.c:257: fcft: 2.3.3 +graphemes info: fcft.c:267: fontconfig: 2.13.93 info: fcft.c:273: freetype: 2.10.4 info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono-Oblique.ttf: size=7.50pt/14px, dpi=140.00 info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono.ttf: size=7.50pt/14px, dpi=140.00 info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono-BoldOblique.ttf: size=7.50pt/14px, dpi=140.00 info: fcft.c:717: /usr/share/fonts/TTF/DejaVuSansMono-Bold.ttf: size=7.50pt/14px, dpi=140.00 info: terminal.c:649: cell width=9, height=17 [...] ``` In termite I set "font = monospace 11" and I'm not sure why the size is interpreted differently but any size=7.2 to size=7.7 in foot matches exactly. I don't know which tables/metrics might be relevant in the font, but I tried it out on alacritty to see if maybe termite was being weird. Alacritty has the same underline placement as termite (with 1px space, image attached), and the same line placement as foot.
Owner

In termite I set "font = monospace 11" and I'm not sure why the size is interpreted differently but any size=7.2 to size=7.7 in foot matches exactly.

Foot (by default, can be changed) uses your monitor's actual DPI when translating from a point size to an actual pixel size. Not many other applications do that (they assume a DPI of 96).

DejaVuSansMono.ttf: "DejaVu Sans Mono" "Book"

This font has built in underline positioning data, and foot uses it as is:

dbg: fcft.c:367: using underline metrics from font
dbg: fcft.c:383: underline: pos=-0.664062, thick=0.703125 -> pos=-0.312500, pos=0, thick=1

However, perhaps one can argue that the underline should be offseted with at least one pixel.

> In termite I set "font = monospace 11" and I'm not sure why the size is interpreted differently but any size=7.2 to size=7.7 in foot matches exactly. Foot (by default, can be changed) uses your monitor's actual DPI when translating from a point size to an actual pixel size. Not many other applications do that (they assume a DPI of 96). > DejaVuSansMono.ttf: "DejaVu Sans Mono" "Book" This font has built in underline positioning data, and foot uses it as is: ``` dbg: fcft.c:367: using underline metrics from font dbg: fcft.c:383: underline: pos=-0.664062, thick=0.703125 -> pos=-0.312500, pos=0, thick=1 ``` However, perhaps one can argue that the underline should be offseted with _at least_ one pixel.
Owner

dbg: fcft.c:383: underline: pos=-0.664062, thick=0.703125 -> pos=-0.312500, pos=0, thick=1

The first numbers are from the font. pos corresponds to the line's centerpoint, and thus we need to take its thickness into account when translating to a pixel offset. That's what you seen in -> pos x. The last numbers are the rounded integer values used when rendering.

> dbg: fcft.c:383: underline: pos=-0.664062, thick=0.703125 -> pos=-0.312500, pos=0, thick=1 The first numbers are from the font. `pos` corresponds to the line's centerpoint, and thus we need to take its thickness into account when translating to a pixel offset. That's what you seen in `-> pos x`. The last numbers are the rounded integer values used when rendering.
Poster

I see, thanks.

However, perhaps one can argue that the underline should be offseted with at least one pixel.

Looking at alacritty in particular, they seem to ensure the line's base is no higher than baseline + descent - thickness. In this case that's below (baseline + position - thickness/2) so it gets moved down. Or equivalently (I think?), the top of the line should never be above the baseline + descent. Does that sound reasonable for foot?

I see, thanks. > However, perhaps one can argue that the underline should be offseted with at least one pixel. Looking at alacritty in particular, they seem to ensure the line's base is no higher than baseline + descent - thickness. In this case that's below (baseline + position - thickness/2) so it gets moved down. Or equivalently (I think?), the top of the line should never be above the baseline + descent. Does that sound reasonable for foot?
Owner

Looking at alacritty in particular, they seem to ensure the line's base is no higher than baseline + descent - thickness.

I'm looking at Alacritty right now, but I'm not sure I see that... In fact, it looks exactly like foot's positioning code. With one exception: Alacritty uses floats all the way to the end, where they have an absolute y value. There they ceil() it. Foot calculates an integer based y offset when loading the font, and then adds that to the baseline when rendering.

We get the same result if we floor() our underline's y offset. Now, doing this breaks the estimated positioning I'm doing for fonts lacking underline positioning (at least the font I'm using). So that algorithm would have to be revisited before I can push a patch.

In the mean time, feel free to test this patch (it does the aforementioned floor():ing). Apply to fcft (can be built as a subproject in foot, if you don't want to patch the system installed fcft):

diff --git a/fcft.c b/fcft.c
index 5281fed..8ea5f1e 100644
--- a/fcft.c
+++ b/fcft.c
@@ -376,7 +376,7 @@ underline_strikeout_metrics(FT_Face ft_face, struct fcft_font *font)
      * When rounding the thickness, take care not go below 1.0 as that
      * would make it invisible.
      */
-    font->underline.position = trunc(underline_position + underline_thickness / 2.);
+    font->underline.position = floor(underline_position + underline_thickness / 2.);
     font->underline.thickness = round(max(1., underline_thickness));
 
     LOG_DBG("underline: pos=%f, thick=%f -> pos=%f, pos=%d, thick=%d",
> Looking at alacritty in particular, they seem to ensure the line's base is no higher than baseline + descent - thickness. I'm looking at Alacritty right now, but I'm not sure I see that... In fact, it looks exactly like foot's positioning code. With one exception: Alacritty uses floats all the way to the end, where they have an absolute y value. There they `ceil()` it. Foot calculates an integer based y offset when loading the font, and then adds that to the baseline when rendering. We get the same result if we `floor()` our underline's y offset. Now, doing this breaks the estimated positioning I'm doing for fonts lacking underline positioning (at least the font I'm using). So that algorithm would have to be revisited before I can push a patch. In the mean time, feel free to test this patch (it does the aforementioned `floor()`:ing). Apply to fcft (can be built as a subproject in foot, if you don't want to patch the system installed fcft): ```diff diff --git a/fcft.c b/fcft.c index 5281fed..8ea5f1e 100644 --- a/fcft.c +++ b/fcft.c @@ -376,7 +376,7 @@ underline_strikeout_metrics(FT_Face ft_face, struct fcft_font *font) * When rounding the thickness, take care not go below 1.0 as that * would make it invisible. */ - font->underline.position = trunc(underline_position + underline_thickness / 2.); + font->underline.position = floor(underline_position + underline_thickness / 2.); font->underline.thickness = round(max(1., underline_thickness)); LOG_DBG("underline: pos=%f, thick=%f -> pos=%f, pos=%d, thick=%d", ```
Poster

My bad, I think I misinterpreted alacritty and you are correct. The signedness of the metrics and the orientation of the y values confused me at first. The patch works as well.

My bad, I think I misinterpreted alacritty and you are correct. The signedness of the metrics and the orientation of the y values confused me at first. The patch works as well.
Owner

So... I've been comparing Foot (unpatched), Alacritty, Kitty and pango-view. As it turns out, Alacritty is the odd one here:

https://codeberg.org/attachments/68bf6b2b-ccc4-4945-858f-aeb1ac9dbdb7

I also tried blowing up the font size to 124 in pango-view and foot, and the underlines were still pixel identical.

But then again, QT appears to be doing something completely different:

https://codeberg.org/attachments/8eecb422-fa5b-4935-b702-8f40e7e6e16e

Quite frankly, it looks like it ignores the positioning data from the font... Given all this I'm somewhat hesitant to changing the defaults in fcft. However, I personally agree that placing the underline on the baseline looks bad.

Given that, I guess an option is the way to go. But in what form? If it simply specifies the absolute position, then it might look weird when you zoom in (i.e. increase the font size). Or is a pixel-based offset ("add this to the font's position) good enough?

So... I've been comparing Foot (unpatched), Alacritty, Kitty and pango-view. As it turns out, Alacritty is the odd one here: ![https://codeberg.org/attachments/68bf6b2b-ccc4-4945-858f-aeb1ac9dbdb7](https://codeberg.org/attachments/68bf6b2b-ccc4-4945-858f-aeb1ac9dbdb7) I also tried blowing up the font size to 124 in pango-view and foot, and the underlines were still pixel identical. But then again, QT appears to be doing something completely different: ![https://codeberg.org/attachments/8eecb422-fa5b-4935-b702-8f40e7e6e16e](https://codeberg.org/attachments/8eecb422-fa5b-4935-b702-8f40e7e6e16e) Quite frankly, it looks like it ignores the positioning data from the font... Given all this I'm somewhat hesitant to changing the defaults in fcft. However, I personally agree that placing the underline **on** the baseline looks bad. Given that, I guess an option is the way to go. But in what form? If it simply specifies the absolute position, then it might look weird when you zoom in (i.e. increase the font size). Or is a pixel-based offset ("add this to the font's position) good enough?
Poster

Well a pixel-based offset would be satisfactory for me, at least.

I noticed that in pango-view, the underline is a darker color than the others. Maybe that is because the line thickness is technically under 1.0 px.

Well a pixel-based offset would be satisfactory for me, at least. I noticed that in pango-view, the underline is a darker color than the others. Maybe that is because the line thickness is technically under 1.0 px.
Owner

Maybe that is because the line thickness is technically under 1.0 px.

Very likely.

For some sizes, I saw a 2px, darker line, which I believe is cairo centering a 1px line between two pixel rows.

> Maybe that is because the line thickness is technically under 1.0 px. Very likely. For some sizes, I saw a 2px, darker line, which I believe is cairo centering a 1px line between two pixel rows.
dnkl added the
enhancement
easy
labels 6 months ago
Poster

I tried kitty as well and it is pixel identical to termite for me at size 11. At size 10 the space vanishes though, whereas in termite it does not.

EDIT: Here is the final line-up (size 11 still). foot, termite, alacritty, kitty, pango-view

foot
termite
alacritty
kitty
pango-view

I tried kitty as well and it is pixel *identical* to termite for me at size 11. At size 10 the space vanishes though, whereas in termite it does not. EDIT: Here is the final line-up (size 11 still). foot, termite, alacritty, kitty, pango-view ![foot](https://codeberg.org/attachments/3550eb98-f44c-4bc2-83ca-5918249cd2ed) ![termite](https://codeberg.org/attachments/296d40bd-45a8-4ce7-9edf-2296d3609035) ![alacritty](https://codeberg.org/attachments/f244987a-11c7-455d-8f64-4d954d3a2a60) ![kitty](https://codeberg.org/attachments/4d024dab-10d2-47ea-9269-1714c1724ffb) ![pango-view](https://codeberg.org/attachments/4f31968a-446c-445d-9827-785152230ef4)
Owner

Kind of expected, in a sense; underlines aren't handled by FreeType, but is something applications need to render themselves. That leads to differences.

Since we're rounding floating point fractional pixel values to integers, there will almost certainly be font sizes where two applications produce different results, unless they calculate the offset in exactly the same way.

Kind of expected, in a sense; underlines aren't handled by FreeType, but is something applications need to render themselves. That leads to differences. Since we're rounding floating point fractional pixel values to integers, there will almost certainly be font sizes where two applications produce different results, unless they calculate the offset in **exactly** the same way.
dnkl added this to the 1.8.0 milestone 5 months ago
dnkl referenced this issue from a commit 4 months ago
dnkl closed this issue 4 months ago
dnkl referenced this issue from a commit 4 months ago
Sign in to join this conversation.
No Milestone
No Assignees
2 Participants
Notifications
Due Date

No due date set.

Dependencies

This issue currently doesn't have any dependencies.

Loading…
There is no content yet.