Skip to content

Commit 1bf8714

Browse files
Merge pull request #152 from SixLabors/js/fix-empty-path
DrawTextProcessor - Handle Empty Paths
2 parents 0849d85 + 81385ac commit 1bf8714

File tree

6 files changed

+103
-46
lines changed

6 files changed

+103
-46
lines changed

src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,11 @@ public void EndGlyph()
325325
{
326326
IPath path = this.builder.Build();
327327

328+
if (path.Bounds.Equals(RectangleF.Empty))
329+
{
330+
return;
331+
}
332+
328333
// if we are using the fonts color layers we ignore the request to draw an outline only
329334
// cause that wont really work and instead force drawing with fill with the requested color
330335
// if color fonts disabled then this.currentColor will always be null

tests/ImageSharp.Drawing.Tests/Drawing/Text/DrawTextOnImageTests.cs

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public DrawTextOnImageTests(ITestOutputHelper output)
4040
public void EmojiFontRendering<TPixel>(TestImageProvider<TPixel> provider, bool enableColorFonts)
4141
where TPixel : unmanaged, IPixel<TPixel>
4242
{
43-
Font font = CreateFont("OpenSans-Regular.ttf", 70);
44-
FontFamily emjoiFontFamily = CreateFont("TwemojiMozilla.ttf", 36).Family;
43+
Font font = CreateFont(TestFonts.OpenSans, 70);
44+
FontFamily emjoiFontFamily = CreateFont(TestFonts.TwemojiMozilla, 36).Family;
4545

4646
Color color = Color.Black;
4747
string text = "A short piece of text 😀 with an emoji";
@@ -74,9 +74,8 @@ public void FallbackFontRendering<TPixel>(TestImageProvider<TPixel> provider)
7474
{
7575
// https://github.com/SixLabors/Fonts/issues/171
7676
var collection = new FontCollection();
77-
FontFamily whitney = collection.Install(TestFontUtilities.GetPath("whitney-book.ttf"));
78-
FontFamily malgun = collection.Install(TestFontUtilities.GetPath("malgun.ttf"));
79-
Font font = whitney.CreateFont(25);
77+
Font whitney = CreateFont(TestFonts.WhitneyBook, 25);
78+
FontFamily malgun = CreateFont(TestFonts.Malgun, 25).Family;
8079

8180
Color color = Color.Black;
8281
const string text = "亞DARKSOUL亞";
@@ -97,7 +96,7 @@ public void FallbackFontRendering<TPixel>(TestImageProvider<TPixel> provider)
9796
img =>
9897
{
9998
var center = new PointF(img.Width / 2, img.Height / 2);
100-
img.Mutate(i => i.DrawText(textGraphicOptions, text, font, color, center));
99+
img.Mutate(i => i.DrawText(textGraphicOptions, text, whitney, color, center));
101100
});
102101
}
103102

@@ -106,7 +105,7 @@ public void FallbackFontRendering<TPixel>(TestImageProvider<TPixel> provider)
106105
public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688<TPixel>(TestImageProvider<TPixel> provider)
107106
where TPixel : unmanaged, IPixel<TPixel>
108107
{
109-
Font font = CreateFont("OpenSans-Regular.ttf", 36);
108+
Font font = CreateFont(TestFonts.OpenSans, 36);
110109
Color color = Color.Black;
111110
string text = "A short piece of text";
112111

@@ -142,7 +141,7 @@ public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688_2<TPixel>(Test
142141
{
143142
using (Image<TPixel> img = provider.GetImage())
144143
{
145-
Font font = CreateFont("OpenSans-Regular.ttf", 39);
144+
Font font = CreateFont(TestFonts.OpenSans, 39);
146145
string text = new string('a', 10000);
147146

148147
Rgba32 color = Color.Black;
@@ -159,7 +158,7 @@ public void OpenSansJWithNoneZeroShouldntExtendPastGlyphe<TPixel>(TestImageProvi
159158
{
160159
using (Image<TPixel> img = provider.GetImage())
161160
{
162-
Font font = CreateFont("OpenSans-Regular.ttf", 50);
161+
Font font = CreateFont(TestFonts.OpenSans, 50);
163162
Color color = Color.Black;
164163

165164
img.Mutate(ctx => ctx.DrawText(TestText, font, Color.Black, new PointF(-50, 2)));
@@ -169,11 +168,11 @@ public void OpenSansJWithNoneZeroShouldntExtendPastGlyphe<TPixel>(TestImageProvi
169168
}
170169

171170
[Theory]
172-
[WithSolidFilledImages(20, 50, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", "i")]
173-
[WithSolidFilledImages(200, 150, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)]
174-
[WithSolidFilledImages(900, 150, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)]
175-
[WithSolidFilledImages(400, 45, "White", PixelTypes.Rgba32, 20, 0, 0, "OpenSans-Regular.ttf", TestText)]
176-
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 50, "OpenSans-Regular.ttf", TestText)]
171+
[WithSolidFilledImages(20, 50, "White", PixelTypes.Rgba32, 50, 0, 0, TestFonts.OpenSans, "i")]
172+
[WithSolidFilledImages(200, 150, "White", PixelTypes.Rgba32, 50, 0, 0, TestFonts.SixLaborsSampleAB, AB)]
173+
[WithSolidFilledImages(900, 150, "White", PixelTypes.Rgba32, 50, 0, 0, TestFonts.OpenSans, TestText)]
174+
[WithSolidFilledImages(400, 45, "White", PixelTypes.Rgba32, 20, 0, 0, TestFonts.OpenSans, TestText)]
175+
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 50, TestFonts.OpenSans, TestText)]
177176
public void FontShapesAreRenderedCorrectly<TPixel>(
178177
TestImageProvider<TPixel> provider,
179178
int fontSize,
@@ -194,10 +193,10 @@ public void FontShapesAreRenderedCorrectly<TPixel>(
194193
}
195194

196195
[Theory]
197-
[WithSolidFilledImages(50, 50, "White", PixelTypes.Rgba32, 50, 25, 25, "OpenSans-Regular.ttf", "i", 45, 25, 25)]
198-
[WithSolidFilledImages(200, 200, "White", PixelTypes.Rgba32, 50, 100, 100, "SixLaborsSampleAB.woff", AB, 45, 100, 100)]
199-
[WithSolidFilledImages(1100, 1100, "White", PixelTypes.Rgba32, 50, 550, 550, "OpenSans-Regular.ttf", TestText, 45, 550, 550)]
200-
[WithSolidFilledImages(400, 400, "White", PixelTypes.Rgba32, 20, 200, 200, "OpenSans-Regular.ttf", TestText, 45, 200, 200)]
196+
[WithSolidFilledImages(50, 50, "White", PixelTypes.Rgba32, 50, 25, 25, TestFonts.OpenSans, "i", 45, 25, 25)]
197+
[WithSolidFilledImages(200, 200, "White", PixelTypes.Rgba32, 50, 100, 100, TestFonts.SixLaborsSampleAB, AB, 45, 100, 100)]
198+
[WithSolidFilledImages(1100, 1100, "White", PixelTypes.Rgba32, 50, 550, 550, TestFonts.OpenSans, TestText, 45, 550, 550)]
199+
[WithSolidFilledImages(400, 400, "White", PixelTypes.Rgba32, 20, 200, 200, TestFonts.OpenSans, TestText, 45, 200, 200)]
201200
public void FontShapesAreRenderedCorrectly_WithRotationApplied<TPixel>(
202201
TestImageProvider<TPixel> provider,
203202
int fontSize,
@@ -230,10 +229,10 @@ public void FontShapesAreRenderedCorrectly_WithRotationApplied<TPixel>(
230229
}
231230

232231
[Theory]
233-
[WithSolidFilledImages(50, 50, "White", PixelTypes.Rgba32, 50, 25, 25, "OpenSans-Regular.ttf", "i", -12, 0, 25, 25)]
234-
[WithSolidFilledImages(200, 200, "White", PixelTypes.Rgba32, 50, 100, 100, "SixLaborsSampleAB.woff", AB, 10, 0, 100, 100)]
235-
[WithSolidFilledImages(1100, 1100, "White", PixelTypes.Rgba32, 50, 550, 550, "OpenSans-Regular.ttf", TestText, 0, 10, 550, 550)]
236-
[WithSolidFilledImages(400, 400, "White", PixelTypes.Rgba32, 20, 200, 200, "OpenSans-Regular.ttf", TestText, 0, -10, 200, 200)]
232+
[WithSolidFilledImages(50, 50, "White", PixelTypes.Rgba32, 50, 25, 25, TestFonts.OpenSans, "i", -12, 0, 25, 25)]
233+
[WithSolidFilledImages(200, 200, "White", PixelTypes.Rgba32, 50, 100, 100, TestFonts.SixLaborsSampleAB, AB, 10, 0, 100, 100)]
234+
[WithSolidFilledImages(1100, 1100, "White", PixelTypes.Rgba32, 50, 550, 550, TestFonts.OpenSans, TestText, 0, 10, 550, 550)]
235+
[WithSolidFilledImages(400, 400, "White", PixelTypes.Rgba32, 20, 200, 200, TestFonts.OpenSans, TestText, 0, -10, 200, 200)]
237236
public void FontShapesAreRenderedCorrectly_WithSkewApplied<TPixel>(
238237
TestImageProvider<TPixel> provider,
239238
int fontSize,
@@ -277,7 +276,7 @@ public void FontShapesAreRenderedCorrectly_LargeText<TPixel>(
277276
TestImageProvider<TPixel> provider)
278277
where TPixel : unmanaged, IPixel<TPixel>
279278
{
280-
Font font = CreateFont("OpenSans-Regular.ttf", 36);
279+
Font font = CreateFont(TestFonts.OpenSans, 36);
281280

282281
var sb = new StringBuilder();
283282
string str = Repeat(" ", 78) + "THISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDS";
@@ -327,7 +326,7 @@ public void FontShapesAreRenderedCorrectly_WithLineSpacing<TPixel>(
327326
bool wrap)
328327
where TPixel : unmanaged, IPixel<TPixel>
329328
{
330-
Font font = CreateFont("OpenSans-Regular.ttf", 16);
329+
Font font = CreateFont(TestFonts.OpenSans, 16);
331330

332331
var sb = new StringBuilder();
333332
string str = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.";
@@ -369,9 +368,9 @@ public void FontShapesAreRenderedCorrectly_WithLineSpacing<TPixel>(
369368
}
370369

371370
[Theory]
372-
[WithSolidFilledImages(200, 150, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)]
373-
[WithSolidFilledImages(900, 150, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)]
374-
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 50, "OpenSans-Regular.ttf", TestText)]
371+
[WithSolidFilledImages(200, 150, "White", PixelTypes.Rgba32, 50, 0, 0, TestFonts.SixLaborsSampleAB, AB)]
372+
[WithSolidFilledImages(900, 150, "White", PixelTypes.Rgba32, 50, 0, 0, TestFonts.OpenSans, TestText)]
373+
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 50, TestFonts.OpenSans, TestText)]
375374
public void FontShapesAreRenderedCorrectlyWithAPen<TPixel>(
376375
TestImageProvider<TPixel> provider,
377376
int fontSize,
@@ -393,9 +392,9 @@ public void FontShapesAreRenderedCorrectlyWithAPen<TPixel>(
393392
}
394393

395394
[Theory]
396-
[WithSolidFilledImages(200, 150, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)]
397-
[WithSolidFilledImages(900, 150, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)]
398-
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 50, "OpenSans-Regular.ttf", TestText)]
395+
[WithSolidFilledImages(200, 150, "White", PixelTypes.Rgba32, 50, 0, 0, TestFonts.SixLaborsSampleAB, AB)]
396+
[WithSolidFilledImages(900, 150, "White", PixelTypes.Rgba32, 50, 0, 0, TestFonts.OpenSans, TestText)]
397+
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 50, TestFonts.OpenSans, TestText)]
399398
public void FontShapesAreRenderedCorrectlyWithAPenPatterned<TPixel>(
400399
TestImageProvider<TPixel> provider,
401400
int fontSize,
@@ -417,7 +416,7 @@ public void FontShapesAreRenderedCorrectlyWithAPenPatterned<TPixel>(
417416
}
418417

419418
[Theory]
420-
[WithSolidFilledImages(1000, 1500, "White", PixelTypes.Rgba32, "OpenSans-Regular.ttf")]
419+
[WithSolidFilledImages(1000, 1500, "White", PixelTypes.Rgba32, TestFonts.OpenSans)]
421420
public void TextPositioningIsRobust<TPixel>(TestImageProvider<TPixel> provider, string fontName)
422421
where TPixel : unmanaged, IPixel<TPixel>
423422
{
@@ -448,21 +447,34 @@ public void TextPositioningIsRobust<TPixel>(TestImageProvider<TPixel> provider,
448447
appendSourceFileOrDescription: false);
449448
}
450449

450+
[Fact]
451+
public void CanDrawTextWithEmptyPath()
452+
{
453+
// The following font/text combination generates an empty path.
454+
Font font = CreateFont(TestFonts.WendyOne, 72);
455+
const string text = "Hello\0World";
456+
var renderOptions = new RendererOptions(font);
457+
FontRectangle textSize = TextMeasurer.Measure(text, renderOptions);
458+
459+
Assert.NotEqual(FontRectangle.Empty, textSize);
460+
461+
using var image = new Image<Rgba32>(Configuration.Default, (int)textSize.Width + 20, (int)textSize.Height + 20);
462+
image.Mutate(x => x.DrawText(
463+
text,
464+
font,
465+
Color.Black,
466+
Vector2.Zero));
467+
}
468+
451469
private static string Repeat(string str, int times) => string.Concat(Enumerable.Repeat(str, times));
452470

453471
private static string ToTestOutputDisplayText(string text)
454472
{
455473
string fnDisplayText = text.Replace("\n", string.Empty);
456-
fnDisplayText = fnDisplayText.Substring(0, Math.Min(fnDisplayText.Length, 4));
457-
return fnDisplayText;
474+
return fnDisplayText.Substring(0, Math.Min(fnDisplayText.Length, 4));
458475
}
459476

460477
private static Font CreateFont(string fontName, int size)
461-
{
462-
var fontCollection = new FontCollection();
463-
string fontPath = TestFontUtilities.GetPath(fontName);
464-
Font font = fontCollection.Install(fontPath).CreateFont(size);
465-
return font;
466-
}
478+
=> TestFontUtilities.GetFont(fontName, size);
467479
}
468480
}

tests/ImageSharp.Drawing.Tests/Issues/Issue_46.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class Issue_46
1515
[Fact]
1616
public void CanRenderCustomFont()
1717
{
18-
Font font = CreateFont("icomoon-events.ttf", 175);
18+
Font font = CreateFont(TestFonts.IcoMoonEvents, 175);
1919

2020
var options = new RendererOptions(font)
2121
{

tests/ImageSharp.Drawing.Tests/TestFontUtilities.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.IO;
66
using System.Linq;
77
using System.Reflection;
8+
using SixLabors.Fonts;
89
using IOPath = System.IO.Path;
910

1011
namespace SixLabors.ImageSharp.Drawing.Tests
@@ -14,21 +15,38 @@ namespace SixLabors.ImageSharp.Drawing.Tests
1415
/// </summary>
1516
public static class TestFontUtilities
1617
{
18+
/// <summary>
19+
/// Gets a font with the given name and size.
20+
/// </summary>
21+
/// <param name="name">The name of the font.</param>
22+
/// <param name="size">The font size.</param>
23+
/// <returns>The <see cref="Font"/></returns>
24+
public static Font GetFont(string name, float size)
25+
=> GetFont(new FontCollection(), name, size);
26+
27+
/// <summary>
28+
/// Gets a font with the given name and size.
29+
/// </summary>
30+
/// <param name="collection">The collection to add the font to</param>
31+
/// <param name="name">The name of the font.</param>
32+
/// <param name="size">The font size.</param>
33+
/// <returns>The <see cref="Font"/></returns>
34+
public static Font GetFont(FontCollection collection, string name, float size)
35+
=> collection.Install(GetPath(name)).CreateFont(size);
36+
1737
/// <summary>
1838
/// The formats directory.
1939
/// </summary>
20-
private static readonly string FormatsDirectory = GetFontsDirectory();
40+
private static readonly string FontsDirectory = GetFontsDirectory();
2141

2242
/// <summary>
2343
/// Gets the full qualified path to the file.
2444
/// </summary>
25-
/// <param name="file">
26-
/// The file path.
27-
/// </param>
45+
/// <param name="file">The file path.</param>
2846
/// <returns>
2947
/// The <see cref="string"/>.
3048
/// </returns>
31-
public static string GetPath(string file) => IOPath.Combine(FormatsDirectory, file);
49+
public static string GetPath(string file) => IOPath.Combine(FontsDirectory, file);
3250

3351
/// <summary>
3452
/// Gets the correct path to the formats directory.
@@ -53,7 +71,7 @@ private static string GetFontsDirectory()
5371

5472
AddFormatsDirectoryFromTestAssemblyPath(directories);
5573

56-
string directory = directories.FirstOrDefault(Directory.Exists);
74+
string directory = directories.Find(Directory.Exists);
5775

5876
if (directory != null)
5977
{
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
namespace SixLabors.ImageSharp.Drawing.Tests
5+
{
6+
public static class TestFonts
7+
{
8+
public const string IcoMoonEvents = "icomoon-events.ttf";
9+
10+
public const string Malgun = "malgun.ttf";
11+
12+
public const string OpenSans = "OpenSans-Regular.ttf";
13+
14+
public const string SixLaborsSampleAB = "SixLaborsSampleAB.woff";
15+
16+
public const string TwemojiMozilla = "TwemojiMozilla.ttf";
17+
18+
public const string WendyOne = "WendyOne-Regular.ttf";
19+
20+
public const string WhitneyBook = "whitney-book.ttf";
21+
}
22+
}
22.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)