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

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

Closed
nil4 opened this issue Mar 12, 2024 · 17 comments · Fixed by #16120
Closed

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

nil4 opened this issue Mar 12, 2024 · 17 comments · Fixed by #16120

Comments

@nil4
Copy link
Contributor

nil4 commented Mar 12, 2024

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

@SourMesen
Copy link
Contributor

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

@rabbitism
Copy link
Contributor

received similar report in irihitech/Semi.Avalonia#347

@zdpcdt
Copy link

zdpcdt commented May 18, 2024

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.

20240518-165642.383-0.mp4

@Gillibald
Copy link
Contributor

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

@baaron4
Copy link

baaron4 commented Jun 7, 2024

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.

@danipen
Copy link

danipen commented Jun 7, 2024

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.

@jpgarza93
Copy link

I am experiencing the same

@danipen
Copy link

danipen commented Jun 10, 2024

Probably fixed by #15971. Can someone check?

@mgkcorty
Copy link

Probably fixed by #15971. Can someone check?

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

@Gillibald
Copy link
Contributor

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

@SourMesen
Copy link
Contributor

SourMesen commented Jun 22, 2024

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

@Gillibald
Copy link
Contributor

Okay thanks

@Gillibald
Copy link
Contributor

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

@mgkcorty
Copy link

@Gillibald Please, check text from my PR #15971
It's reproduces issue on current master.

@Gillibald
Copy link
Contributor

It reproduces via some unit test in all Avalonia versions

@Sophisticated-IS
Copy link

Sophisticated-IS commented Jun 25, 2024

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.

font.Shape(buffer, GetFeatures(options));

For Example: ff (CodePoint=102) converts to single different glyph with CodePoint=299
Before Call
image
After
image

Mb Related issue: harfbuzz/harfbuzz#3000 (comment)
Video: you can see that caret moves backward/forward like "f*" is a single ligature

AvaloniaUI.ff.Glyph.Bug.mp4

@Gillibald
Copy link
Contributor

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

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

Successfully merging a pull request may close this issue.