// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Linq; using System.Text; using SixLabors.Fonts; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Drawing.Text { [GroupOutput("Drawing/Text")] public class DrawTextOnImageTests { private const string AB = "AB\nAB"; private const string TestText = "Sphinx of black quartz, judge my vow\n0123456789"; public static ImageComparer TextDrawingComparer = ImageComparer.TolerantPercentage(1e-5f); public static ImageComparer OutlinedTextDrawingComparer = ImageComparer.TolerantPercentage(5e-4f); public DrawTextOnImageTests(ITestOutputHelper output) { this.Output = output; } private ITestOutputHelper Output { get; } [Theory] [WithSolidFilledImages(276, 336, "White", PixelTypes.Rgba32)] public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688(TestImageProvider provider) where TPixel : struct, IPixel { Font font = CreateFont("OpenSans-Regular.ttf", 36); Color color = Color.Black; float padding = 5; var text = "A short piece of text"; using (var img = provider.GetImage()) { float targetWidth = img.Width - (padding * 2); float targetHeight = img.Height - (padding * 2); // measure the text size SizeF size = TextMeasurer.Measure(text, new RendererOptions(font)); //find out how much we need to scale the text to fill the space (up or down) float scalingFactor = Math.Min(img.Width / size.Width, img.Height / size.Height); //create a new font Font scaledFont = new Font(font, scalingFactor * font.Size); var center = new PointF(img.Width / 2, img.Height / 2); var textGraphicOptions = new TextGraphicsOptions(true) { HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; img.Mutate(i => i.DrawText(textGraphicOptions, text, scaledFont, color, center)); } } [Theory] [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] [WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)] [WithSolidFilledImages(400, 40, "White", PixelTypes.Rgba32, 20, 0, 0, "OpenSans-Regular.ttf", TestText)] [WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)] public void FontShapesAreRenderedCorrectly( TestImageProvider provider, int fontSize, int x, int y, string fontName, string text) where TPixel : struct, IPixel { Font font = CreateFont(fontName, fontSize); Color color = Color.Black; provider.VerifyOperation( TextDrawingComparer, img => { img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y))); }, $"{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})", appendPixelTypeToFileName: false, appendSourceFileOrDescription: true); } /// /// Based on: /// https://github.com/SixLabors/ImageSharp/issues/572 /// [Theory] [WithSolidFilledImages(2480, 3508, "White", PixelTypes.Rgba32)] public void FontShapesAreRenderedCorrectly_LargeText( TestImageProvider provider) where TPixel : struct, IPixel { Font font = CreateFont("OpenSans-Regular.ttf", 36); var sb = new StringBuilder(); string str = Repeat(" ", 78) + "THISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDS"; sb.Append(str); string newLines = Repeat(Environment.NewLine, 80); sb.Append(newLines); for (int i = 0; i < 10; i++) { sb.AppendLine(str); } var textOptions = new TextGraphicsOptions { Antialias = true, ApplyKerning = true, VerticalAlignment = VerticalAlignment.Top, HorizontalAlignment = HorizontalAlignment.Left, }; Color color = Color.Black; // Based on the reported 0.0270% difference with AccuracyMultiple = 8 // We should avoid quality regressions leading to higher difference! var comparer = ImageComparer.TolerantPercentage(0.03f); provider.VerifyOperation( comparer, img => { img.Mutate(c => c.DrawText(textOptions, sb.ToString(), font, color, new PointF(10, 5))); }, false, false); } [Theory] [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] [WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)] [WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)] public void FontShapesAreRenderedCorrectlyWithAPen( TestImageProvider provider, int fontSize, int x, int y, string fontName, string text) where TPixel : struct, IPixel { Font font = CreateFont(fontName, fontSize); Color color = Color.Black; provider.VerifyOperation( OutlinedTextDrawingComparer, img => { img.Mutate(c => c.DrawText(text, new Font(font, fontSize), null, Pens.Solid(color, 1), new PointF(x, y))); }, $"pen_{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})", appendPixelTypeToFileName: false, appendSourceFileOrDescription: true); } [Theory] [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] [WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)] [WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)] public void FontShapesAreRenderedCorrectlyWithAPenPatterned( TestImageProvider provider, int fontSize, int x, int y, string fontName, string text) where TPixel : struct, IPixel { Font font = CreateFont(fontName, fontSize); Color color = Color.Black; provider.VerifyOperation( OutlinedTextDrawingComparer, img => { img.Mutate(c => c.DrawText(text, new Font(font, fontSize), null, Pens.DashDot(color, 3), new PointF(x, y))); }, $"pen_{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})", appendPixelTypeToFileName: false, appendSourceFileOrDescription: true); } [Theory] [WithSolidFilledImages(1000, 1500, "White", PixelTypes.Rgba32, "OpenSans-Regular.ttf")] public void TextPositioningIsRobust(TestImageProvider provider, string fontName) where TPixel : struct, IPixel { Font font = CreateFont(fontName, 30); string text = Repeat("Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!\n", 20); var textOptions = new TextGraphicsOptions(true) { WrapTextWidth = 1000 }; string details = fontName.Replace(" ", ""); // Based on the reported 0.1755% difference with AccuracyMultiple = 8 // We should avoid quality regressions leading to higher difference! var comparer = ImageComparer.TolerantPercentage(0.2f); provider.RunValidatingProcessorTest( x => x.DrawText(textOptions, text, font, Color.Black, new PointF(10, 50)), details, comparer, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); } private static string Repeat(string str, int times) => string.Concat(Enumerable.Repeat(str, times)); private static string ToTestOutputDisplayText(string text) { string fnDisplayText = text.Replace("\n", ""); fnDisplayText = fnDisplayText.Substring(0, Math.Min(fnDisplayText.Length, 4)); return fnDisplayText; } private static Font CreateFont(string fontName, int size) { var fontCollection = new FontCollection(); string fontPath = TestFontUtilities.GetPath(fontName); Font font = fontCollection.Install(fontPath).CreateFont(size); return font; } } }