SixLabors / ImageSharp.Drawing

:pen: Extensions to ImageSharp containing a cross-platform 2D polygon manipulation API and drawing operations.
https://sixlabors.com/products/imagesharp-drawing/
Other
286 stars 39 forks source link

Gap at top of text when using DrawText #338

Closed ispysoftware closed 2 months ago

ispysoftware commented 2 months ago

Prerequisites

ImageSharp 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

img.Mutate(x =>
{
var to = new TextOptions(font);
to.HorizontalAlignment = HorizontalAlignment.Left;
to.VerticalAlignment = VerticalAlignment.Top;

FontRectangle size = TextMeasurer.MeasureSize("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)));
});

Images

text-align

JimBobSquarePants commented 2 months ago

You're actually measuring the wrong thing here.

TextMeasurer offers 3 measurements.

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.

JimBobSquarePants commented 2 months ago
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");

2798

ispysoftware commented 2 months ago

Thanks @JimBobSquarePants much appreciated

ispysoftware commented 2 months ago

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 ?

JimBobSquarePants commented 2 months ago
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");

2798