Closed ispysoftware closed 2 months ago
You're actually measuring the wrong thing here.
TextMeasurer
offers 3 measurements.
MeasureBounds
Measures the text bounds in sub-pixel units.MeasureSize
Measures the minimum size required, in pixel units, to render the glyphs within a text block. Basically, the size of the bounds.MeasureAdvance
Measures the advance (line-height and horizontal/vertical advance) of the text in pixel units.The last one is the one you want in this scenario.
All glyphs within a font contain metrics which dictate where the glyph sits vertically with a line and the MeasureAdvance
method captures this by providing the total advance width and height, taking into account the glyph's metrics, including kerning, bearing, and line height. These metrics dictate where each glyph should be positioned relative to the baseline and how much space it occupies horizontally or vertically, including spacing between characters.
When rendering a background rectangle, using MeasureAdvance
ensures that the rectangle encompasses the entire area that the text will occupy, including any necessary spacing, so your rectangle will correctly align with the full extent of the text. This method prevents the common issue of cutting off portions of the text or misaligning the background due to underestimating the necessary space.
using Image<Rgba32> img = new(224, 108, Color.Green);
img.Mutate(x =>
{
Font font = SystemFonts.CreateFont("Arial", 70);
var to = new TextOptions(font);
to.HorizontalAlignment = HorizontalAlignment.Left;
to.VerticalAlignment = VerticalAlignment.Top;
FontRectangle size = TextMeasurer.MeasureAdvance("Test", to);
DrawingOptions opts = new DrawingOptions
{
GraphicsOptions = new GraphicsOptions { BlendPercentage = 0.4F }
};
var brect = new Rectangle(0, 0, (int)size.Width, (int)size.Height);
x.Fill(opts, Color.FromRgb(100, 0, 0), brect);
var rto = new RichTextOptions(font)
{
Origin = new PointF(0, 0),
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
};
x.DrawText(rto, "Test", new SolidBrush(Color.FromRgb(255, 255, 255)));
});
img.SaveAsPng(@"C:\Users\james\Downloads\2798.png");
Thanks @JimBobSquarePants much appreciated
Using Advance results in spacing above and below the text (i'm guessing where accents and things would go if needed) - is there a way to position this text so it's right at the location - so it fits inside the actual pixel bounds of MeasureSize ?
using Image<Rgba32> img = new(224, 108, Color.Green);
img.Mutate(x =>
{
Font font = SystemFonts.CreateFont("Arial", 70);
var to = new TextOptions(font);
to.HorizontalAlignment = HorizontalAlignment.Left;
to.VerticalAlignment = VerticalAlignment.Top;
FontRectangle size = TextMeasurer.MeasureBounds("Test", to);
DrawingOptions opts = new DrawingOptions
{
GraphicsOptions = new GraphicsOptions { BlendPercentage = 0.4F }
};
RectangularPolygon brect = new(size.X, size.Y, size.Width, size.Height);
x.Fill(opts, Color.FromRgb(100, 0, 0), brect);
var rto = new RichTextOptions(font)
{
Origin = new PointF(0, 0),
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
};
x.DrawText(rto, "Test", new SolidBrush(Color.FromRgb(255, 255, 255)));
});
img.SaveAsPng(@"C:\Users\james\Downloads\2798.png");
Prerequisites
DEBUG
andRELEASE
modeImageSharp version
3.1.5
Other ImageSharp packages and versions
ImageSharp.Drawing 2.1.4
Environment (Operating system, version and so on)
Windows 11 64 bit/ Ubuntu
.NET Framework version
net 6
Description
MeasureText works ok and returns the bounding rectangle of the text for the font. We then draw a rectangle and DrawText to the origin of the rectangle. The text is drawn with a Y-offset that seems to be related to LineSpacing. It's not clear how this offset should be calculated for adjustment so it aligns with the rectangle.
(the code below works as expected on v2.1.9)
Steps to Reproduce
Images