Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cascadia Code ligature rendering issues #3504

Closed
kgizdov opened this issue Apr 19, 2021 · 34 comments
Closed

Cascadia Code ligature rendering issues #3504

kgizdov opened this issue Apr 19, 2021 · 34 comments
Labels

Comments

@kgizdov
Copy link

kgizdov commented Apr 19, 2021

Describe the bug
Release 0.20.1 renders some ligatures incorrectly. For example:

###########################################

looks like this:
hashes
while:

>>--->--||--<<--|-->--||---<<---|

looks like:
dashes

There seems to be extra spaces here and there and some missing characters too.

To Reproduce
Steps to reproduce the behavior:

  1. install kitty 0.20.1
  2. install Cascadia Code 2102.25
  3. set Cascadia Code as default font family
  4. See error

Expected behavior
There should be no spaces or missing characters from the render.

Screenshots
It should look like so:
cascadia-code-reference

Environment details
OS: Arch Linux

$ kitty --debug-config
kitty 0.20.1 created by Kovid Goyal
Linux yavin4 5.11.15-arch1-2 #1 SMP PREEMPT Sat, 17 Apr 2021 00:22:30 +0000 x86_64
Arch Linux \r (\l)
LSB_VERSION=1.4
DISTRIB_ID=Arch
DISTRIB_RELEASE=rolling
DISTRIB_DESCRIPTION="Arch Linux"
Loaded config files: /home/gizdov/.config/kitty/kitty.conf
Running under: X11

Config options different from defaults:
bold_font             CascadiaCode Bold
bold_italic_font      CascadiaCode BoldItalic
cursor                Color(red=237, green=212, blue=0)
font_family           CascadiaCode
font_size             12.5
foreground            Color(red=237, green=212, blue=0)
italic_font           CascadiaCode Italic
scrollback_lines      20000
strip_trailing_spaces smart
symbol_map            {(8277, 8277): 'NotoColorEmoji'}
tab_bar_edge          1
Added shortcuts:
	alt+ctrl+- KeyAction(func='change_font_size', args=(True, '-', 2.0))
	alt+ctrl+= KeyAction(func='change_font_size', args=(True, '+', 2.0))
	alt+ctrl+backspace KeyAction(func='change_font_size', args=(True, None, 0.0))
Removed shortcuts:
	shift+ctrl+- KeyAction(func='change_font_size', args=(True, '-', 2.0))
	shift+ctrl+= KeyAction(func='change_font_size', args=(True, '+', 2.0))
	shift+ctrl+backspace KeyAction(func='change_font_size', args=(True, None, 0.0))

Additional context
This was working fine in 0.20.0 with this patch applied on top only.

@kgizdov kgizdov added the bug label Apr 19, 2021
@kovidgoyal
Copy link
Owner

kitty doesnt support infinite length ligatures for performance reasons. It has a max ligature size of 9 characters, IIRC. Anything longer and you will get some rendering artifacts. That has always been the case.

@kgizdov
Copy link
Author

kgizdov commented May 11, 2021

wait, so does it support it now or not? I'm a bit confused by the referenced commit. Is there a code patch I can backport to 0.20.3?

@kovidgoyal
Copy link
Owner

It does and no there is no single patch

@Luflosi
Copy link
Contributor

Luflosi commented May 11, 2021

Yes, this is supported now, see 3328711. To back-port it, you would need to use several commits after that one. If you want to use this feature now, just compile from master, that will be easier than backporting.

@kgizdov
Copy link
Author

kgizdov commented May 11, 2021

cool!

@kgizdov
Copy link
Author

kgizdov commented Jun 30, 2021

hey, I just got the kitty 0.21.2 on arch and ligatures do look better, however, I'm getting some weird behaviour with Cascadia Code 2105.24. For example, when I have a few # it works fine:
short_hash
but when it gets longer one of the hashes starts to disappear bit by bit at the end:
medium_hash
and if it becomes very long, like in a progress bar, the hashes start to disappear one by one and only empty space it rendered:
long_hash
the longer the string the bigger the gap:
longest_hash
but the cursor is in the correct place, while symbols disappear visually. However, if the string overflows on the next line, then it starts anew - first few characters are rendered correctly and longer strings start missing hashes.
It does happen for all symbols that I've tried, not just hashes.
Is that expected behaviour?

@kovidgoyal
Copy link
Owner

yes it is, basically the ligature takes up less length than individual characters, since with individual characters there is spacing between each character. So you will get the full ligature being shorter than the individual characters one after another.

@kgizdov
Copy link
Author

kgizdov commented Jun 30, 2021

my only concern was the artefacting at the end, where a ligature does get truncated, even though is should extend further. I think Cascadia Code did purport infinite ligatures support for the first time and something to do with mono spacing. Like, if you look on the second picture, there is one ligature occluding the other.

@kovidgoyal
Copy link
Owner

Yeah is see the artifact, but that looks like a font bug as far as I can tell, however, I will take a look at it in detail when I have some time.

@kgizdov
Copy link
Author

kgizdov commented Jun 30, 2021

Just to provide some test data - went and tested the same font with same ligatures in Sublime Text 4 and there it works as expected with all of the above examples. No occlusion and, thus, no truncation or space at the end. So possibly something weird happening. Hopefully, this helps with debugging. Cheers.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jul 1, 2021 via email

@kgizdov
Copy link
Author

kgizdov commented Jul 5, 2021

I mentioned above that I'm running 0.21.2, which is the latest release. I could try to run the baster branch if you think it's solved there.

@kovidgoyal
Copy link
Owner

OK, I tried it on my Arch system and I cannot reproduce. The # ligature takes up all the cells available, with no blanks at the end, tested with:

kitty --config NONE -o 'font_family Cascadia Code' python -c "print(('#' * 60), end=']'); input()"

I do see the rendering artifacts at the end of it, but that is a font bug as I thought. The infinite ligature is actually longer than the string of individual characters, as a result the last glyph is being moved back to fit causing the rendering artifact. This commit prevents that from happening, with it, I now get a truncated last character instead, as expected: e07ba2c

You wont see this bug in other programs because kitty always renders within cell boundaries, other programs are happy to overflow.

@kgizdov
Copy link
Author

kgizdov commented Jul 5, 2021

I see. Thanks for having a look at it. I will report upstream to Cascadia Code.

@kgizdov
Copy link
Author

kgizdov commented Jul 5, 2021

apparently, something is not quite as it should be - microsoft/cascadia-code#527

@aaronbell
Copy link

For convenience, here is the comment that I left on this bug:

The fun thing with these infinite ligatures is that in the font's construction , the final # of ### is the same glyph as the final # in

Screen Shot 2021-07-05 at 5 50 40 AM

On the font side, here's the substitution for, say #####:
# - numbersign_start.seq
# - numbersign_middle.seq
# - numbersign_middle.seq
# - numbersign_middle.seq
# - numbersign_end.seq

And as the string gets longer, the number of # - numbersign_middle.seq only gets longer. There's no other difference.

If ### renders fine and a really long string of #################### does not, then the bug is on kitty's end. In both situations the ligated form has the same advanced width as the constituent #.

@kgizdov
Copy link
Author

kgizdov commented Jul 5, 2021

Went and did some experiments to possibly help with issue identification. Firstly, if I test with a character that has not ligatures, I get this:
cascadia-code-zeros
Then I went and tested Cascadia Code, Cascadia Code Mono and Cascadia Code PL. The triple compare is this:
cascadia-code-tripple-compare
For some reason font size is not consistent, even though kitty is running with no config - so maybe the font does have different size between variants (@aaronbell )
Then I noticed that when I run print(('=' * 3), end=']'), I get a weird artefact:
cascadia-code-equals3
This should instead be this
threeequals
but when there is a ] behind it turns into a different ligature as above. Weird. It also only happens for ===] and not for any other number of =.
It is also interesting to see what happens when the font overflows the line:
cascadia-code-overflow
seems like the issue is reset and and extra # character is consistently being drawn at the same place atop an already existing one. This starts to happen at around '#' * 90.
At this point, I thought that this should not be isolated to #, but to possibly every ligature, but since # has vertical features it won't be apparent on the horizontal ones. And I was correct.
This ------------------- is just a line. However, it does produce and extra space at the end and the length of the space is proportional to the number of -. Now let's put a vertical feature in there, like |, this has an interesting and possibly revealing effect:
-|
Screenshot from 2021-07-05 18-04-21
-|]
Screenshot from 2021-07-05 18-03-38
118 * '-' + '|' has this at the end:
Screenshot from 2021-07-05 18-07-25
at 119 * '-' the vertical | cannot be seen anymore. If we add ], the ligature is no longer working:
Screenshot from 2021-07-05 18-08-41
Finally, I manually marked the bounds of each cell
cascadia-code-artefact-test1-lines
I think there are two explanations for this and possibly two separate issues as well. The wrong ligature is selected at the end of a string when ] is present, which breaks the sequence and has effects along a few characters behind. Also, there is some evidence that some ligature sequence (middle or end) is ever so slightly longer/shorter, causing clipping to occur at the end of these strings.

I'd really appreciate if we could have a productive discussion on this and get to the bottom of this. I don't mind investigating further and/or committing dev time if pointed in the correct direction. Thanks.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jul 5, 2021 via email

@aaronbell
Copy link

Thanks for the analysis @kgizdov. The various versions of Cascadia Code are identical. The only difference between Code and Mono is that Mono is not using the ligature for infinite ### marks.

@kovidgoyal There are two "advanced widths". Internal to the font, each of the number sign alternate forms are exactly 1200 units, identical to Mono. Here's a screen from FontGoggles, which is built on top of Harfbuzz where you can see the 1200 units mentioned on the left:
Screen Shot 2021-07-05 at 12 57 31 PM

The other "advanced width" that you mention in your comment is likely the 'full width' of the glyph that includes any horizontal overlaps. The infinite ligature numbersigns have a slight overlap (aka, negative sideabearings) to ensure that they connect smoothly with the preceding and proceeding glyphs as pixel rounding can cause slight gaps between glyphs. I suspect that the Harfbuzz metric that you're looking at is including that overlap in its determination:

Screen Shot 2021-07-05 at 1 00 56 PM

This sort of overlapping is not uncommon in the world of fonts (Fira Code does the same for these sorts of glyphs), and it is also quite common in fonts for scripts that feature connecting elements. I would fully expect that text in scripts like Arabic, or Devanagari would demonstrate a similar issue. As I haven't seen this sort of problem in other applications that rely on Harfbuzz (such as FontGoggles), I have to conclude that kitty is perhaps using the wrong harfbuzz function to calculate glyph widths, leading to this misalignment.

@aaronbell
Copy link

BTW @kgizdov, a couple of notes about questions you raised:

===] This was an interesting situation where the ligature for equal_equal activated on the first =, but the substitution that was supposed to happen on the second = did not occur due to it matching an ignore command in the OT feature. I've re-arranged the code so that this will not happen in the future. Thanks for finding this!

-|] Here the |] ligature takes precedence over the infinite arrow-| form. Perhaps unexpectedly, but that's the reason why it jumps from a merged -| form to a split -|] form.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jul 6, 2021 via email

@kovidgoyal
Copy link
Owner

Although, thinking about it, I think I can workaround this issue, basically have to change the glyph placement code when drawing ligatures to respect negative LSB which it currently doesnt as for single cell glyphs negative LSB would leak outside the cell.

@aaronbell
Copy link

That’s a good idea. Though I’d suggest doing it with all glyphs rather than just ligatures. It would help with Arabic (which the next version of Cascadia includes) and box drawing characters (which also feature the overlap).

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jul 6, 2021 via email

@kovidgoyal
Copy link
Owner

I was wrong, the negative LSB is already handled in ligatures https://github.com/kovidgoyal/kitty/blob/master/kitty/freetype.c#L577 there bitmap_left is the LSB which is -1 for the ligature glyphs, however LSB does not affect advance, only glyph relative placement, so the original issue remains and I dont see a good way to fix it. As far as I can see this needs to be fixed in the font, using an overlap is going to cause overlarge advances, which means long strings of these characters occupy a different width than the individual glyphs would, making the font not really monospace anymore.

@aaronbell
Copy link

Unfortunately I have no plans to change the way the font works because doing so will cause rendering regressions in every other coding environment. It seems that the approach kitty uses for rendering glyphs (at least as far as I understand it) is incompatible with this feature of Cascadia Code.

@kgizdov I’m sorry that we weren’t able to resolve this for you. I’d suggest using Cascadia Mono, but then you’d lose all coding ligatures.

@kgizdov
Copy link
Author

kgizdov commented Jul 6, 2021

@kovidgoyal I'm confused by this line. Why should kitty be ignoring width correction for long ligatures? Because of char-by-char width estimation or?

Also, I think I found a similar issue with another font - Iosevka.
When the ligature for => kicks in, everything is fine:
iosevka_arrow
But when it gets longer:
iosevka_long_arrow
it overflows as discussed with Cascadia Code. However, if it was purely the same issue with overflowing, making it even longer will have wider side effects. Except it doesn't:
iosevka_very_long_arrow
I also think in the case of Iosevka the overhang is not there, since there are tiny gaps visible between each character. Can't confirm, because can't get FontGoggles to compile for me yet.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Jul 6, 2021 via email

@kgizdov
Copy link
Author

kgizdov commented Jul 6, 2021

Alright, I will try master and see how it goes.

@kgizdov
Copy link
Author

kgizdov commented Aug 16, 2021

I just upgraded to kitty 0.23.0 on Arch Linux, and I am pleased to say I no longer experience any of the above issues. Everything works as expected. :)

@kovidgoyal
Copy link
Owner

Yes, I did the simple expedient of rounding the advances, which takes care of this for most fonts. See 55319cd

@rixx
Copy link

rixx commented Nov 27, 2021

I'm running kitty 0.23.1 on Arch, and I'm running into a similar issue with the above mentioned Iosevka font with = ligatures. Here's how different amounts of equal signs look in a row:
2021-11-27T09:47+01:00

I'm not sure if this is a font bug or a Kitty bug.

@kovidgoyal
Copy link
Owner

you need to install the version of iosevka with glyph names embedded.

@vdrandom
Copy link

vdrandom commented Feb 18, 2023

Seems like kitty version 0.27.1 on MacOS is still affected:
Screenshot 2023-02-18 at 13 43 51

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants