Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

svg

blazor

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?

like image 454
Kesavan Subramaniam Che Avatar asked Jul 27 '20 06:07

Kesavan Subramaniam Che


People also ask

How do you find the width and height of text in SVG?

To get the SVG's text element's width and height with JavaScript, we can call the getBBox method of the text element to get the text's width and height. We get the text element with document. querySelector . Then we call getBBox on the returned textElement to get an object with the dimensions of the text.

How do I change SVG text size?

You can not change the font size or font width because SVG is not a font. It is Scalable Vector Graphics. If you would have some text in your SVG then you could do something with the font from the text element.


1 Answers

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.

like image 175
Tewr Avatar answered Oct 16 '22 18:10

Tewr