Browse Source

Added tests for elements: Box, Layers, Rotate, Scale, SimpleRotate, Translate, Unconstrained

Marcin Ziąbek 4 years ago
parent
commit
e411554a29

+ 3 - 3
QuestPDF.Examples/ElementExamples.cs

@@ -558,7 +558,7 @@ namespace QuestPDF.Examples
         {
         {
             RenderingTest
             RenderingTest
                 .Create()
                 .Create()
-                .PageSize(350, 350)
+                .PageSize(650, 450)
                 .FileName()
                 .FileName()
                 .Render(container =>
                 .Render(container =>
                 {
                 {
@@ -572,8 +572,8 @@ namespace QuestPDF.Examples
                             foreach (var turns in Enumerable.Range(0, 4))
                             foreach (var turns in Enumerable.Range(0, 4))
                             {
                             {
                                 grid.Item()
                                 grid.Item()
-                                    .Width(150)
-                                    .Height(150)
+                                    .Width(300)
+                                    .Height(200)
                                     .Background(Colors.Grey.Lighten2)
                                     .Background(Colors.Grey.Lighten2)
                                     .Padding(10)
                                     .Padding(10)
                                     .Element(element =>
                                     .Element(element =>

+ 2 - 2
QuestPDF.Examples/QuestPDF.Examples.csproj

@@ -6,8 +6,8 @@
     </PropertyGroup>
     </PropertyGroup>
 
 
     <ItemGroup>
     <ItemGroup>
-        <PackageReference Include="nunit" Version="3.12.0" />
-        <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
+        <PackageReference Include="nunit" Version="3.13.2" />
+        <PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
         <PackageReference Include="SkiaSharp" Version="2.80.2" />
         <PackageReference Include="SkiaSharp" Version="2.80.2" />
     </ItemGroup>
     </ItemGroup>

+ 2 - 2
QuestPDF.ReportSample/QuestPDF.ReportSample.csproj

@@ -9,8 +9,8 @@
 
 
     <ItemGroup>
     <ItemGroup>
         <PackageReference Include="DeepCloner" Version="0.10.2" />
         <PackageReference Include="DeepCloner" Version="0.10.2" />
-        <PackageReference Include="nunit" Version="3.12.0" />
-        <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
+        <PackageReference Include="nunit" Version="3.13.2" />
+        <PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
         <PackageReference Include="SkiaSharp" Version="2.80.2" />
         <PackageReference Include="SkiaSharp" Version="2.80.2" />
     </ItemGroup>
     </ItemGroup>

+ 53 - 0
QuestPDF.UnitTests/BoxTests.cs

@@ -0,0 +1,53 @@
+using NUnit.Framework;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using QuestPDF.UnitTests.TestEngine;
+
+namespace QuestPDF.UnitTests
+{
+    [TestFixture]
+    public class BoxTests
+    {
+        [Test]
+        public void Draw_Wrap()
+        {
+            TestPlan
+                .For(x => new Box
+                {
+                    Child = x.CreateChild()
+                })
+                .DrawElement(new Size(400, 300))
+                .ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new Wrap())
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Measure_PartialRender()
+        {
+            TestPlan
+                .For(x => new Box
+                {
+                    Child = x.CreateChild()
+                })
+                .MeasureElement(new Size(400, 300))
+                .ExpectChildMeasure(expectedInput: new Size(400, 300), returns: new PartialRender(200, 100))
+                .ExpectChildDraw(new Size(200, 100))
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Measure_FullRender()
+        {
+            TestPlan
+                .For(x => new Box
+                {
+                    Child = x.CreateChild()
+                })
+                .MeasureElement(new Size(500, 400))
+                .ExpectChildMeasure(expectedInput: new Size(500, 400), returns: new FullRender(300, 200))
+                .ExpectChildDraw(new Size(300, 200))
+                .CheckDrawResult();
+        }
+    }
+}

+ 131 - 0
QuestPDF.UnitTests/LayersTests.cs

@@ -0,0 +1,131 @@
+using System.Collections.Generic;
+using NUnit.Framework;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using QuestPDF.UnitTests.TestEngine;
+
+namespace QuestPDF.UnitTests
+{
+    [TestFixture]
+    public class LayersTests
+    {
+        private const string BackgroundLayer = "background";
+        private const string MainLayer = "main";
+        private const string ForegroundLayer = "foreground";
+        
+        private static Layers GetLayers(TestPlan x)
+        {
+            return new Layers
+            {
+                Children = new List<Layer>
+                {
+                    new Layer
+                    {
+                        Child = x.CreateChild(BackgroundLayer)
+                    },
+                    new Layer
+                    {
+                        Child = x.CreateChild(MainLayer),
+                        IsPrimary = true
+                    },
+                    new Layer
+                    {
+                        Child = x.CreateChild(ForegroundLayer)
+                    }
+                }
+            };
+        }
+        
+        #region measure
+        
+        [Test]
+        public void Measure_Wrap()
+        {
+            TestPlan
+                .For(GetLayers)
+                .MeasureElement(new Size(800, 600))
+                .ExpectChildMeasure(MainLayer, new Size(800, 600), new Wrap())
+                .CheckMeasureResult(new Wrap());
+        }
+
+        [Test]
+        public void Measure_PartialRender()
+        {
+            TestPlan
+                .For(GetLayers)
+                .MeasureElement(new Size(800, 600))
+                .ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(700, 500))
+                .CheckMeasureResult(new PartialRender(700, 500));
+        }
+        
+        [Test]
+        public void Measure_FullRender()
+        {
+            TestPlan
+                .For(GetLayers)
+                .MeasureElement(new Size(800, 600))
+                .ExpectChildMeasure(MainLayer, new Size(800, 600), new FullRender(500, 400))
+                .CheckMeasureResult(new FullRender(500, 400));
+        }
+        
+        #endregion
+        
+        #region draw
+        
+        [Test]
+        public void Draw_Simple()
+        {
+            TestPlan
+                .For(GetLayers)
+                .MeasureElement(new Size(800, 600))
+                
+                .ExpectChildMeasure(BackgroundLayer, new Size(800, 600), new FullRender(100, 200))
+                .ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(200, 300))
+                .ExpectChildMeasure(ForegroundLayer, new Size(800, 600), new FullRender(300, 400))
+                
+                
+                .ExpectChildDraw(BackgroundLayer, new Size(800, 600))
+                .ExpectChildDraw(MainLayer, new Size(800, 600))
+                .ExpectChildDraw(ForegroundLayer, new Size(800, 600))
+                
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_WhenSecondaryLayerReturnsWrap_SkipThatLayer_1()
+        {
+            TestPlan
+                .For(GetLayers)
+                .MeasureElement(new Size(800, 600))
+                
+                .ExpectChildMeasure(BackgroundLayer, new Size(800, 600), new PartialRender(100, 200))
+                .ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(200, 300))
+                .ExpectChildMeasure(ForegroundLayer, new Size(800, 600), new Wrap())
+                
+                .ExpectChildDraw(BackgroundLayer, new Size(800, 600))
+                .ExpectChildDraw(MainLayer, new Size(800, 600))
+                
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_WhenSecondaryLayerReturnsWrap_SkipThatLayer_2()
+        {
+            TestPlan
+                .For(GetLayers)
+                .MeasureElement(new Size(800, 600))
+                
+                .ExpectChildMeasure(BackgroundLayer, new Size(800, 600), new Wrap())
+                .ExpectChildMeasure(MainLayer, new Size(800, 600), new PartialRender(200, 300))
+                .ExpectChildMeasure(ForegroundLayer, new Size(800, 600), new PartialRender(300, 400))
+                
+                .ExpectChildDraw(MainLayer, new Size(800, 600))
+                .ExpectChildDraw(ForegroundLayer, new Size(800, 600))
+                
+                .CheckDrawResult();
+        }
+        
+        #endregion
+    }
+}

+ 3 - 3
QuestPDF.UnitTests/QuestPDF.UnitTests.csproj

@@ -6,10 +6,10 @@
     </PropertyGroup>
     </PropertyGroup>
 
 
     <ItemGroup>
     <ItemGroup>
-        <PackageReference Include="FluentAssertions" Version="5.10.3" />
+        <PackageReference Include="FluentAssertions" Version="6.1.0" />
         <PackageReference Include="Moq" Version="4.13.1" />
         <PackageReference Include="Moq" Version="4.13.1" />
-        <PackageReference Include="nunit" Version="3.13.1" />
-        <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
+        <PackageReference Include="nunit" Version="3.13.2" />
+        <PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
         <PackageReference Include="SkiaSharp" Version="2.80.2" />
         <PackageReference Include="SkiaSharp" Version="2.80.2" />
     </ItemGroup>
     </ItemGroup>

+ 28 - 0
QuestPDF.UnitTests/RotateTests.cs

@@ -0,0 +1,28 @@
+using NUnit.Framework;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using QuestPDF.UnitTests.TestEngine;
+
+namespace QuestPDF.UnitTests
+{
+    [TestFixture]
+    public class RotateTests
+    {
+        [Test]
+        public void Draw()
+        {
+            TestPlan
+                .For(x => new Rotate
+                {
+                    Child = x.CreateChild(),
+                    Angle = 123
+                })
+                .DrawElement(new Size(400, 300))
+                .ExpectCanvasRotate(123)
+                .ExpectChildDraw(new Size(400, 300))
+                .ExpectCanvasRotate(-123)
+                .CheckDrawResult();
+        } 
+    }
+}

+ 152 - 0
QuestPDF.UnitTests/ScaleTests.cs

@@ -0,0 +1,152 @@
+using NUnit.Framework;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using QuestPDF.UnitTests.TestEngine;
+
+namespace QuestPDF.UnitTests
+{
+    [TestFixture]
+    public class ScaleTests
+    {
+        #region measure
+        
+        [Test]
+        public void Measure_Wrap()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = 3,
+                    ScaleY = 2
+                })
+                .MeasureElement(new Size(900, 800))
+                .ExpectChildMeasure(new Size(300, 400), new Wrap())
+                .CheckMeasureResult(new Wrap());
+        }
+        
+        [Test]
+        public void Measure_PartialRender()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = 3,
+                    ScaleY = 2
+                })
+                .MeasureElement(new Size(900, 800))
+                .ExpectChildMeasure(new Size(300, 400), new PartialRender(200, 350))
+                .CheckMeasureResult(new PartialRender(600, 700));
+        }
+        
+        [Test]
+        public void Measure_FullRender()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = 3,
+                    ScaleY = 2
+                })
+                .MeasureElement(new Size(900, 800))
+                .ExpectChildMeasure(new Size(300, 400), new FullRender(250, 300))
+                .CheckMeasureResult(new FullRender(750, 600));
+        }
+        
+        [Test]
+        public void Measure_NegativeScaleX()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = -2,
+                    ScaleY = 1
+                })
+                .MeasureElement(new Size(800, 600))
+                .ExpectChildMeasure(new Size(400, 600), new FullRender(300, 500))
+                .CheckMeasureResult(new FullRender(600, 500));
+        }
+        
+        [Test]
+        public void Measure_NegativeScaleY()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = 1,
+                    ScaleY = -3
+                })
+                .MeasureElement(new Size(800, 600))
+                .ExpectChildMeasure(new Size(800, 200), new FullRender(800, 100))
+                .CheckMeasureResult(new FullRender(800, 300));
+        }
+        
+        #endregion
+        
+        #region draw
+        
+        [Test]
+        public void Draw_Simple()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = 3,
+                    ScaleY = 2
+                })
+                .DrawElement(new Size(900, 800))
+                .ExpectCanvasTranslate(0, 0)
+                .ExpectCanvasScale(3, 2)
+                .ExpectChildDraw(new Size(300, 400))
+                .ExpectCanvasScale(1/3f, 1/2f)
+                .ExpectCanvasTranslate(0, 0)
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_NegativeScaleX()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = -3,
+                    ScaleY = 2
+                })
+                .DrawElement(new Size(900, 800))
+                .ExpectCanvasTranslate(900, 0)
+                .ExpectCanvasScale(-3, 2)
+                .ExpectChildDraw(new Size(300, 400))
+                .ExpectCanvasScale(-1/3f, 1/2f)
+                .ExpectCanvasTranslate(-900, 0)
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_NegativeScaleY()
+        {
+            TestPlan
+                .For(x => new Scale
+                {
+                    Child = x.CreateChild(),
+                    ScaleX = 3,
+                    ScaleY = -2
+                })
+                .DrawElement(new Size(900, 800))
+                .ExpectCanvasTranslate(0, 800)
+                .ExpectCanvasScale(3, -2)
+                .ExpectChildDraw(new Size(300, 400))
+                .ExpectCanvasScale(1/3f, -1/2f)
+                .ExpectCanvasTranslate(0, -800)
+                .CheckDrawResult();
+        }
+        
+        #endregion
+    }
+}

+ 162 - 0
QuestPDF.UnitTests/SimpleRotateTests.cs

@@ -0,0 +1,162 @@
+using NUnit.Framework;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using QuestPDF.UnitTests.TestEngine;
+
+namespace QuestPDF.UnitTests
+{
+    [TestFixture]
+    public class SimpleRotateTests
+    {
+        #region measure
+        
+        [Test]
+        public void Measure_Wrap()
+        {
+            TestPlan
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 0
+                })
+                .MeasureElement(new Size(400, 300))
+                .ExpectChildMeasure(new Size(400, 300), new Wrap())
+                .CheckMeasureResult(new Wrap());
+        }
+        
+        [Test]
+        public void Measure_PartialRender()
+        {
+            TestPlan
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 0
+                })
+                .MeasureElement(new Size(400, 300))
+                .ExpectChildMeasure(new Size(400, 300), new PartialRender(300, 200))
+                .CheckMeasureResult(new PartialRender(300, 200));
+        }
+        
+        [Test]
+        public void Measure_RotateRight()
+        {
+            TestPlan
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 1
+                })
+                .MeasureElement(new Size(400, 300))
+                .ExpectChildMeasure(new Size(300, 400), new FullRender(200, 300))
+                .CheckMeasureResult(new FullRender(300, 200));
+        }
+        
+        [Test]
+        public void Measure_RotateFlip()
+        {
+            TestPlan
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 2
+                })
+                .MeasureElement(new Size(400, 300))
+                .ExpectChildMeasure(new Size(400, 300), new FullRender(200, 100))
+                .CheckMeasureResult(new FullRender(200, 100));
+        }
+        
+        [Test]
+        public void Measure_RotateLeft()
+        {
+            TestPlan
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 3 // or -1
+                })
+                .MeasureElement(new Size(500, 400))
+                .ExpectChildMeasure(new Size(400, 500), new FullRender(300, 350))
+                .CheckMeasureResult(new FullRender(350, 300));
+        }
+        
+        #endregion
+        
+        #region draw
+        
+        [Test]
+        public void Draw_Simple()
+        {
+            TestPlan
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 0
+                })
+                .DrawElement(new Size(640, 480))
+                .ExpectCanvasTranslate(0, 0)
+                .ExpectCanvasRotate(0)
+                .ExpectChildDraw(new Size(640, 480))
+                .ExpectCanvasRotate(0)
+                .ExpectCanvasTranslate(0, 0)
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_RotateRight()
+        {
+            TestPlan 
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 1
+                })
+                .DrawElement(new Size(640, 480))
+                .ExpectCanvasTranslate(640, 0)
+                .ExpectCanvasRotate(90)
+                .ExpectChildDraw(new Size(480, 640))
+                .ExpectCanvasRotate(-90)
+                .ExpectCanvasTranslate(-640, 0)
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_RotateFlip()
+        {
+            TestPlan 
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 2
+                })
+                .DrawElement(new Size(640, 480))
+                .ExpectCanvasTranslate(640, 480)
+                .ExpectCanvasRotate(180)
+                .ExpectChildDraw(new Size(640, 480))
+                .ExpectCanvasRotate(-180)
+                .ExpectCanvasTranslate(-640, -480)
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_RotateLeft()
+        {
+            TestPlan 
+                .For(x => new SimpleRotate
+                {
+                    Child = x.CreateChild(),
+                    TurnCount = 3 // or -1
+                })
+                .DrawElement(new Size(640, 480))
+                .ExpectCanvasTranslate(0, 480)
+                .ExpectCanvasRotate(270)
+                .ExpectChildDraw(new Size(480, 640))
+                .ExpectCanvasRotate(-270)
+                .ExpectCanvasTranslate(0, -480)
+                .CheckDrawResult();
+        }
+        
+        #endregion
+    }
+}

+ 5 - 0
QuestPDF.UnitTests/TestEngine/CanvasMock.cs

@@ -7,11 +7,16 @@ namespace QuestPDF.UnitTests.TestEngine
     internal class CanvasMock : ICanvas
     internal class CanvasMock : ICanvas
     {
     {
         public Action<Position> TranslateFunc { get; set; }
         public Action<Position> TranslateFunc { get; set; }
+        public Action<float> RotateFunc { get; set; }
+        public Action<float, float> ScaleFunc { get; set; }
         public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
         public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
         public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
         public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
         public Action<Position, Size, string> DrawRectFunc { get; set; }
         public Action<Position, Size, string> DrawRectFunc { get; set; }
 
 
         public void Translate(Position vector) => TranslateFunc(vector);
         public void Translate(Position vector) => TranslateFunc(vector);
+        public void Rotate(float angle) => RotateFunc(angle);
+        public void Scale(float scaleX, float scaleY) => ScaleFunc(scaleX, scaleY);
+
         public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
         public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
         public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
         public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
         public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
         public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);

+ 12 - 0
QuestPDF.UnitTests/TestEngine/Operations/CanvasRotateOperation.cs

@@ -0,0 +1,12 @@
+namespace QuestPDF.UnitTests.TestEngine.Operations
+{
+    public class CanvasRotateOperation : OperationBase
+    {
+        public float Angle { get; }
+
+        public CanvasRotateOperation(float angle)
+        {
+            Angle = angle;
+        }
+    }
+}

+ 14 - 0
QuestPDF.UnitTests/TestEngine/Operations/CanvasScaleOperation.cs

@@ -0,0 +1,14 @@
+namespace QuestPDF.UnitTests.TestEngine.Operations
+{
+    public class CanvasScaleOperation : OperationBase
+    {
+        public float ScaleX { get; }
+        public float ScaleY { get; }
+
+        public CanvasScaleOperation(float scaleX, float scaleY)
+        {
+            ScaleX = scaleX;
+            ScaleY = scaleY;
+        }
+    }
+}

+ 27 - 0
QuestPDF.UnitTests/TestEngine/TestPlan.cs

@@ -55,6 +55,23 @@ namespace QuestPDF.UnitTests.TestEngine
                     
                     
                     //position.Should().BeEquivalentTo(expected.Position);
                     //position.Should().BeEquivalentTo(expected.Position);
                 },
                 },
+                RotateFunc = angle =>
+                {
+                    var expected = GetExpected<CanvasRotateOperation>();
+
+                    Assert.AreEqual(expected.Angle, angle, "Rotate angle");
+                    
+                    //position.Should().BeEquivalentTo(expected.Position);
+                },
+                ScaleFunc = (scaleX, scaleY) =>
+                {
+                    var expected = GetExpected<CanvasScaleOperation>();
+
+                    Assert.AreEqual(expected.ScaleX, scaleX, "Scale X");
+                    Assert.AreEqual(expected.ScaleY, scaleY, "Scale Y");
+                    
+                    //position.Should().BeEquivalentTo(expected.Position);
+                },
                 DrawRectFunc = (position, size, color) =>
                 DrawRectFunc = (position, size, color) =>
                 {
                 {
                     var expected = GetExpected<CanvasDrawRectangleOperationBase>();
                     var expected = GetExpected<CanvasDrawRectangleOperationBase>();
@@ -188,6 +205,16 @@ namespace QuestPDF.UnitTests.TestEngine
             return AddOperation(new CanvasTranslateOperationBase(new Position(left, top)));
             return AddOperation(new CanvasTranslateOperationBase(new Position(left, top)));
         }
         }
 
 
+        public TestPlan ExpectCanvasScale(float scaleX, float scaleY)
+        {
+            return AddOperation(new CanvasScaleOperation(scaleX, scaleY));
+        }
+        
+        public TestPlan ExpectCanvasRotate(float angle)
+        {
+            return AddOperation(new CanvasRotateOperation(angle));
+        }
+        
         public TestPlan ExpectCanvasDrawRectangle(Position position, Size size, string color)
         public TestPlan ExpectCanvasDrawRectangle(Position position, Size size, string color)
         {
         {
             return AddOperation(new CanvasDrawRectangleOperationBase(position, size, color));
             return AddOperation(new CanvasDrawRectangleOperationBase(position, size, color));

+ 29 - 0
QuestPDF.UnitTests/TranslateTests.cs

@@ -0,0 +1,29 @@
+using NUnit.Framework;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using QuestPDF.UnitTests.TestEngine;
+
+namespace QuestPDF.UnitTests
+{
+    [TestFixture]
+    public class TranslateTests
+    {
+        [Test]
+        public void Draw()
+        {
+            TestPlan
+                .For(x => new Translate
+                {
+                    Child = x.CreateChild(),
+                    TranslateX = 50,
+                    TranslateY = 75
+                })
+                .DrawElement(new Size(400, 300))
+                .ExpectCanvasTranslate(50, 75)
+                .ExpectChildDraw(new Size(400, 300))
+                .ExpectCanvasTranslate(-50, -75)
+                .CheckDrawResult();
+        }
+    }
+}

+ 100 - 0
QuestPDF.UnitTests/UnconstrainedTests.cs

@@ -0,0 +1,100 @@
+using NUnit.Framework;
+using QuestPDF.Drawing.SpacePlan;
+using QuestPDF.Elements;
+using QuestPDF.Infrastructure;
+using QuestPDF.UnitTests.TestEngine;
+
+namespace QuestPDF.UnitTests
+{
+    [TestFixture]
+    public class UnconstrainedTests
+    {
+        #region measure
+        
+        [Test]
+        public void Measure_Wrap()
+        {
+            TestPlan
+                .For(x => new Unconstrained
+                {
+                    Child = x.CreateChild()
+                })
+                .MeasureElement(new Size(900, 800))
+                .ExpectChildMeasure(Size.Max, new Wrap())
+                .CheckMeasureResult(new Wrap());
+        }
+        
+        [Test]
+        public void Measure_PartialRender()
+        {
+            TestPlan
+                .For(x => new Unconstrained
+                {
+                    Child = x.CreateChild()
+                })
+                .MeasureElement(new Size(900, 800))
+                .ExpectChildMeasure(Size.Max, new PartialRender(1200, 1600))
+                .CheckMeasureResult(new PartialRender(Size.Zero));
+        }
+        
+        [Test]
+        public void Measure_FullRender()
+        {
+            TestPlan
+                .For(x => new Unconstrained
+                {
+                    Child = x.CreateChild()
+                })
+                .MeasureElement(new Size(900, 800))
+                .ExpectChildMeasure(Size.Max, new FullRender(1200, 1600))
+                .CheckMeasureResult(new FullRender(Size.Zero));
+        }
+        
+        #endregion
+        
+        #region draw
+        
+        [Test]
+        public void Draw_SkipWhenChildWraps()
+        {
+            TestPlan
+                .For(x => new Unconstrained
+                {
+                    Child = x.CreateChild()
+                })
+                .DrawElement(new Size(900, 800))
+                .ExpectChildMeasure(Size.Max, new Wrap())
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_WhenChildPartiallyRenders()
+        {
+            TestPlan
+                .For(x => new Unconstrained
+                {
+                    Child = x.CreateChild()
+                })
+                .DrawElement(new Size(900, 800))
+                .ExpectChildMeasure(Size.Max, new PartialRender(1200, 1600))
+                .ExpectChildDraw(new Size(1200, 1600))
+                .CheckDrawResult();
+        }
+        
+        [Test]
+        public void Draw_WhenChildFullyRenders()
+        {
+            TestPlan
+                .For(x => new Unconstrained
+                {
+                    Child = x.CreateChild()
+                })
+                .DrawElement(new Size(900, 800))
+                .ExpectChildMeasure(Size.Max, new FullRender(1600, 1000))
+                .ExpectChildDraw(new Size(1600, 1000))
+                .CheckDrawResult();
+        }
+        
+        #endregion
+    }
+}

+ 10 - 0
QuestPDF/Drawing/FreeCanvas.cs

@@ -66,6 +66,16 @@ namespace QuestPDF.Drawing
             
             
         }
         }
 
 
+        public void Rotate(float angle)
+        {
+            
+        }
+
+        public void Scale(float scaleX, float scaleY)
+        {
+            
+        }
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 10 - 0
QuestPDF/Drawing/SkiaCanvasBase.cs

@@ -54,5 +54,15 @@ namespace QuestPDF.Drawing
         {
         {
             Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
             Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
         }
         }
+
+        public void Rotate(float angle)
+        {
+            Canvas.RotateDegrees(angle);
+        }
+
+        public void Scale(float scaleX, float scaleY)
+        {
+            Canvas.Scale(scaleX, scaleY);
+        }
     }
     }
 }
 }

+ 2 - 9
QuestPDF/Elements/Rotate.cs

@@ -8,16 +8,9 @@ namespace QuestPDF.Elements
 
 
         internal override void Draw(Size availableSpace)
         internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
-            
-            if (skiaCanvas == null)
-                return;
-            
-            var currentMatrix = skiaCanvas.TotalMatrix;
-            
-            skiaCanvas.RotateDegrees(Angle);
+            Canvas.Rotate(Angle);
             Child?.Draw(availableSpace);
             Child?.Draw(availableSpace);
-            skiaCanvas.SetMatrix(currentMatrix);
+            Canvas.Rotate(-Angle);
         }
         }
     }
     }
 }
 }

+ 8 - 13
QuestPDF/Elements/Scale.cs

@@ -35,26 +35,21 @@ namespace QuestPDF.Elements
         
         
         internal override void Draw(Size availableSpace)
         internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
-            
-            if (skiaCanvas == null)
-                return;
-            
             var targetSpace = new Size(
             var targetSpace = new Size(
                 Math.Abs(availableSpace.Width / ScaleX), 
                 Math.Abs(availableSpace.Width / ScaleX), 
                 Math.Abs(availableSpace.Height / ScaleY));
                 Math.Abs(availableSpace.Height / ScaleY));
 
 
-            var currentMatrix = skiaCanvas.TotalMatrix;
-            
-            if (ScaleX < 0)
-                skiaCanvas.Translate(availableSpace.Width, 0);
+            var translate = new Position(
+                ScaleX < 0 ? availableSpace.Width : 0,
+                ScaleY < 0 ? availableSpace.Height : 0);
             
             
-            if (ScaleY < 0)
-                skiaCanvas.Translate(0, availableSpace.Height);
+            Canvas.Translate(translate);
+            Canvas.Scale(ScaleX, ScaleY);
             
             
-            skiaCanvas.Scale(ScaleX, ScaleY);
             Child?.Draw(targetSpace);
             Child?.Draw(targetSpace);
-            skiaCanvas.SetMatrix(currentMatrix);
+             
+            Canvas.Scale(1/ScaleX, 1/ScaleY);
+            Canvas.Translate(translate.Reverse());
         }
         }
     }
     }
 }
 }

+ 9 - 13
QuestPDF/Elements/SimpleRotate.cs

@@ -34,26 +34,22 @@ namespace QuestPDF.Elements
         
         
         internal override void Draw(Size availableSpace)
         internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
-            
-            if (skiaCanvas == null)
-                return;
-
-            var currentMatrix = skiaCanvas.TotalMatrix;
+            var translate = new Position(
+                (NormalizedTurnCount == 1 || NormalizedTurnCount == 2) ? availableSpace.Width : 0,
+                (NormalizedTurnCount == 2 || NormalizedTurnCount == 3) ? availableSpace.Height : 0);
 
 
-            if (NormalizedTurnCount == 1 || NormalizedTurnCount == 2)
-                skiaCanvas.Translate(availableSpace.Width, 0);
+            var rotate = NormalizedTurnCount * 90;
             
             
-            if (NormalizedTurnCount == 2  || NormalizedTurnCount == 3)
-                skiaCanvas.Translate(0, availableSpace.Height);
-
-            skiaCanvas.RotateDegrees(NormalizedTurnCount * 90);
+            Canvas.Translate(translate);
+            Canvas.Rotate(rotate);
             
             
             if (NormalizedTurnCount == 1 || NormalizedTurnCount == 3)
             if (NormalizedTurnCount == 1 || NormalizedTurnCount == 3)
                 availableSpace = new Size(availableSpace.Height, availableSpace.Width);
                 availableSpace = new Size(availableSpace.Height, availableSpace.Width);
             
             
             Child?.Draw(availableSpace);
             Child?.Draw(availableSpace);
-            skiaCanvas.SetMatrix(currentMatrix);
+            
+            Canvas.Rotate(-rotate);
+            Canvas.Translate(translate.Reverse());
         }
         }
     }
     }
 }
 }

+ 3 - 6
QuestPDF/Elements/Translate.cs

@@ -10,14 +10,11 @@ namespace QuestPDF.Elements
 
 
         internal override void Draw(Size availableSpace)
         internal override void Draw(Size availableSpace)
         {
         {
-            var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
+            var translate = new Position(TranslateX, TranslateY);
             
             
-            if (skiaCanvas == null)
-                return;
-            
-            skiaCanvas.Translate(TranslateX, TranslateY);
+            Canvas.Translate(translate);
             base.Draw(availableSpace);
             base.Draw(availableSpace);
-            skiaCanvas.Translate(-TranslateX, -TranslateY);
+            Canvas.Translate(translate.Reverse());
         }
         }
     }
     }
 }
 }

+ 3 - 0
QuestPDF/Infrastructure/ICanvas.cs

@@ -14,5 +14,8 @@ namespace QuestPDF.Infrastructure
         void DrawExternalLink(string url, Size size);
         void DrawExternalLink(string url, Size size);
         void DrawLocationLink(string locationName, Size size);
         void DrawLocationLink(string locationName, Size size);
         void DrawLocation(string locationName);
         void DrawLocation(string locationName);
+        
+        void Rotate(float angle);
+        void Scale(float scaleX, float scaleY);
     }
     }
 }
 }