Skip to content

Commit

Permalink
Merge pull request #418 from SixLabors/js/fix-measurements
Browse files Browse the repository at this point in the history
Don't use Ceil when measuring.
  • Loading branch information
JimBobSquarePants authored Oct 28, 2024
2 parents 51d4306 + caaf901 commit 43b97c4
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 202 deletions.
4 changes: 2 additions & 2 deletions src/SixLabors.Fonts/TextMeasurer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,13 @@ internal static FontRectangle GetAdvance(IReadOnlyList<GlyphLayout> glyphLayouts
Vector2 topLeft = new(left, top);
Vector2 bottomRight = new(right, bottom);
Vector2 size = (bottomRight - topLeft) * dpi;
return new FontRectangle(0, 0, MathF.Ceiling(size.X), MathF.Ceiling(size.Y));
return new FontRectangle(0, 0, size.X, size.Y);
}

internal static FontRectangle GetSize(IReadOnlyList<GlyphLayout> glyphLayouts, float dpi)
{
FontRectangle bounds = GetBounds(glyphLayouts, dpi);
return new FontRectangle(0, 0, MathF.Ceiling(bounds.Width), MathF.Ceiling(bounds.Height));
return new FontRectangle(0, 0, bounds.Width, bounds.Height);
}

internal static FontRectangle GetBounds(IReadOnlyList<GlyphLayout> glyphLayouts, float dpi)
Expand Down
89 changes: 89 additions & 0 deletions tests/SixLabors.Fonts.Tests/ApproximateFloatComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Numerics;

namespace SixLabors.Fonts.Tests;

internal class ApproximateFloatComparer :
IEqualityComparer<float>,
IEqualityComparer<Vector2>,
IEqualityComparer<IEnumerable<Vector2>>,
IEqualityComparer<FontRectangle>
{
private readonly float epsilon;

/// <summary>
/// Initializes a new instance of the <see cref="ApproximateFloatComparer"/> class.
/// </summary>
/// <param name="epsilon">The comparison error difference epsilon to use.</param>
public ApproximateFloatComparer(float epsilon = 1F) => this.epsilon = epsilon;

public bool Equals(float x, float y)
{
float d = x - y;
return d >= -this.epsilon && d <= this.epsilon;
}

public bool Equals(Vector2 x, Vector2 y)
=> this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y);

public bool Equals(IEnumerable<Vector2> x, IEnumerable<Vector2> y)
{
if (ReferenceEquals(x, y))
{
return true;
}

if (x is null)
{
return y is null;
}

if (y is null)
{
return false;
}

if (x.Count() != y.Count())
{
return false;
}

using IEnumerator<Vector2> e1 = x.GetEnumerator();
using IEnumerator<Vector2> e2 = x.GetEnumerator();
while (e1.MoveNext())
{
if (!(e2.MoveNext() && this.Equals(e1.Current, e2.Current)))
{
return false;
}
}

return !e2.MoveNext();
}

public bool Equals(FontRectangle x, FontRectangle y)
=> this.Equals(x.X, y.X) &&
this.Equals(x.Y, y.Y) &&
this.Equals(x.Width, y.Width)
&& this.Equals(x.Height, y.Height);

public int GetHashCode(float obj) => obj.GetHashCode();

public int GetHashCode(Vector2 obj) => obj.GetHashCode();

public int GetHashCode(IEnumerable<Vector2> obj)
{
int hash = 17;
foreach (Vector2 point in obj)
{
hash = (hash * 31) + point.GetHashCode();
}

return hash;
}

public int GetHashCode(FontRectangle obj)
=> obj.GetHashCode();
}
5 changes: 3 additions & 2 deletions tests/SixLabors.Fonts.Tests/GlyphTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace SixLabors.Fonts.Tests;
public class GlyphTests
{
private readonly GlyphRenderer renderer = new();
private static readonly ApproximateFloatComparer Comparer = new(.1F);

[Fact]
public void RenderToPointAndSingleDPI()
Expand Down Expand Up @@ -164,8 +165,8 @@ public void EmojiWidthIsComputedCorrectlyWithSubstitutionOnZwj()
FontRectangle size = TextMeasurer.MeasureSize(text, new TextOptions(font));
FontRectangle size2 = TextMeasurer.MeasureSize(text2, new TextOptions(font));

Assert.Equal(51f, size.Width);
Assert.Equal(51f, size2.Width);
Assert.Equal(50.625F, size.Width, Comparer);
Assert.Equal(50.625F, size2.Width, Comparer);
}

[Theory]
Expand Down
6 changes: 4 additions & 2 deletions tests/SixLabors.Fonts.Tests/Issues/Issues_269.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ namespace SixLabors.Fonts.Tests.Issues;

public class Issues_269
{
private static readonly ApproximateFloatComparer Comparer = new(.1F);

[Fact]
public void CorrectlySetsMetricsForFontsNotAdheringToSpec()
{
// AliceFrancesHMK has invalid subtables.
Font font = new FontCollection().Add(TestFonts.AliceFrancesHMKRegularFile).CreateFont(25);

FontRectangle size = TextMeasurer.MeasureSize("H", new TextOptions(font));
Assert.Equal(32, size.Width, 1F);
Assert.Equal(25, size.Height, 1F);
Assert.Equal(30.6000004F, size.Width, Comparer);
Assert.Equal(24.75F, size.Height, Comparer);
}
}
10 changes: 6 additions & 4 deletions tests/SixLabors.Fonts.Tests/Issues/Issues_367.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
namespace SixLabors.Fonts.Tests.Issues;
public class Issues_367
{
private static readonly ApproximateFloatComparer Comparer = new(.1F);

[Fact]
public void ShouldMatchBrowserBreak()
{
Font font = new FontCollection().Add(TestFonts.CourierPrimeFile).CreateFont(12);

TextOptions options = new(font)
{
Dpi = 96f // 1in = 96px
Dpi = 96F // 1in = 96px
};

const float wrappingLengthInInches = 3.875f;
const float wrappingLengthInInches = 3.875F;
options.WrappingLength = wrappingLengthInInches * options.Dpi;

const string text = "Leer, but lonesome has fussin' change a faith. Themself seen and four trample.";
Expand All @@ -23,7 +25,7 @@ public void ShouldMatchBrowserBreak()
Assert.Equal(3, lineCount);

FontRectangle advance = TextMeasurer.MeasureAdvance(text, options);
Assert.Equal(355, advance.Width);
Assert.Equal(48, advance.Height);
Assert.Equal(354.968658F, advance.Width, Comparer);
Assert.Equal(48, advance.Height, Comparer);
}
}
Loading

0 comments on commit 43b97c4

Please sign in to comment.