toptensoftware / RichTextKit

Rich text rendering for SkiaSharp
Other
367 stars 73 forks source link

TextBlock Exception #43

Open emveepee opened 2 years ago

emveepee commented 2 years ago

I am using a netcore application NextPVR that uses RichTextKit and on an RPi I see this error on '\n' breaks in a TextBlock Is anyone aware of how to deal with this? Replacing '\n' with 0x20 and there is no issue.

2022-01-03 17:50:02.245 [ERROR][71] System.InvalidOperationException: Exception in BuildFontRuns() with original length of 77 now 77, style run count 1, font run count 0, direction overrides: False
 ---> System.ArgumentNullException: Value cannot be null. (Parameter 'asset')
   at Topten.RichTextKit.TextShaper.GetHarfBuzzBlob(SKStreamAsset asset)
   at Topten.RichTextKit.TextShaper..ctor(SKTypeface typeface)
   at Topten.RichTextKit.TextShaper.ForTypeface(SKTypeface typeface)
   at Topten.RichTextKit.TextShaper.Shape(ResultBufferSet bufferSet, Slice`1 codePoints, IStyle style, TextDirection direction, Int32 clusterAdjustment, SKTypeface asFallbackFor, TextAlignment textAlignment)
   at Topten.RichTextKit.TextBlock.CreateFontRun(StyleRun styleRun, Slice`1 codePoints, TextDirection direction, IStyle style, SKTypeface typeface, SKTypeface asFallbackFor)
   at Topten.RichTextKit.TextBlock.FlushUnshapedRuns()
   at Topten.RichTextKit.TextBlock.AddFontRun(StyleRun styleRun, Int32 start, Int32 length, TextDirection direction, IStyle style, SKTypeface typeface, SKTypeface asFallbackFor)
   at Topten.RichTextKit.TextBlock.AddDirectionalRun(StyleRun styleRun, Int32 start, Int32 length, TextDirection direction, IStyle style)
   at Topten.RichTextKit.TextBlock.BuildFontRuns()
   --- End of inner exception stack trace ---
   at Topten.RichTextKit.TextBlock.BuildFontRuns()
toptensoftware commented 2 years ago

Thanks for reporting this. Usually \n works fine with TextBlock and in fact the test Sandbox program in the RichTextKit repository uses \n's for line breaks. So I'm wondering:

Any chance you can try to reproduce the issue using the same text and font on Windows (use the Sandbox program if you like) and see if the cause of this can be narrowed down?

Happy to help get this resolved, but don't have a setup to test this on RPi right now.

emveepee commented 2 years ago

I'm not the developer of the NextPVR app so I don't have the source. However, if it was the simply text or HarfBuff then I suspect that replacing \n with space would still crash. I have even tested removing all text and it still crashes on \n which leads me to a TextBlock issue. Unfortunately I cannot figure out how to compile RichTextKit from the sln to add some debug code, missing files and /unsafe errors even when all the projects are marked to allow unsafe get in the way.

It does run in intel Linux and macOS, and even in Docker on the RPi, so if it RPi related I suspect the unsafe blocks might be where to look if it is an endianness issue. I don't understand the code to link those areas to \n

toptensoftware commented 2 years ago

OK... this is going to be difficult to resolve. Without being able to debug or reproduce it not sure what I can do. If I remember correctly the carriage returns should be processed and stripped out before calling harfbuzz anyway so really no idea what's going on here.

I guess I could dig out my rpi and try to setup a dev environment, but bit busy right now.

emveepee commented 2 years ago

You don't need to do any dev on the RPi4 since the netcore dll can be created on Windows. I will see what I can do but it would help I could build from the sln file. Do you know if others have tried to build it?

emveepee commented 2 years ago

I solved my compile issue by manually adding <AllowUnsafeBlocks>true</AllowUnsafeBlocks> to the main project https://github.com/toptensoftware/RichTextKit/blob/master/Topten.RichTextKit/Topten.RichTextKit.csproj#L7 That might help others if you included that.

I trouble getting TestBench going it was stuck on instantiating UnicodeClasses I had to ensure that all the trie resources where marked as Embedded Resource

emveepee commented 2 years ago

Good news I can run TestBench on the RPi, one small change is you use TestData\\ and Linux needs / so you can make the test multi platform using slashes if you'd like. Is there are simple way for me to test the \n issue in these tests?

Grapheme Cluster Tests
----------------------

Passed 602 of 602 tests
Time in algorithm: 00:00:00.0014165
Memory in algorithm: 0
GC Collection Counts:
  - generation #0: 0
  - generation #1: 0
emveepee commented 2 years ago

I have determined the cause of the crash. The calling app as not setting a FontFamily on the text with the \n. RichTextKit logic selected a font Liberation Mono, a mono spaced font which results in the crash. As a test I hard-coded the normal font family called and distributed by the client and it worked. As well, specifying another font installed on this RPi, Liberation Sans was successful. Shouldn't RichTextKit be able to try multiple installed font families if none are specified?

toptensoftware commented 2 years ago

Thanks for the info... this sounds like a fault of the app - there should always be a font family specified for all text. Perhaps I should update it to throw an exception if there isn't one.

Regarding selecting fonts it works like this:

  1. The user specifies a font family
  2. RichTextKit will call either the default, or user installed FontMapper to map that font family to an SKFont.
  3. SkiaSharp's font fallback mechanism is used to replace fonts where the supplied font doesn't contain the required characters.

The default font mapper currently only matches font families to SKFont's with the same family name, returning null if none is found. This could be updated, or the client can provide a custom font mapper to do smarter things if required.

emveepee commented 2 years ago

The app has changed so yes it works now specifying a font that is distributed with the app. It would have worked if the "" detection if the right font Liberation Sans had been selected rather then the crash on the first one.

Do you want to look at a PR to see the changes I needed to build from the sln file?