How to find svg text size in pixels for blazor application?

I am using svg elements in our blazor applications. In that I am using text elements inside an Svg. Meanwhile I need text element's height and width in pixels. Based on that I am doing some dimension changes in SVG element. Unfortunately, I have no idea on how to get the text element's size.

In ASP.Net applications, there is a way like below,

using System.Drawing;
private float GetWidthOfString(string str)
{
    Bitmap objBitmap = default(Bitmap);
    Graphics objGraphics = default(Graphics);
    objBitmap = new Bitmap(500, 200);
    objGraphics = Graphics.FromImage(objBitmap);
    SizeF stringSize = objGraphics.MeasureString(str, new Font("Arial", 12));
    objBitmap.Dispose();
    objGraphics.Dispose();
    return stringSize.Width;
}

But I can't use this in blazor application. Is there any way find the text element's size in pixels?

Upvotes: 6

Views: 1871

Answers (1)

Tewr
Tewr

Reputation: 3853

The simplest way to get graphical information in Blazor is to use the DOM.

Measure the text using javascript by creating a temporary svg element.

In your javascript, create the following function:

window.measureString = function(textParams) {
    const svg = `
    <svg style="position:absolute">
            <text 
             font-family="${textParams.Font}" 
             font-size="${textParams.Size}">
                    ${textParams.Text}
            </text>
    </svg>`;
    const el = document.createElement("div");
    el.innerHTML = svg;
    document.body.appendChild(el);
    const svgText = el.querySelector('text').getBBox();
    el.remove();
    return { Height: svgText.height, Width: svgText.width };
}

...and interop to that function.

In your .blazor file:

@inject IJsInterop js;

@code {
   
   public class Measurement { public decimal Width { get;set; } public decimal Height { get;set; }}

   Task MeasureString(string text, string font, int size) {
      var measurement = await js.InvokeAsync<Measurement>("measureString", 
          new {
              Text = text,
              Font = font,
              Size = size
          });
      Console.WriteLine($"Width: {measurement.Width} Height: {measurement.Height}";
   }
}

The code above averages at 2ms per call.

Out of curiosity, I also found a way of doing (more or less) the same measurement with native c# using the library SixLabors.Fonts.

The following code gets you a measurement. The font itself must be provided first as a Stream, so quite a bit of work. Also, the measurement does not yield the same result as the js code above, so probably not compatible with SVG measurements. I used C:\Windows\Fonts\Arial.ttf as a source in a MemoryStream.

using SixLabors.Fonts;

var fontStream = GetFontAsMemoryStream();
var fonts = new FontCollection();    
var font = fonts.Install(fontStream);
var renderOption = new RendererOptions(font.CreateFont(size));
var measurement = TextMeasurer.Measure(text, renderOption);

The above code averages at 2500ms, so a bit more than 1000 times slower than js interop. Might be possible to cache the result of fonts.Install() somehow, but I did not immediately succeed to re-use the font instance without exceptions.

Upvotes: 2

Related Questions