Avalonia
Avalonia copied to clipboard
Typing `fl` or `fi` in a textbox crashes with Avalonia 11.0.10 on MacOS
Describe the bug
Apps built with Avalonia 11.0.10 and 11.1.0-beta1 packages crash repeatably on MacOS 14.4, when typing fi
or fl
in a text box.
To Reproduce
git clone https://github.com/Actipro/Avalonia-Controls.git
cd Avalonia-Controls/Samples/SampleBrowser/SampleBrowser.Desktop
dotnet run
Select Themes from the drop-down at the top-left corner, then type fl
or fi
in a textbox (e.g. E-mail Address below):
The text input is processed and shown as expected. Close the sample browser app.
Edit the file Avalonia-Controls/Samples/SampleBrowser/References/Avalonia.References.props
and update:
<PropertyGroup>
- <AvaloniaVersion>11.0.7</AvaloniaVersion>
+ <AvaloniaVersion>11.0.10</AvaloniaVersion>
</PropertyGroup>
Run the application again, this time using Avalonia 11.0.10 packages:
cd Avalonia-Controls/Samples/SampleBrowser/SampleBrowser.Desktop
dotnet run
Type fl
or fi
in one of the text boxes. The application crashes, printing this stack trace:
% dotnet run
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
at Avalonia.Media.GlyphRun.CreateGlyphRunMetrics()
at Avalonia.Media.GlyphRun.get_BaselineOrigin()
at Avalonia.Media.GlyphRun.CreateGlyphRunImpl()
at Avalonia.Media.TextFormatting.TextLineImpl.CreateLineMetrics()
at Avalonia.Media.TextFormatting.TextLineImpl.FinalizeLine()
at Avalonia.Media.TextFormatting.TextFormatterImpl.FormatLine(ITextSource textSource, Int32 firstTextSourceIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak)
at Avalonia.Media.TextFormatting.TextLayout.CreateTextLines()
at Avalonia.Media.TextFormatting.TextLayout..ctor(String text, Typeface typeface, Double fontSize, IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping, TextTrimming textTrimming, TextDecorationCollection textDecorations, FlowDirection flowDirection, Double maxWidth, Double maxHeight, Double lineHeight, Double letterSpacing, Int32 maxLines, IReadOnlyList`1 textStyleOverrides)
at Avalonia.Controls.Presenters.TextPresenter.CreateTextLayoutInternal(Size constraint, String text, Typeface typeface, IReadOnlyList`1 textStyleOverrides)
at Avalonia.Controls.Presenters.TextPresenter.CreateTextLayout()
at Avalonia.Controls.Presenters.TextPresenter.get_TextLayout()
at Avalonia.Controls.Presenters.TextPresenter.MoveCaretToTextPosition(Int32 textPosition, Boolean trailingEdge)
at Avalonia.Controls.TextBox.OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
at Avalonia.Animation.Animatable.OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
at Avalonia.PropertyStore.EffectiveValue`1.SetAndRaiseCore(ValueStore owner, StyledProperty`1 property, T value, BindingPriority priority, Boolean isOverriddenCurrentValue, Boolean isCoercedDefaultValue)
at Avalonia.PropertyStore.EffectiveValue`1.SetCurrentValueAndRaise(ValueStore owner, StyledProperty`1 property, T value)
at Avalonia.PropertyStore.ValueStore.SetCurrentValue[T](StyledProperty`1 property, T value)
at Avalonia.AvaloniaObject.SetCurrentValue[T](StyledProperty`1 property, T value)
at Avalonia.Controls.TextBox.OnCaretIndexChanged(AvaloniaPropertyChangedEventArgs e)
at Avalonia.Controls.TextBox.OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
at Avalonia.Animation.Animatable.OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
at Avalonia.PropertyStore.EffectiveValue`1.SetAndRaiseCore(ValueStore owner, StyledProperty`1 property, T value, BindingPriority priority, Boolean isOverriddenCurrentValue, Boolean isCoercedDefaultValue)
at Avalonia.PropertyStore.EffectiveValue`1.SetCurrentValueAndRaise(ValueStore owner, StyledProperty`1 property, T value)
at Avalonia.PropertyStore.ValueStore.SetCurrentValue[T](StyledProperty`1 property, T value)
at Avalonia.AvaloniaObject.SetCurrentValue[T](StyledProperty`1 property, T value)
at Avalonia.Controls.TextBox.HandleTextInput(String input)
at Avalonia.Controls.TextBox.OnTextInput(TextInputEventArgs e)
at Avalonia.Input.InputElement.<>c.<.cctor>b__32_4(InputElement x, TextInputEventArgs e)
at Avalonia.Reactive.LightweightObservableBase`1.PublishNext(T value)
at Avalonia.Interactivity.EventRoute.RaiseEventImpl(RoutedEventArgs e)
at Avalonia.Interactivity.Interactive.RaiseEvent(RoutedEventArgs e)
at Avalonia.Input.KeyboardDevice.ProcessRawEvent(RawInputEventArgs e)
at Avalonia.Controls.TopLevel.HandleInput(RawInputEventArgs e)
at Avalonia.Native.WindowBaseImpl.RawTextInputEvent(UInt64 timeStamp, String text)
at Avalonia.Native.WindowBaseImpl.WindowBaseEvents.Avalonia.Native.Interop.IAvnWindowBaseEvents.RawTextInputEvent(UInt64 timeStamp, String text)
at Avalonia.Native.Interop.Impl.__MicroComIAvnWindowBaseEventsVTable.RawTextInputEvent(Void* this, UInt64 timeStamp, Byte* text)
--- End of stack trace from previous location ---
at Avalonia.Native.DispatcherImpl.RunLoop(CancellationToken token)
at Avalonia.Threading.DispatcherFrame.Run(IControlledDispatcherImpl impl)
at Avalonia.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at Avalonia.Threading.Dispatcher.MainLoop(CancellationToken cancellationToken)
at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args)
at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(AppBuilder builder, String[] args, Action`1 lifetimeBuilder)
at ActiproSoftware.SampleBrowser.Program.Main(String[] args) in ~/dev/Avalonia-Controls/Samples/SampleBrowser/SampleBrowser.Desktop/Program.cs:line 22
The same crash also reproduces with Avalonia 11.1.0-beta1
packages.
Reverting <AvaloniaVersion>
to 11.0.7, or 11.0.9, no longer reproes -- this appears to be a regression in 11.0.10 and later versions.
Expected behavior
Entering text that may result in ligatures being displayed (e.g. fi
or fl
) does not crash on MacOS.
Avalonia version
11.0.10, 11.1.0-beta1
OS
macOS
Additional context
No response
The same crash also happens on Linux, just typing fl
into any textbox will cause it. Doesn't crash on Windows, though.
received similar report in https://github.com/irihitech/Semi.Avalonia/issues/347
I have tested the issue and found that inputting ff
, fi
, fl
, ffi
, or ffl
does not cause an error. However, I observed that when deleting these characters, they are deleted together as a single unit.
I suspect this behavior is related to font ligatures. When I changed the font to Courier, the characters are deleted individually as expected.
https://github.com/AvaloniaUI/Avalonia/assets/54255897/61d28fb3-292d-4fc0-9881-66a979718c99
Currently we handle hit testing via glyph clusters. So a delete action always removes the whole ligature.
Can confirm on Avalonia 11.0.10 that typing fi in any TextBox causes a similar crash. Not seeing that deleting behavior however. So fixing the font to Courier stopped the crashing and did not cause that deleting behavior.
We're experiencing the same crash typing =>
at the beginning of a line. For sure it's affected by ligatures since that char combination is causing a ligature.
We're using Inter font.
I am experiencing the same
Probably fixed by https://github.com/AvaloniaUI/Avalonia/pull/15971. Can someone check?
Probably fixed by #15971. Can someone check?
Checked. Fixed inside #15971, but PR not merged.
Can someone provide a sample that runs as is and reproduces the issue? I can't reproduce any crash
@Gillibald I've managed to reproduce it on Windows with the Calibri font on Avalonia 11.0.11. It only seems to crash if I set the SelectionForegroundBrush property?
I have no code in the project except a window defined like this - hopefully this lets you reproduce the crash on your end, too.
<Window
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Test.Views.MainWindow"
>
<TextBox
FontFamily="Calibri"
SelectionForegroundBrush="White"
/>
</Window>
Typing fl
in the textbox crashes:
System.IndexOutOfRangeException
HResult=0x80131508
Message=Index was outside the bounds of the array.
Source=Avalonia.Base
StackTrace:
at Avalonia.Utilities.ArraySlice`1.get_Item(Int32 index) in Avalonia.Utilities\ArraySlice.cs:line 28
at Avalonia.Media.TextFormatting.ShapedBuffer.get_Item(Int32 index) in Avalonia.Media.TextFormatting\ShapedBuffer.cs:line 33
at Avalonia.Media.GlyphRun.CreateGlyphRunMetrics() in Avalonia.Media\GlyphRun.cs:line 466
at Avalonia.Media.GlyphRun.get_Metrics() in Avalonia.Media\GlyphRun.cs:line 56
at Avalonia.Media.GlyphRun.get_BaselineOrigin() in Avalonia.Media\GlyphRun.cs:line 68
at Avalonia.Media.GlyphRun.CreateGlyphRunImpl() in Avalonia.Media\GlyphRun.cs:line 614
at Avalonia.Media.GlyphRun.get_PlatformImpl()
at Avalonia.Media.GlyphRun.get_InkBounds()
at Avalonia.Media.TextFormatting.TextLineImpl.CreateLineMetrics() in Avalonia.Media.TextFormatting\TextLineImpl.cs:line 886
at Avalonia.Media.TextFormatting.TextLineImpl.FinalizeLine() in Avalonia.Media.TextFormatting\TextLineImpl.cs:line 783
at Avalonia.Media.TextFormatting.TextFormatterImpl.FormatLine(ITextSource textSource, Int32 firstTextSourceIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak) in Avalonia.Media.TextFormatting\TextFormatterImpl.cs:line 86
at Avalonia.Media.TextFormatting.TextLayout.CreateTextLines() in Avalonia.Media.TextFormatting\TextLayout.cs:line 302
at Avalonia.Media.TextFormatting.TextLayout..ctor(String text, Typeface typeface, Double fontSize, IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping, TextTrimming textTrimming, TextDecorationCollection textDecorations, FlowDirection flowDirection, Double maxWidth, Double maxHeight, Double lineHeight, Double letterSpacing, Int32 maxLines, IReadOnlyList`1 textStyleOverrides) in Avalonia.Media.TextFormatting\TextLayout.cs:line 76
at Avalonia.Controls.Presenters.TextPresenter.CreateTextLayoutInternal(Size constraint, String text, Typeface typeface, IReadOnlyList`1 textStyleOverrides)
at Avalonia.Controls.Presenters.TextPresenter.CreateTextLayout()
at Avalonia.Controls.Presenters.TextPresenter.get_TextLayout()
at Avalonia.Controls.Presenters.TextPresenter.MoveCaretToTextPosition(Int32 textPosition, Boolean trailingEdge)
at Avalonia.Controls.TextBox.OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
[...]
Okay thanks
The provided sample reproduces the issue but only with Avalonia 11.0.X and not with the current master
@Gillibald Please, check text from my PR https://github.com/AvaloniaUI/Avalonia/pull/15971 It's reproduces issue on current master.
It reproduces via some unit test in all Avalonia versions
On Windows I get the same bug. I tested on release/11.1.0-beta2 and there is no crush anymore but strange behavior with deleting "f*" as one ligature stays there. After some research I found that bug is likely in HarfBuzzSharp because it interpreters "fi" "fl" "fk" "ff" "fk" "fj" "fh" with FontFamily="Calibri" as one ligature.
In the code below buffer after calling "font.Shape" fills only with one glyph info. https://github.com/AvaloniaUI/Avalonia/blob/133d7ca7484e2a4e74bb70ae05b9d9c9660f98fa/src/Skia/Avalonia.Skia/TextShaperImpl.cs#L44
For Example: ff (CodePoint=102) converts to single different glyph with CodePoint=299
Before Call
After
Mb Related issue: https://github.com/harfbuzz/harfbuzz/issues/3000#issuecomment-855161327 Video: you can see that caret moves backward/forward like "f*" is a single ligature
https://github.com/AvaloniaUI/Avalonia/assets/57034720/8e694b6e-2879-488c-8eef-80a60af64185
We are deleting grapheme clusters instead of codepoints when we handle backspace