Giter Site home page Giter Site logo

richtextkit's People

Contributors

charlenni avatar clancey avatar dbriard avatar dependabot[bot] avatar gresolio avatar handerss-spotfire avatar mattleibow avatar parentelement avatar sady4850 avatar topperdel avatar toptensoftware avatar vercidium avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

richtextkit's Issues

MessuredHeight hangs on Linux

Hi.
I stumbled upon the RichTextKit and it seems pretty awesome.

I made a small program in net 6.0, which works perfectly on my Windows computer, but when I try to run the program on my Raspberry pi, it hangs for some reason..

I did some debugging and found that it hangs when it tries to calculate the size.
The code is pretty simple, and my guess is that it has something to do with the fallback fonts, but.. Help :)

Just to make sure SkiaSharp was working, I tried to use the DrawText first - And that works like a charm..

Any idea what I'm missing / doing wrong??

var rs = new RichString
  {
    MaxWidth = 800,
    MaxHeight = 480,
  }
  .FontSize(20)
  .BackgroundColor(SKColors.White)
  .LineHeight(1.1f)
  .Alignment(TextAlignment.Center)
  .Add("Test");

using (SKCanvas bitmapCanvas = new SKCanvas(this.image))
{
  Console.WriteLine("WriteRichStringCentered - 1");
  using (var textPaint = new SKPaint { Color = SKColors.White, TextSize = 60, IsAntialias = true })
  {
    bitmapCanvas.DrawRect(0, 100, this.image.Width, 30, textPaint);
    textPaint.Color = SKColors.Coral;
    bitmapCanvas.DrawText("TestTestTestTestTestTestTestTest", new SKPoint(10, 100), textPaint);
    var top = (this.image.Height - richString.MeasuredHeight) / 2; <---- Code hangs here
    Console.WriteLine("WriteRichStringCentered - 2");
  }
}

Regards,
Martin

Exception: Attempting to JIT

I get, while using RichTextKit, the following error when calling Layout with FontFamily=null (default for new Font() ) or FontFamily="".

Error Message:
"Attempting to JIT compile method 'bool Topten.RichTextKit.FontFallback/d__2:MoveNext ()' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.\n"

StackTrace:
at Topten.RichTextKit.TextBlock.AddDirectionalRun (Topten.RichTextKit.StyleRun styleRun, System.Int32 start, System.Int32 length, Topten.RichTextKit.TextDirection direction, Topten.RichTextKit.IStyle style) [0x00057] in <19e5ac464bd84f9c875ac01889614bdc>:0

What did I wrong?

Different line break behavior based on how texts are added

I have the bolded text aaaaaaaaaa in a text block with MaxWidth = 36 and MaxHeight = 30.

If I add the letters as two separate texts:

textBlock.AddText("aaaaa", styleBold);
textBlock.AddText("aaaaa", styleBold);

it results in:
image

On the other hand, if I add the entire text in one AddText:

textBlock.AddText("aaaaaaaaaa", styleBold);

it results in:
image

I expect that I see the second image in both scenarios.

Does the drawing string support Hindi language?

Hi:

My method of using TextBlock Paint does not support Hindi problems. Is there a good solution?

Now it is drawn by the box instead of the box, and the correct one is not drawn

environment : linux environment
version : .net 5

RichString.Paint is not rendering ClearType when IsAntialias and LcsRenderText are both set to true

Test code:

RichString richString = new RichString
{
    MaxWidth = ScreenClientBounds.Width,
    MaxHeight = ScreenClientBounds.Height
};

richString.FontFamily(Font.FontFamily.Name);
richString.FontSize(Font.Size * 1.25f * ZoomFactor);
richString.FontItalic(Font.Italic);
richString.Bold(Font.Bold);
richString.Add(text);

richString.Paint(canvas, clientRectangle.Location, new TextPaintOptions { IsAntialias = true, LcdRenderText = true });

text on new line

Hello,

I have been wondering if there is a way to add each sentece on new line with TextBlock, the text displayed in the picture should look as bellow.

DestinationAddress, 
DestinationType, 
OneTimeToken, 
Time, 
AuthenticationType

However the text displayed is broken, the text doesn't have to be the same every time. If I am not mistaken it all depends on the MaxWidth, but can I somehow involve this in any other way?

I get this usually

DestinationAddres
s
OneTimeToken,Tim
e...
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Text"
             xmlns:forms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="Text.MainPage">
    <StackLayout>
        <forms:SKCanvasView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
                            PaintSurface="OnPaintSurface"/>
    </StackLayout>
</ContentPage>
 private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
        {
            var canvas = e.Surface.Canvas;
            
            // Create the text block
            var tb = new TextBlock {MaxWidth = 600, Alignment = TextAlignment.Auto, MaxLines = 5};

           var list =  new List<string> {"DestinationAddress", "DestinationType", "OneTimeToken", "Time", "AuthenticationType"};
           var styleNormal = new Style()
           {
               FontFamily = "Arial",
               FontSize = 60
           };
           foreach (var field in list)
           {
               tb.AddText(field, styleNormal);
           }
           tb.Paint(canvas);
         
        }
    }

Hang in FontFallback.GetFontRuns with latest SkiaSharp

When using the latest SkiaSharp 2.80.0 and HarfBuzzSharp 2.6.1.5, the following code hangs:

var tb = new TextBlock ();
tb.AddText ("Hello", new Style ());
Console.WriteLine (tb.MeasuredHeight);

This is caused by getting stuck in an infinite loop in FontFallback.GetFontRuns () due to both of these lines returning 0:

count = typeface.GetGlyphs((IntPtr)(pch + pos), length - pos, SKEncoding.Utf32, out var glyphs);
count = RunFace.GetGlyphs((IntPtr)(pch + pos), length - pos, SKEncoding.Utf32, out var glyphs);

^^ This overload of GetGlyphs is marked as [Obsolete].

It seems like something in SkiaSharp has changed and this code no longer works as expected. I do not know enough about what is going on to determine if the RichTextKit code needs to be updated or if this is a bug in SkiaSharp.

Windows 10 - VS 2019 - netcoreapp3.1

Font fallback from private font files

Thank you for making this great library.

I am making a server application and need to be able to render multi-language text. Use multiple font files in different languages.

Since font files are not installed on the server, all font files must be included in the application.

Current font fallback is based on system fonts. Is there a way to set fallback from multiple private font files?

StrikeThrough and Underline not visible for certain fonts and small sizes

Certain fonts, especially when font size is small, result in strikethrough and underline not being rendered. I'm guessing this is because the thickness is determined by the font metric's StrikeoutThickness and UnderlineThickness properties, which may be smaller than 1. For some fonts this is worse than others. Whether the line is rendered also depends on the subpixel position of the line (and I guess what anti aliasing is used).

Should be reproducible with Arial font with a pixel font size smaller than 10 for example.

I can submit a PR to fix this. My two suggestions for the fix would be:

  1. Add UnderlineThickness and StrikeThroughThickness as properties to IStyle to let the user override. I guess this would lead to strikethrough not being centered when thickness is increased, maybe that's fine since it is an override.
  2. Set the minimum size to 1. Maybe this leads to too thick lines for certain anti aliasing values? I notice that underline thickness is set to 1 if UnderlineThickness is null, and strikethrough thickness is set to 0 if StrikeoutThickness. Settings it to 1 makes sense to me, but I don't understand defaulting to 0 for strikethrough.

Any other suggestion I could also take a look at.

making this work with foreign languages

Hi,

I have a situation where I need to render Hebrew and I see if I just use the example code it's all blocks, is there a special font or setting I need to set to render the text correctly?

Thanks.

overdraw

is it normal for RTK to do partial drawing?

this is my shader

var sksl = SKRuntimeEffect.Create(
    "uniform shader input;\n" +
    "uniform shader inputAlpha;\n" +
    "uniform shader inputGradient;\n" +
    "\n" +
    "half4 main() {\n" +
    "    half4 color = sample(input);\n" +
    // return zero if alpha is zero
    "    if (color.a == 0) return vec4(0,0,0,0);\n" +
    "    int alpha = 255.0 * sample(inputAlpha).a;\n" +
    // return color if input alpha is 0, this means we only drawn this pixel once
    // Skia's overdraw canvas increases the alpha of a pixel each time it drawn touched
    // R G B A
    "    if (alpha == 0) {\n" +
    // apply greyscale to the overdraw canvas in order to isolate the overdraw colors
    "       return half4(vec3((color.r + color.g + color.b) / 3), 1);\n" +
    "    }\n" +
    "    return half4(1,0,0,1);\n" +
    //// gradient heatmap
    //"    return sample(inputGradient, float2(0, alpha));\n" +
    "}\n",
    out string err
);

and if use this

void drawText(SKCanvas canvas, int n, int x, int y)
{
    Topten.RichTextKit.TextBlock block = new();
    Topten.RichTextKit.Style style = new();
    style.TextColor = SKColors.Silver;
    style.FontSize = 20;
    var t = new Topten.RichTextKit.TextPaintOptions();
    t.Edging = SKFontEdging.SubpixelAntialias;
    for (int i = 0; i < n; i++)
    {
        string text = "drawn " + n + " time";
        if (i != 0) text += "s";
        block.Clear();
        block.AddText(text, style);
        block.Paint(canvas, new SKPoint(x, y));
        canvas.Flush();
    }
}

then i get this

image

and if i use this

void drawText(SKCanvas canvas, int n, int x, int y)
{
    using var paint = new SKPaint();
    paint.Color = SKColors.Silver;
    for (int i = 0; i < n; i++)
    {
        string text = "drawn " + n + " time";
        if (i != 0) text += "s";
        canvas.DrawText(text, x, y, paint);
    }
}

then i get this (expected result)

image

my full code is this

SKShader createAlphaGradientShader()
{
    return SKShader.CreateLinearGradient(
        // start
        new SKPoint(0, 0),
        // end
        new SKPoint(0, 255),
        // colors
        new SKColor[]
        {
        // light blue
        //new SKColorF(0, 0.5f, 0.75f).ToSKColor(),
        // blueish-whitish
        //new SKColorF(0.37f, 0.5f, 0.75f).ToSKColor(),
        // lighter orange
        new SKColor(249, 205, 172),
        // light orange
        //new SKColor(243, 158, 95),
        // orange-redish
        //new SKColorF(1f, 0.28f, 0).ToSKColor(),
        // red
        new SKColorF(1f, 0, 0).ToSKColor(),
        },
        // distribution (color pos from 0 to 1)
        new float[] { 0, 0.05f },
        SKShaderTileMode.Clamp
    );
}

using var gradient = createAlphaGradientShader();

// to improve overdraw quality we only apply overdraw to non transparent final output pixels
// this means we need to draw twice, once in full color, another in alpha
// if the full color pixel has an alpha of zero we discard the result
var sksl = SKRuntimeEffect.Create(
    "uniform shader input;\n" +
    "uniform shader inputAlpha;\n" +
    "uniform shader inputGradient;\n" +
    "\n" +
    "half4 main() {\n" +
    "    half4 color = sample(input);\n" +
    // return zero if alpha is zero
    "    if (color.a == 0) return vec4(0,0,0,0);\n" +
    "    int alpha = 255.0 * sample(inputAlpha).a;\n" +
    // return color if input alpha is 0, this means we only drawn this pixel once
    // Skia's overdraw canvas increases the alpha of a pixel each time it drawn touched
    // R G B A
    "    if (alpha == 0) {\n" +
    // apply greyscale to the overdraw canvas in order to isolate the overdraw colors
    "       return half4(vec3((color.r + color.g + color.b) / 3), 1);\n" +
    "    }\n" +
    "    return half4(1,0,0,1);\n" +
    //// gradient heatmap
    //"    return sample(inputGradient, float2(0, alpha));\n" +
    "}\n",
    out string err
);

if (err != null)
{
    Log.d("SHADER", "runtime effect compiled with errors: " + err);
    return;
}

int w = canvas.BaseLayerSize.Width;
int h = canvas.BaseLayerSize.Height;
SKImageInfo offscreenInfo = new(w, h);
SKImageInfo offscreenAlphaInfo = new(w, h, SKColorType.Alpha8);
using var offscreenSurface = SKSurface.Create(offscreenInfo);
using var offscreenAlphaSurface = SKSurface.Create(offscreenAlphaInfo);
using SKCanvas imageCanvas = offscreenSurface.Canvas;
using SKOverdrawCanvas overdrawCanvas = new(offscreenAlphaSurface.Canvas);
using SKNWayCanvas nWayCanvas = new(w, h);
nWayCanvas.AddCanvas(overdrawCanvas);
nWayCanvas.AddCanvas(imageCanvas);

using SKPaint colorPaint = new();

void drawText_(SKCanvas canvas, int n, int x, int y)
{
    Topten.RichTextKit.TextBlock block = new();
    Topten.RichTextKit.Style style = new();
    style.TextColor = SKColors.Silver;
    style.FontSize = 20;
    var t = new Topten.RichTextKit.TextPaintOptions();
    t.Edging = SKFontEdging.SubpixelAntialias;
    for (int i = 0; i < n; i++)
    {
        string text = "drawn " + n + " time";
        if (i != 0) text += "s";
        block.Clear();
        block.AddText(text, style);
        block.Paint(canvas, new SKPoint(x, y));
        canvas.Flush();
    }
}

void drawText(SKCanvas canvas, int n, int x, int y)
{
    using var paint = new SKPaint();
    paint.Color = SKColors.Silver;
    for (int i = 0; i < n; i++)
    {
        string text = "drawn " + n + " time";
        if (i != 0) text += "s";
        canvas.DrawText(text, x, y, paint);
    }
}

void drawMatrix(SKCanvas canvas, int count, int max_lines, int spacing)
{
    max_lines++;
    int n = 0;
    int column = 0;
    int line = 1;
    for (int i = 0; i < count; i++)
    {
        n = i + 1;
        if (line == max_lines)
        {
            line = 1;
            column += spacing;
        }
        //int s = canvas.Save();
        drawText(canvas, n, column, 20 * line);
        //canvas.RestoreToCount(s);
        line++;
    }
}

drawMatrix(nWayCanvas, 20, 20, 50);

nWayCanvas.Flush();

using var imageAlpha = offscreenAlphaSurface.Snapshot();
using var image = offscreenSurface.Snapshot();
var imageAlphaShader = imageAlpha.ToShader();
var imageShader = image.ToShader();

SKRuntimeEffectChildren children = new(sksl) {
    { "input", imageShader },
    { "inputAlpha", imageAlphaShader },
    { "inputGradient", gradient },
};

var ourShader = sksl.ToShader(false, new(sksl), children);

sksl.Dispose();
imageAlphaShader.Dispose();
imageShader.Dispose();

// we only want to write our paint shader's output pixel to the canvas
// this is the same as if the canvas was cleared before painting the shader
colorPaint.BlendMode = SKBlendMode.Src;
colorPaint.Shader = ourShader;
canvas.DrawPaint(colorPaint);
ourShader.Dispose();

MAUI: Exception in BuildFontRuns() System.TypeLoadException: Could not resolve type with token 01000034 from typeref

EDIT: duplicate (mostly exact same error) found dotnet/maui#5142

[DOTNET] ViewRootImpl: Caught exception while drawing view: System.InvalidOperationException: Exception in BuildFontRuns() with original length of 13 now 13, style run count 1, font run count 0, direction overrides: False
[DOTNET]  ---> System.TypeLoadException: Could not resolve type with token 01000034 from typeref (expected class 'System.ReadOnlySpan`1' in assembly 'mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e')
[DOTNET]    at Topten.RichTextKit.TextBlock.AddDirectionalRun(StyleRun styleRun, Int32 start, Int32 length, TextDirection direction, IStyle style) in C:\Users\Brad\Projects\RichTextKit\Topten.RichTextKit\TextBlock.cs:line 1182
[DOTNET]    at Topten.RichTextKit.TextBlock.BuildFontRuns() in C:\Users\Brad\Projects\RichTextKit\Topten.RichTextKit\TextBlock.cs:line 1115
[DOTNET]    --- End of inner exception stack trace ---
[DOTNET]    at Topten.RichTextKit.TextBlock.BuildFontRuns() in C:\Users\Brad\Projects\RichTextKit\Topten.RichTextKit\TextBlock.cs:line 1133
[DOTNET]    at Topten.RichTextKit.TextBlock.Layout() in C:\Users\Brad\Projects\RichTextKit\Topten.RichTextKit\TextBlock.cs:line 313
[DOTNET]    at Topten.RichTextKit.TextBlock.Paint(SKCanvas canvas, TextPaintOptions options) in C:\Users\Brad\Projects\RichTextKit\Topten.RichTextKit\TextBlock.cs:line 359
[DOTNET]    at AndroidUI.Topten_RichTextKit_TextView.onDraw(SKCanvas canvas) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\Topten_RichTextKit_TextView.cs:line 196
[DOTNET]    at AndroidUI.View.draw(SKCanvas canvas) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 6284
[DOTNET]    at AndroidUI.View.updateDisplayListIfDirty() in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 6173
[DOTNET]    at AndroidUI.View.draw(SKCanvas canvas, View parent, Int64 drawingTime) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11159
[DOTNET]    at AndroidUI.View.drawChild(SKCanvas canvas, View child, Int64 drawingTime) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11433
[DOTNET]    at AndroidUI.View.dispatchDraw(SKCanvas canvas) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11525
[DOTNET]    at AndroidUI.View.updateDisplayListIfDirty() in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 6160
[DOTNET]    at AndroidUI.View.draw(SKCanvas canvas, View parent, Int64 drawingTime) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11159
[DOTNET]    at AndroidUI.View.drawChild(SKCanvas canvas, View child, Int64 drawingTime) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11433
[DOTNET]    at AndroidUI.View.dispatchDraw(SKCanvas canvas) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11525
[DOTNET]    at AndroidUI.View.updateDisplayListIfDirty() in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 6160
[DOTNET]    at AndroidUI.View.draw(SKCanvas canvas, View parent, Int64 drawingTime) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11159
[DOTNET]    at AndroidUI.View.drawChild(SKCanvas canvas, View child, Int64 drawingTime) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11433
[DOTNET]    at AndroidUI.View.dispatchDraw(SKCanvas canvas) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 11525
[DOTNET]    at AndroidUI.View.updateDisplayListIfDirty() in C:\Users\small\source\repos\WindowsProject1\AndroidUI\View.cs:line 6160
[DOTNET]    at AndroidUI.ViewRootImpl.updateViewTreeDisplayList(SKCanvas drawingCanvas, View view) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\ViewRootImpl.cs:line 2179
[DOTNET]    at AndroidUI.ViewRootImpl.updateRootDisplayList(SKCanvas canvas, View view, Object callbacks) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\ViewRootImpl.cs:line 2192
[DOTNET]    at AndroidUI.ViewRootImpl.draw(SKCanvas canvas, View view, AttachInfo attachInfo, Object callbacks) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\ViewRootImpl.cs:line 2138
[DOTNET]    at AndroidUI.ViewRootImpl.draw(SKCanvas canvas, Boolean fullRedrawNeeded) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\ViewRootImpl.cs:line 2076
[DOTNET]    at AndroidUI.ViewRootImpl.performDraw(SKCanvas canvas) in C:\Users\small\source\repos\WindowsProject1\AndroidUI\ViewRootImpl.cs:line 1462

this works fine in Xamarin build, but gives above in MAUI build

Topten.RichTextKit.Style FontFamily from custom typeface in SKTypeface

I have a custom Typeface that I am using for text in my application. For painting text with SKPaint I can set the Typeface from an custom font that I load into a SKTypeface object. I can't seem to find a way to do that for a Style in the RichTextKit since Style.FontFamily only takes a string. I have tried using SKTypeface.FamilyName but the style doesn't seem to recognize it since it is a custom font.
Is there a way to do this? I have looked around but I can't seem to find anywhere that this is referenced. If it isn't possible could someone point me to a list of acceptable values for Style.FontFamily?

Possible bug - BadImageFormatException

I don't have good repro on this because it was reported on AppCenter, but here's the callstack:

Xamarin Exception Stack:
System.BadImageFormatException: Method has zero rva
  at Topten.RichTextKit.TextShaper.GetHarfBuzzBlob (SkiaSharp.SKStreamAsset asset) [0x00045] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextShaper..ctor (SkiaSharp.SKTypeface typeface) [0x00015] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextShaper.ForTypeface (SkiaSharp.SKTypeface typeface) [0x0001f] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextBlock.CreateFontRun (Topten.RichTextKit.StyleRun styleRun, Topten.RichTextKit.Utils.Slice`1[T] codePoints, Topten.RichTextKit.TextDirection direction, Topten.RichTextKit.IStyle style, SkiaSharp.SKTypeface typeface, SkiaSharp.SKTypeface asFallbackFor) [0x00000] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextBlock.AddFontRun (Topten.RichTextKit.StyleRun styleRun, System.Int32 start, System.Int32 length, Topten.RichTextKit.TextDirection direction, Topten.RichTextKit.IStyle style, SkiaSharp.SKTypeface typeface, SkiaSharp.SKTypeface asFallbackFor) [0x0000e] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextBlock.AddDirectionalRun (Topten.RichTextKit.StyleRun styleRun, System.Int32 start, System.Int32 length, Topten.RichTextKit.TextDirection direction, Topten.RichTextKit.IStyle style) [0x00030] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextBlock.BuildFontRuns () [0x002db] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextBlock.Layout () [0x000d8] in <48c43bfd31714027914cbea8c298ec38>:0
  at Topten.RichTextKit.TextBlock.get_MeasuredWidth () [0x00000] in <48c43bfd31714027914cbea8c298ec38>:0

This was on Android using Topten.RichTextKit v0.1.118

I do realize this is somewhat of an old version, and we may upgrade just to be safe, but I also wanted to bring this up in case it wasn't a known issue.

Missing glyphs when rendering clusters with unmatched combining characters

"Missing glyphs" are rendered when rendering a grapheme cluster where the base character is contained in the font but the combining characters are not. This can be reproduced easily by rendering a text with obscure diacritics, e.g. A, using a font like "Agency FB".

I believe this issue occurs since the font fallback mechanisms only applies fallbacks at cluster boundaries (in FontFallback.GetFontRuns):

// Must be a cluster boundary
if (!GraphemeClusterAlgorithm.IsBoundary(codePoints, i))
    continue;

When rendering text like this with Firefox, Chrome, and GDI+, you get slightly different results for each but they all at least fallback to some other font for the missing combining characters. The CSS specification has some guidance w.r.t. this problem [1], however that solution may be a bit overkill for this library. A simpler solution may be to just use an available font for the missing characters (remove the boundary limitation, this seems to be what Firefox does), or use the font which matches the most consecutive characters in the cluster (seems to be what Chrome does).

Is there a reason why fallbacks are skipped when not at a cluster boundary? Simply removing this limitation results in rendering fairly similar to Firefox.

[1]: https://drafts.csswg.org/css-fonts/#cluster-matching

Selection is not painted if SelectionStart is equal to the lenght of the text.

The selected range is not painted when drawing text if the SelectionStart is set to the length of the string to be drawn, and the SelectionEnd is less than the Selection start. Dragging or selecting rendered text from the end of the text does not show the blue selection background due this if one implements selection in their text control

iOS San Francisco: Different font sizes in the same paragraph ends up with incorrect character spacing

Heyo 👋

This issue appears on iOS when using "San Francisco" as the font family.

In the picture below, the characters for the formatted date (10/17/2020 11:40 AM) are spread too far apart. Looks like this occurs because the date string is added to a paragraph that already has text of a bigger font size.

image

Compare that to this, where I added a new paragraph before the formatted date. The character spacing is correct.

image

TextBlock Exception

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

Option to control ellipsis

As far as I understand, there is no way to disable the ellipsis in RichString, it is always added if the TextBlock is truncated:

if (TextBlock.Lines.Count == 0 && ctx.previousParagraph != null)
{
ctx.previousParagraph.TextBlock.AddEllipsis();
}
// All following blocks should be truncated
ctx.Truncated = true;

Sometimes this behavior is undesirable, and it would be nice to be able to not render the ellipsis at the end.

I suggest to add a bool property to control ellipsis, for example Ellipsis, EllipsisEnabled (true by default).
Is the idea reasonable? If so, I can prepare a patch.

My use case:
I render fairly simple pdf reports, and use one RichString per page. The Truncated property serves as an indicator to me that a new page needs to be created. Everything works fine, but the ellipsis at the end of each page looks terrible, and I want to turn it off.

I also had a thought that maybe I was using RichString wrongly, but after reading the source code I couldn't find a better way. Please correct me if I have missed something obvious.

p.s. Thanks for the great RichTextKit project :)

Google font synthesis?

Hi. We are using google fonts. I cant figure out how to get bold or italics to work. In the browser, these are synthesised from regular. Ive tried loading a specific bold font into the style, but it still comes out at 400. I would rather the font was faked to match the browser (not all google fonts come with italic and bold).

Is this possible? I read that Chrome uses Skia for this so maybe this is not part of SkiaSharp?

Same issue on windows and linux.

TextBlock and Halo

Is there an easy way to create a halo for a given TextBlock? Perhaps by getting the SKPath for the drawn characters and then use SKPaint.GetFillPath()? Or draw TextBlock twice?

Crash with last version of SkiaSharp

Hello,

we encounter a problem, when using RichTextKit 0.3.134 with the version 2.80.2 or 2.80.3 of SkiaSharp. We get the following error message

Method not found: void SkiaSharp.SKFont.GetGlyphs(System.ReadOnlySpan`1<int>,System.Span`1<uint16>)

with the following call stack:

at Topten.RichTextKit.TextBlock.AddDirectionalRun (Topten.RichTextKit.StyleRun styleRun, System.Int32 start, System.Int32 length, Topten.RichTextKit.TextDirection direction, Topten.RichTextKit.IStyle style) [0x00057] in <19e5ac464bd84f9c875ac01889614bdc>:0 
at Topten.RichTextKit.TextBlock.BuildFontRuns () [0x002e6] in <19e5ac464bd84f9c875ac01889614bdc>:0 
at Topten.RichTextKit.TextBlock.Layout () [0x000f5] in <19e5ac464bd84f9c875ac01889614bdc>:0

We get this only on iOS, not on Android or UWP.

After updating to RichTextKit 0.4.145 and SkiaSharp 2.80.3 the error message is slightly different:

Exception in BuildFontRuns() with original length of 13 now 13, style run count 1, font run count 0, direction overrides: False

but the inner exception seems to be the same

Method not found: void SkiaSharp.SKFont.GetGlyphs(System.ReadOnlySpan`1<int>,System.Span`1<uint16>)

and a stack trace like

at Topten.RichTextKit.TextBlock.BuildFontRuns () [0x00370] in C:\Users\Brad\Projects\RichTextKit\Topten.RichTextKit\TextBlock.cs:1133 
at Topten.RichTextKit.TextBlock.Layout () [0x000f5] in C:\Users\Brad\Projects\RichTextKit\Topten.RichTextKit\TextBlock.cs:313 

Did anyone see this problem before? Are we missing something? Worked before (was it SkiaSharp 1.63?), so there must be a change while this time.

Memory leak?

Trying to track down a memory leak we noticed this:

Screenshot from 2021-06-08 12-49-32

We are really only using RichTextKit to measure and paint a text box. I have a custom FontMapper. Perhaps the typefaces are not getting disposed?

possible formatting bug

considering the following code:

var rs = new RichString("Test Text")
    .FontSize(14)
    .TextColor(SkColors.Blue);

when I do it this way the color and size are not taking.

RichString.MeasuredWidth fails if margins are set

The property MeasuredWith returns the proper with of a RichString if left and right margin are 0.

If I set the left and right margin to 10, it fails (it returns the same width as when it was without margin).

Here I have attached two scenarios, where I render the second parragraph following the first one. The top example is whithout margin. The second one is with a margin of 10 in every direction. I drew the bounding box of the RichString in red. You can clearly see the problem.
The bitmap size was calculated adding the MeassuredWidth of both RichText.

I hope you fix it.

PS: congrats for the great library. I had to start using Skia and I couldn't be happier after I found RichTextKit

margin_error

Large PDF file sizes with text

I guess it's because it's vectorizing all the text (or storing the font?); files get huge pretty huge.

Is there a setting or something that might help with this?

I can provide more details if you can let me know what information would be useful.

Thanks in advance.

Publish TextEditorView reference code

Small feature request: would it be possible to publish the source code for TextEditorView.cs?

I know it will not compile because of the GUIKit dependency, but it would be great reference code for implementing your own text editor.

Also, since recent additions in RichTextKit, such as IME support, aren't handled in your editor videos (which are great BTW), it would be nice to have some reference code on how to use this.

Read from RTF

Is it possible to read from an RTF file or string and display using this?

I'm looking to (smoothly) scroll some text from a RichTextBox. This seems to render very well and fast but I just need a way to read the RTF from the RichTextBox.

Feature request: Add support for inline placeholders

Absolutely love this library and it's API.

The only big feature that is missing IMHO, is support for (inline) placeholders that can be custom rendered. This would allow for rendering images, controls or anything custom you may want to draw.

The placeholders just have a size and (baseline) alignment, and possibly an event that is fired to render it.

The Unicode Object Replacement Character (U+FFFD) can be used to represent the placeholder in the text.

See Flutter's ParagraphBuilder.addPlaceholder method for possible API.

Add strong name for .NET framework

My project has a strong name so .net framework does not let me use this library because it does not have a strong name. Please add a strong name to it (SNK file)

Font sizing

A trickier problem this time. We use FabricJS on the front end designer and SkiaSharp on the back end to print documents. Getting the two things to match is always a challenge. Generating a PDF using SkiaSharp and RichTextKit produces different results to the same thing created in MS Word also.

Eg. A sample front end design like this:
image

generates a PDF like this:

image

(the rectangles in these images actually are the same size and are correct when printed - it's just the scaling of these screen shots that dont match).

The font is Arial 10 pt. Im assuming that RichTextKit Font Sizes are pixels and there are 72 points per inch and a PDF is 72 DPI so 10 pt is 10 pixels. However, the character width seems narrower, especially compared to the Word version (sorry, dont have an image of that).

I guess my question is, how realistic is it to expect to get identical/consistent results? I think I'd be happy to get a close match between FabricJS and RichTextKit / SkiaSharp so maybe Ive just answered my own question. :)

Font Fallback breaks text alignment

Right now the current logic, is spaces uses the last Font found during the fallback. When you take into a string like Ride the Comet! ☄️ with even spaces on each side, the spacing is being used to help with padding. It should still properly center, however Emoji character spaces are a lot larger than say Helvetica.

I propose the line should be changed to RunFace = typeface;

Select Roboto Condensed as font for text

Hello,

I have the problem, that I don't find the Roboto Condensed font on my machine when selecting font family for my text. If I look into the fonts in the system manager or use Word, both can see the font. If I look into SKFontManager.FontFamilies I see only the Roboto font.

So I have to provide SKFontStyleWidth with SKFontStyleWidth.Condensed instead of SKFontStyleWidth.Normal. But how to do this with RichTextKit? Is this missing? Could it be, that this line is the problem?

Regards,
Dirk

Incorrect results when used from multiple threads concurrently.

Hi,

when using RichTextKit within an ASP.NET application for PDF export, we noticed that in a multi-threaded context RichTextKit is not behaving properly. We noticed multiple exceptions as well as weird issues with TextBlocks being rendered in different styles than the ones that were defined for the specific text block.

First of all my question would be: Should this usecase (using RichTextKit from multiple threads) be supported? If so, what follows is some example code to reproduce issues and some exceptions that occurred for me.

Open to view test example
using System;
using System.IO;
using System.Threading;
using SkiaSharp;
using Topten.RichTextKit;

namespace RichTextKitIssue
{
    class Program
    {
        private const int MAX_THREADS = 10;

        static void Main(string[] args)
        {
            for (var i = 0; i < MAX_THREADS; i++)
            {
                var i1 = i;
                Thread thread = new Thread(() =>
                {
                    var index = i1;
                    Console.WriteLine("Thread started");
                    var text = GetLayout();
                    var bitmap = Render(text);
                    var path = $"{index.ToString()}.png";
                    using (var file = File.OpenWrite(path))
                    {
                        bitmap.Encode(file, SKEncodedImageFormat.Png, 90);
                    }
                });
                thread.Start();
            }
        }

        public static RichString GetLayout()
        {
            var r = new RichString();
            r = r.FontSize(64).FontWeight((int) SKFontStyleWeight.Bold).Add("Some Text in 64 Bold");
            r = r.Paragraph().FontSize(48).FontWeight((int) SKFontStyleWeight.Bold).Add("Some Text in 48 Bold");
            r = r.Paragraph().FontSize(32).FontWeight((int) SKFontStyleWeight.Bold).Add("Some Text in 32 Bold");
            r = r.Paragraph().FontSize(24).FontWeight((int) SKFontStyleWeight.Bold).Add("Some Text in 24 Bold");
            r = r.Paragraph().FontSize(18).FontWeight((int) SKFontStyleWeight.Bold).Add("Some Text in 18 Bold");
            r = r.Paragraph().FontSize(12).FontWeight((int) SKFontStyleWeight.Bold).Add("Some Text in 12 Bold");
            r = r.Paragraph().FontSize(64).FontWeight((int) SKFontStyleWeight.Normal).Add("Some Text in 64 Normal");
            r = r.Paragraph().FontSize(48).FontWeight((int) SKFontStyleWeight.Normal).Add("Some Text in 48 Normal");
            r = r.Paragraph().FontSize(32).FontWeight((int) SKFontStyleWeight.Normal).Add("Some Text in 32 Normal");
            r = r.Paragraph().FontSize(24).FontWeight((int) SKFontStyleWeight.Normal).Add("Some Text in 24 Normal");
            r = r.Paragraph().FontSize(18).FontWeight((int) SKFontStyleWeight.Normal).Add("Some Text in 18 Normal");
            r = r.Paragraph().FontSize(12).FontWeight((int) SKFontStyleWeight.Normal).Add("Some Text in 12 Normal");
            r = r.Paragraph().FontSize(64).FontWeight((int) SKFontStyleWeight.Light).Add("Some Text in 64 Light");
            r = r.Paragraph().FontSize(48).FontWeight((int) SKFontStyleWeight.Light).Add("Some Text in 48 Light");
            r = r.Paragraph().FontSize(32).FontWeight((int) SKFontStyleWeight.Light).Add("Some Text in 32 Light");
            r = r.Paragraph().FontSize(24).FontWeight((int) SKFontStyleWeight.Light).Add("Some Text in 24 Light");
            r = r.Paragraph().FontSize(18).FontWeight((int) SKFontStyleWeight.Light).Add("Some Text in 18 Light");
            r = r.Paragraph().FontSize(12).FontWeight((int) SKFontStyleWeight.Light).Add("Some Text in 12 Light");
            return r;
        }

        public static SKBitmap Render(RichString r)
        {
            var bitmap = new SKBitmap(1080, 1920);
            var canvas = new SKCanvas(bitmap);
            canvas.Clear(SKColors.White);
            r.Paint(canvas);
            return bitmap;
        }
    }
}

Exceptions that were noticed by just running the example:

System.InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
   at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
   at Topten.RichTextKit.StyleManager.Update(String fontFamily, Nullable`1 fontSize, Nullable`1 fontWeight, Nullable`1 fontItalic, Nullable`1 underline, Nullable`1 strikeThrough, Nullable`1 lineHeight, Nullable`1 textColor, Nullable`1 letterSpacing, Nullable`1 fontVariant, Nullable`1 textDirection)
Unhandled exception. System.ArgumentException: An item with the same key has already been added. Key: Arial.64.700.False.None.None.1.#ff000000.0.Normal.Auto
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at Topten.RichTextKit.StyleManager.Update(String fontFamily, Nullable`1 fontSize, Nullable`1 fontWeight, Nullable`1 fontItalic, Nullable`1 underline, Nullable`1 strikeThrough, Nullable`1 lineHeight, Nullable`1 textColor, Nullable`1 letterSpacing, Nullable`1 fontVariant, Nullable`1 textDirection)

Also there seem to be some rendering issues where text styles are mixed up. The following is an example for this. This is the output of the above sample code when it is rendered correctly:

expected

Here is an example of the above code with rendering issues, where the "Text in 32 Normal" is unfortunately not rendered in 32 normal

got

Hyphenation

hi all,

i'm currently playing a bit with this api - it's really nice so far!

one thing i want to explore is hyphenation. so far, the line break algorithm works nice and also breaks lines correctly at syllables, when the strings contain soft hyphens. This is already great.
so my idea is to prepare the strings and insert soft hyphens based on some dictionary.

a great addition to richtextkit would be to control the hyphen symbol somehow. like:
if a line breaks at a soft hyphen, add a given hyphen character(s) at the end of this line

and maybe hyphenation also came to your mind already and there's more opinions about this?

best,
sebl

Multi-Character emojis do not render correctly

Emoji sequences that are made up of multiple characters combined with the Zero-Width joiners are getting rendered as individual emoji, not the resulting single emoji. A good example of this is the pirate flag (🏴‍☠️) is getting rendered as a black flag (🏴) and a skull and crossbones (☠).

On line 79 of FontFallback.cs, there is a comment:

// Consume as many characters as possible using the requested type face

However, on line 80, there is a 1 hard-coded and the call to consume additional characters is commented out:

count = 1;// RunFace.GetGlyphs((IntPtr)(pch + pos), length - pos, SKEncoding.Utf32, out var glyphs);

If I remove the 1 and restore the call to RunFace.GetGlyphs, the emoji render correctly.
I am curious why the 1 was hard-coded and what possibly breaks by restoring the commented out code.

Strikeout line not centered

Hi. I've started using RichTextKit and noticed that strikeout line is not centered - it is very close to the underline line. See the:
example .

Change TextColor of TextBlock after creation

Hello,

first: thank you for this great project.

I have only one style in a TextBlock and want to change the TextColor of this text later. Is this possible? Or must I recreate the whole TextBlock?

Thank you for your help.

Best regards,
Dirk

Support StrokeWidth on text

Historically (before I transitioned to this library) I would draw text twice. Once with a dark coloured text and a stroke. Then again with another colour over the top to give a high contrast to the text.

It would be great to be able to reproduce this effect. At the moment I'm having to set a background colour instead which is not quite as nice to look at.

image

Would you consider adding this feature or advise me how to achieve it if I have missed it in the docs.

Thanks

Trailing \n is ignored in measurements

When a string ending with \n is added to a TextBlock, measurements and hit testing do not take the newline into account unless there are other characters after it. For example,

tb = new TextBlock();
tb.AddText("Abc\n", new Style());

has tb.LineCount == 1, and it has a MeasuredHeight of a single line.

Where,

tb.AddText("Abc\nxyz", new Style());

or

tb.AddText("Abc\n\n", new Style());

have, as expected, tb.LineCount == 2 and a MeasuredHeight of two lines.

This makes even simple text editing difficult, as it's reasonable for a user to append a single \n at the end of the last line, and it's difficult for developers to special-case correctly to handle this to, for example, put the cursor on the new empty line or correctly size a container to the TextBlock.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.