|
- using EnvelopeRenderer.Cli.Render;
-
- namespace EnvelopeRenderer.Cli.Tests;
-
- public class RotatedTextAnchorCalculatorTests
- {
- [Fact]
- public void ComputeAnchor_ZeroAngle_ReturnsOriginalAnchorUnchanged()
- {
- var (x, y) = RotatedTextAnchorCalculator.ComputeAnchor(
- x: 120, y: 240, width: 50, ascent: 10, descent: -3, angleDegrees: 0);
-
- Assert.Equal(120, x, precision: 9);
- Assert.Equal(240, y, precision: 9);
- }
-
- [Fact]
- public void ComputeAnchor_180Degrees_ReflectsAnchorThroughBoundingBoxCenter()
- {
- // A 180-degree rotation about a fixed center C maps any point P to 2C - P. Applying that
- // to the anchor itself: the corrected anchor should be exactly the bbox center reflected
- // through itself minus... concretely: center = (x + width/2, y + (ascent+descent)/2);
- // for a 180-degree rotation, anchor' = 2*center - (x, y).
- const double x = 100;
- const double y = 100;
- const double width = 40;
- const double ascent = 20;
- const double descent = -4;
-
- var centerX = x + (width / 2.0);
- var centerY = y + ((ascent + descent) / 2.0);
-
- var (anchorX, anchorY) = RotatedTextAnchorCalculator.ComputeAnchor(x, y, width, ascent, descent, 180);
-
- Assert.Equal((2 * centerX) - x, anchorX, precision: 6);
- Assert.Equal((2 * centerY) - y, anchorY, precision: 6);
- }
-
- [Fact]
- public void ComputeAnchor_90Degrees_MatchesHandDerivedCounterclockwiseFormula()
- {
- // Confirmed empirically against the real Debenu DLL (see RotatedTextAnchorCalculator's
- // class remarks): positive angle is counterclockwise in PDF's Y-up space, i.e. the
- // standard math rotation matrix applied directly with no sign flip.
- const double x = 0;
- const double y = 0;
- const double width = 10; // centerLocal = (5, 5)
- const double ascent = 12;
- const double descent = -2; // (ascent+descent)/2 = 5
-
- var (anchorX, anchorY) = RotatedTextAnchorCalculator.ComputeAnchor(x, y, width, ascent, descent, 90);
-
- // R(90) * (5,5) = (5*cos90 - 5*sin90, 5*sin90 + 5*cos90) = (-5, 5).
- // anchor' = (x,y) + centerLocal - R(90)*centerLocal = (0,0) + (5,5) - (-5,5) = (10, 0).
- Assert.Equal(10, anchorX, precision: 6);
- Assert.Equal(0, anchorY, precision: 6);
- }
-
- [Fact]
- public void ComputeAnchor_NegativeAngle_RotatesTheOppositeDirection()
- {
- var (positiveX, positiveY) = RotatedTextAnchorCalculator.ComputeAnchor(50, 50, 30, 15, -3, 40);
- var (negativeX, negativeY) = RotatedTextAnchorCalculator.ComputeAnchor(50, 50, 30, 15, -3, -40);
-
- Assert.NotEqual(positiveX, negativeX, precision: 3);
- Assert.NotEqual(positiveY, negativeY, precision: 3);
- }
-
- [Theory]
- [InlineData(30)]
- [InlineData(90)]
- [InlineData(180)]
- [InlineData(-45)]
- [InlineData(270)]
- public void ComputeAnchor_AnyAngle_PreservesTheUnrotatedBoundingBoxCenter(double angle)
- {
- // The whole point of this calculator: regardless of angle, the resulting anchor, once
- // Debenu rotates its local bbox center around it by `angle`, lands back on the *original*
- // (unrotated) bbox center. Verify that directly by applying the same rotation formula
- // DrawRotatedText uses (confirmed CCW-positive) to the anchor's own local center offset.
- const double x = 77;
- const double y = 33;
- const double width = 64;
- const double ascent = 18;
- const double descent = -6;
-
- var centerLocalX = width / 2.0;
- var centerLocalY = (ascent + descent) / 2.0;
- var expectedCenterX = x + centerLocalX;
- var expectedCenterY = y + centerLocalY;
-
- var (anchorX, anchorY) = RotatedTextAnchorCalculator.ComputeAnchor(x, y, width, ascent, descent, angle);
-
- var radians = angle * Math.PI / 180.0;
- var cos = Math.Cos(radians);
- var sin = Math.Sin(radians);
- var rotatedCenterX = anchorX + ((centerLocalX * cos) - (centerLocalY * sin));
- var rotatedCenterY = anchorY + ((centerLocalX * sin) + (centerLocalY * cos));
-
- Assert.Equal(expectedCenterX, rotatedCenterX, precision: 6);
- Assert.Equal(expectedCenterY, rotatedCenterY, precision: 6);
- }
- }
|