Drawtext Multiline

cinarfoto2cinarfoto2 FRMember ✭✭

Hi, how can I use multiline text in canvas with SkiaSharp? I want set width of my dynamic text where in the middle of square.

Thank you very much

Best Answer

  • AloisDenielAloisDeniel FRUniversity ✭✭
    Accepted Answer

    Okay, I see, something like this :

        private void OnPaint(object sender, SKPaintSurfaceEventArgs e)
            {
                using (var paint = new SKPaint()
                {
                    Color = SKColors.Red,
                    Style = SKPaintStyle.Fill,
                    TextSize = 50,
                })
                {
                    var area = SKRect.Create(0, 0, e.Info.Width, e.Info.Height);
    
                    // Background
                    e.Surface.Canvas.DrawRect(area, paint);
    
                    paint.Color = SKColors.White;
                    this.DrawText(e.Surface.Canvas,"I'm not\nfor everyone\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit.Donec malesuada purus eget dolor faucibus, a convallis erat lacinia.Nunc hendrerit consequat tortor, at sagittis nulla.", area, paint);
                }
            }
    
            public class Line
            {
                public string Value { get; set; }
    
                public float Width { get; set; }
            }
    
            private void DrawText(SKCanvas canvas, string text, SKRect area, SKPaint paint)
            {
                float lineHeight = paint.TextSize * 1.2f;
                var lines = SplitLines(text, paint, area.Width);
                var height = lines.Count() * lineHeight;
    
                var y = area.MidY - height / 2;
    
                foreach (var line in lines)
                {
                    y += lineHeight;
                    var x = area.MidX - line.Width / 2;
                    canvas.DrawText(line.Value, x, y, paint);
                }
            }
    
            private Line[] SplitLines(string text, SKPaint paint, float maxWidth)
            {
                var spaceWidth = paint.MeasureText(" ");
                var lines = text.Split('\n');
    
                return lines.SelectMany((line) =>
                {
                    var result = new List<Line>();
    
                    var words = line.Split(new[] { " " }, StringSplitOptions.None);
    
                    var lineResult = new StringBuilder();
                    float width = 0;
                    foreach (var word in words)
                    {
                        var wordWidth = paint.MeasureText(word);
                        var wordWithSpaceWidth = wordWidth + spaceWidth;
                        var wordWithSpace = word + " ";
    
                        if (width + wordWidth > maxWidth)
                        {
                            result.Add(new Line() { Value = lineResult.ToString(), Width = width });
                            lineResult = new StringBuilder(wordWithSpace);
                            width = wordWithSpaceWidth;
                        }
                        else
                        {
                            lineResult.Append(wordWithSpace);
                            width += wordWithSpaceWidth;
                        }
                    }
    
                    result.Add(new Line() { Value = lineResult.ToString(), Width = width });
    
                    return result.ToArray();
                }).ToArray();
            }
    

Answers

  • AloisDenielAloisDeniel FRUniversity ✭✭

    I opened an issue related to this on SkiaSharp.Extented : https://github.com/mono/SkiaSharp.Extended/issues/12

    A naive workaround :

    private void DrawTextArea(SKCanvas canvas, SKPaint paint, float x, float y,  float maxWidth, float lineHeight, string text)
    {
        var spaceWidth = paint.MeasureText(" ");
        var lines = text.Split(new [] { Environment.NewLine }, StringSplitOptions.None);
        lines = lines.SelectMany(l => SplitLine(paint,maxWidth,l,spaceWidth)).ToArray();
    
        for (int i = 0; i < lines.Length; i++)
        {
            var line = lines[i];
            canvas.DrawText(line, x, y, paint);
            y += lineHeight;
        }
    }
    
    private string[] SplitLine(SKPaint paint, float maxWidth, string text, float spaceWidth)
    {
        var result = new List<string>();
    
        var words = text.Split(new[] { " " }, StringSplitOptions.None);
    
        var line = new StringBuilder();
        float width = 0;
        foreach (var word in words)
        {
            var wordWidth = paint.MeasureText(word);
            var wordWithSpaceWidth = wordWidth + spaceWidth;
            var wordWithSpace = word + " ";
    
            if(width + wordWidth > maxWidth)
            {
                result.Add(line.ToString());
                line = new StringBuilder(wordWithSpace);
                width = wordWithSpaceWidth;
            }
            else
            {
                line.Append(wordWithSpace);
                width += wordWithSpaceWidth;
            }
        }
    
        result.Add(line.ToString());
    
        return result.ToArray();
    }
    
  • cinarfoto2cinarfoto2 FRMember ✭✭

    Thank you very much Alois, but its very hard for something about alignment, style. I am not good at Xamarin. If you help me about style I will be really grateful. it seems like 2. png; And I want that it seems like 1.png

    Thank you very much.

    2.png 17.7K
    1.png 132.9K
  • AloisDenielAloisDeniel FRUniversity ✭✭
    Accepted Answer

    Okay, I see, something like this :

        private void OnPaint(object sender, SKPaintSurfaceEventArgs e)
            {
                using (var paint = new SKPaint()
                {
                    Color = SKColors.Red,
                    Style = SKPaintStyle.Fill,
                    TextSize = 50,
                })
                {
                    var area = SKRect.Create(0, 0, e.Info.Width, e.Info.Height);
    
                    // Background
                    e.Surface.Canvas.DrawRect(area, paint);
    
                    paint.Color = SKColors.White;
                    this.DrawText(e.Surface.Canvas,"I'm not\nfor everyone\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit.Donec malesuada purus eget dolor faucibus, a convallis erat lacinia.Nunc hendrerit consequat tortor, at sagittis nulla.", area, paint);
                }
            }
    
            public class Line
            {
                public string Value { get; set; }
    
                public float Width { get; set; }
            }
    
            private void DrawText(SKCanvas canvas, string text, SKRect area, SKPaint paint)
            {
                float lineHeight = paint.TextSize * 1.2f;
                var lines = SplitLines(text, paint, area.Width);
                var height = lines.Count() * lineHeight;
    
                var y = area.MidY - height / 2;
    
                foreach (var line in lines)
                {
                    y += lineHeight;
                    var x = area.MidX - line.Width / 2;
                    canvas.DrawText(line.Value, x, y, paint);
                }
            }
    
            private Line[] SplitLines(string text, SKPaint paint, float maxWidth)
            {
                var spaceWidth = paint.MeasureText(" ");
                var lines = text.Split('\n');
    
                return lines.SelectMany((line) =>
                {
                    var result = new List<Line>();
    
                    var words = line.Split(new[] { " " }, StringSplitOptions.None);
    
                    var lineResult = new StringBuilder();
                    float width = 0;
                    foreach (var word in words)
                    {
                        var wordWidth = paint.MeasureText(word);
                        var wordWithSpaceWidth = wordWidth + spaceWidth;
                        var wordWithSpace = word + " ";
    
                        if (width + wordWidth > maxWidth)
                        {
                            result.Add(new Line() { Value = lineResult.ToString(), Width = width });
                            lineResult = new StringBuilder(wordWithSpace);
                            width = wordWithSpaceWidth;
                        }
                        else
                        {
                            lineResult.Append(wordWithSpace);
                            width += wordWithSpaceWidth;
                        }
                    }
    
                    result.Add(new Line() { Value = lineResult.ToString(), Width = width });
    
                    return result.ToArray();
                }).ToArray();
            }
    
  • cinarfoto2cinarfoto2 FRMember ✭✭

    Wow! its exactly as I want. Thank you very much!

Sign In or Register to comment.