Avalonia icon indicating copy to clipboard operation
Avalonia copied to clipboard

Typing `fl` or `fi` in a textbox crashes with Avalonia 11.0.10 on MacOS

Open nil4 opened this issue 11 months ago • 4 comments

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):

image

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

nil4 avatar Mar 12 '24 20:03 nil4

The same crash also happens on Linux, just typing fl into any textbox will cause it. Doesn't crash on Windows, though.

SourMesen avatar May 11 '24 05:05 SourMesen

received similar report in https://github.com/irihitech/Semi.Avalonia/issues/347

rabbitism avatar May 11 '24 05:05 rabbitism

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

zdpcdt avatar May 18 '24 09:05 zdpcdt

Currently we handle hit testing via glyph clusters. So a delete action always removes the whole ligature.

Gillibald avatar May 18 '24 18:05 Gillibald

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.

baaron4 avatar Jun 07 '24 14:06 baaron4

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.

danipen avatar Jun 07 '24 16:06 danipen

I am experiencing the same

jpgarza93 avatar Jun 07 '24 22:06 jpgarza93

Probably fixed by https://github.com/AvaloniaUI/Avalonia/pull/15971. Can someone check?

danipen avatar Jun 10 '24 13:06 danipen

Probably fixed by #15971. Can someone check?

Checked. Fixed inside #15971, but PR not merged.

mgkcorty avatar Jun 14 '24 06:06 mgkcorty

Can someone provide a sample that runs as is and reproduces the issue? I can't reproduce any crash

Gillibald avatar Jun 22 '24 12:06 Gillibald

@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)
[...]

SourMesen avatar Jun 22 '24 13:06 SourMesen

Okay thanks

Gillibald avatar Jun 22 '24 13:06 Gillibald

The provided sample reproduces the issue but only with Avalonia 11.0.X and not with the current master

Gillibald avatar Jun 25 '24 11:06 Gillibald

@Gillibald Please, check text from my PR https://github.com/AvaloniaUI/Avalonia/pull/15971 It's reproduces issue on current master.

mgkcorty avatar Jun 25 '24 11:06 mgkcorty

It reproduces via some unit test in all Avalonia versions

Gillibald avatar Jun 25 '24 12:06 Gillibald

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 image After image

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

Sophisticated-IS avatar Jun 25 '24 15:06 Sophisticated-IS

We are deleting grapheme clusters instead of codepoints when we handle backspace

Gillibald avatar Jun 26 '24 06:06 Gillibald