diff --git a/src/UglyToad.PdfPig.Core/PdfRectangle.cs b/src/UglyToad.PdfPig.Core/PdfRectangle.cs index f5661b50..dd500b4e 100644 --- a/src/UglyToad.PdfPig.Core/PdfRectangle.cs +++ b/src/UglyToad.PdfPig.Core/PdfRectangle.cs @@ -11,32 +11,32 @@ /// The Y-axis extends vertically upwards and the X-axis horizontally to the right. /// Unless otherwise specified on a per-page basis, units in PDF space are equivalent to a typographic point (1/72 inch). /// - public struct PdfRectangle + public readonly struct PdfRectangle { /// /// Top left point of the rectangle. /// - public readonly PdfPoint TopLeft { get; } + public PdfPoint TopLeft { get; } /// /// Top right point of the rectangle. /// - public readonly PdfPoint TopRight { get; } + public PdfPoint TopRight { get; } /// /// Bottom right point of the rectangle. /// - public readonly PdfPoint BottomRight { get; } + public PdfPoint BottomRight { get; } /// /// Bottom left point of the rectangle. /// - public readonly PdfPoint BottomLeft { get; } + public PdfPoint BottomLeft { get; } /// /// Centroid point of the rectangle. /// - public readonly PdfPoint Centroid + public PdfPoint Centroid { get { @@ -46,47 +46,23 @@ } } - private double width; /// /// Width of the rectangle. /// A positive number. /// - public double Width - { - get - { - if (double.IsNaN(width)) - { - GetWidthHeight(); - } + public double Width { get; } - return width; - } - } - - private double height; /// /// Height of the rectangle. /// A positive number. /// - public double Height - { - get - { - if (double.IsNaN(height)) - { - GetWidthHeight(); - } - - return height; - } - } + public double Height { get; } /// /// Rotation angle of the rectangle. Counterclockwise, in degrees. /// -180 ≤ θ ≤ 180 /// - public readonly double Rotation => GetT() * 180 / Math.PI; + public double Rotation => GetT() * 180 / Math.PI; /// /// Area of the rectangle. @@ -96,22 +72,22 @@ /// /// Left. This value is only valid if the rectangle is not rotated, check . /// - public readonly double Left => TopLeft.X < TopRight.X ? TopLeft.X : TopRight.X; + public double Left => TopLeft.X < TopRight.X ? TopLeft.X : TopRight.X; /// /// Top. This value is only valid if the rectangle is not rotated, check . /// - public readonly double Top => TopLeft.Y > BottomLeft.Y ? TopLeft.Y : BottomLeft.Y; + public double Top => TopLeft.Y > BottomLeft.Y ? TopLeft.Y : BottomLeft.Y; /// /// Right. This value is only valid if the rectangle is not rotated, check . /// - public readonly double Right => BottomRight.X > BottomLeft.X ? BottomRight.X : BottomLeft.X; + public double Right => BottomRight.X > BottomLeft.X ? BottomRight.X : BottomLeft.X; /// /// Bottom. This value is only valid if the rectangle is not rotated, check . /// - public readonly double Bottom => BottomRight.Y < TopRight.Y ? BottomRight.Y : TopRight.Y; + public double Bottom => BottomRight.Y < TopRight.Y ? BottomRight.Y : TopRight.Y; /// /// Create a new . @@ -155,8 +131,8 @@ BottomLeft = bottomLeft; BottomRight = bottomRight; - width = double.NaN; - height = double.NaN; + Width = Math.Sqrt((BottomLeft.X - BottomRight.X) * (BottomLeft.X - BottomRight.X) + (BottomLeft.Y - BottomRight.Y) * (BottomLeft.Y - BottomRight.Y)); + Height = Math.Sqrt((BottomLeft.X - TopLeft.X) * (BottomLeft.X - TopLeft.X) + (BottomLeft.Y - TopLeft.Y) * (BottomLeft.Y - TopLeft.Y)); } /// @@ -165,7 +141,7 @@ /// The distance to move the rectangle in the x direction relative to its current location. /// The distance to move the rectangle in the y direction relative to its current location. /// A new rectangle shifted on the y axis by the given delta value. - public readonly PdfRectangle Translate(double dx, double dy) + public PdfRectangle Translate(double dx, double dy) { return new PdfRectangle(TopLeft.Translate(dx, dy), TopRight.Translate(dx, dy), BottomLeft.Translate(dx, dy), BottomRight.Translate(dx, dy)); @@ -174,7 +150,7 @@ /// /// -π ≤ θ ≤ π /// - private readonly double GetT() + private double GetT() { if (!BottomRight.Equals(BottomLeft)) { @@ -185,24 +161,6 @@ return Math.Atan2(TopLeft.Y - BottomLeft.Y, TopLeft.X - BottomLeft.X) - Math.PI / 2; } - private void GetWidthHeight() - { - var t = GetT(); - var cos = Math.Cos(t); - var sin = Math.Sin(t); - - var inverseRotation = new TransformationMatrix( - cos, -sin, 0, - sin, cos, 0, - 0, 0, 1); - - // Using Abs as a proxy for Euclidean distance in 1D - // as it might happen that points have negative coordinates. - var bl = inverseRotation.Transform(BottomLeft); - width = Math.Abs(inverseRotation.Transform(BottomRight).X - bl.X); - height = Math.Abs(inverseRotation.Transform(TopLeft).Y - bl.Y); - } - /// public override string ToString() { diff --git a/src/UglyToad.PdfPig.Tests/Fonts/SystemFonts/Linux.cs b/src/UglyToad.PdfPig.Tests/Fonts/SystemFonts/Linux.cs index ecd4c305..0906c8d4 100644 --- a/src/UglyToad.PdfPig.Tests/Fonts/SystemFonts/Linux.cs +++ b/src/UglyToad.PdfPig.Tests/Fonts/SystemFonts/Linux.cs @@ -64,10 +64,10 @@ namespace UglyToad.PdfPig.Tests.Fonts.SystemFonts var current = page.Letters[i]; - Assert.Equal(expectedData.TopLeft.X, current.GlyphRectangle.TopLeft.X, 7); - Assert.Equal(expectedData.TopLeft.Y, current.GlyphRectangle.TopLeft.Y, 7); - Assert.Equal(expectedData.Width, current.GlyphRectangle.Width, 7); - Assert.Equal(expectedData.Height, current.GlyphRectangle.Height, 7); + Assert.Equal(expectedData.TopLeft.X, current.GlyphRectangle.TopLeft.X, 6); + Assert.Equal(expectedData.TopLeft.Y, current.GlyphRectangle.TopLeft.Y, 6); + Assert.Equal(expectedData.Width, current.GlyphRectangle.Width, 6); + Assert.Equal(expectedData.Height, current.GlyphRectangle.Height, 6); Assert.Equal(expectedData.Rotation, current.GlyphRectangle.Rotation, 3); } } diff --git a/src/UglyToad.PdfPig.Tests/Geometry/PdfRectangleTests.cs b/src/UglyToad.PdfPig.Tests/Geometry/PdfRectangleTests.cs index 40364341..db728b30 100644 --- a/src/UglyToad.PdfPig.Tests/Geometry/PdfRectangleTests.cs +++ b/src/UglyToad.PdfPig.Tests/Geometry/PdfRectangleTests.cs @@ -3,12 +3,11 @@ using Content; using PdfPig.Geometry; using PdfPig.Core; - using System.Drawing; public class PdfRectangleTests { - private static readonly DoubleComparer DoubleComparer = new DoubleComparer(3); - private static readonly DoubleComparer PreciseDoubleComparer = new DoubleComparer(6); + private static readonly DoubleComparer DoubleComparer = new DoubleComparer(0.001); + private static readonly DoubleComparer PreciseDoubleComparer = new DoubleComparer(0.000001); private static readonly PointComparer PointComparer = new PointComparer(DoubleComparer); private static readonly PdfRectangle UnitRectangle = new PdfRectangle(new PdfPoint(0, 0), new PdfPoint(1, 1)); @@ -1460,6 +1459,11 @@ new PdfPoint(79.72935886126837, 162.40175959739133), new PdfPoint(36.31110596050322, 137.33421959739135), new PdfPoint(61.57811596050321, 93.57047452204044) + }, + new double[] + { + 50.135079999999988, // width + 50.53402 // height } }, new object[] @@ -1475,6 +1479,11 @@ new PdfPoint(355.93382538513987, -509.2927859424674), new PdfPoint(291.2932316380764, -201.33455131845915), new PdfPoint(123.95226520870021, -236.45950727688313) + }, + new double[] + { + 314.66916060000005, + 170.98760649999997 } }, new object[] @@ -1490,6 +1499,11 @@ new PdfPoint(748.3932365836752, 221.98391118471883), new PdfPoint(-70.46470122718794, 247.50297212341658), new PdfPoint(-79.61250178788342, -46.03248047926875) + }, + new double[] + { + 819.255482, + 293.67796 } }, new object[] @@ -1505,6 +1519,11 @@ new PdfPoint(-62.47760759065051, 323.00991498515555), new PdfPoint(227.9582036948802, 613.4457262706862), new PdfPoint(-120.22754499429334, 961.6314749598598) + }, + new double[] + { + 410.73826331883026, + 492.4090080212593 } }, new object[] @@ -1520,6 +1539,11 @@ new PdfPoint(956.4918489990248, 290.16467735562713), new PdfPoint(1243.8589223186318, 2.797604036020175), new PdfPoint(1163.8416408097196, -77.21967747289204) + }, + new double[] + { + 406.39841246805167, + 113.16152473412956 } } }; @@ -1646,7 +1670,7 @@ Assert.Equal(new PdfPoint(-1, 1), rotated.TopRight); Assert.Equal(1, rotated.Width, PreciseDoubleComparer); - Assert.Equal(-1, rotated.Height, PreciseDoubleComparer); + Assert.Equal(1, rotated.Height, PreciseDoubleComparer); Assert.Equal(90, rotated.Rotation, PreciseDoubleComparer); } @@ -1662,20 +1686,28 @@ Assert.Equal(new PdfPoint(0, -1), rotated.TopLeft); Assert.Equal(new PdfPoint(-1, -1), rotated.TopRight); - Assert.Equal(-1, rotated.Width, PreciseDoubleComparer); - Assert.Equal(-1, rotated.Height, PreciseDoubleComparer); + Assert.Equal(1, rotated.Width, PreciseDoubleComparer); + Assert.Equal(1, rotated.Height, PreciseDoubleComparer); Assert.Equal(180, rotated.Rotation, PreciseDoubleComparer); } [Theory] [MemberData(nameof(RotateData))] - public void Rotate(double[][] data, PdfPoint[] expected) + public void Rotate(double[][] data, PdfPoint[] expected, double[] expectedSize) { var points = data[0]; var angle = data[1][0]; + double width = expectedSize[0]; + double height = expectedSize[1]; + var rect = new PdfRectangle(points[0], points[1], points[2], points[3]); + Assert.Equal(width, rect.Width, PreciseDoubleComparer); + Assert.Equal(height, rect.Height, PreciseDoubleComparer); + var rectR = TransformationMatrix.GetRotationMatrix(angle).Transform(rect); + Assert.Equal(width, rectR.Width, PreciseDoubleComparer); + Assert.Equal(height, rectR.Height, PreciseDoubleComparer); Assert.Equal(expected[0], rectR.BottomRight, PointComparer); Assert.Equal(expected[1], rectR.TopRight, PointComparer);