Browse Source

Implemented ScaleToFit element

Marcin Ziąbek 4 years ago
parent
commit
ba8b35b1c0

+ 42 - 0
QuestPDF.Examples/ScaleToFitExamples.cs

@@ -0,0 +1,42 @@
+using System.Linq;
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class ScaleToFitExamples
+    {
+        [Test]
+        public void ScaleToFit()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(PageSizes.A4)
+                .ProducePdf()
+                .ShowResults()
+                .Render(container =>
+                {
+                    container.Padding(25).Stack(stack =>
+                    {
+                        var text = Placeholders.Paragraph();
+
+                        foreach (var i in Enumerable.Range(0, 16))
+                        {
+                            stack
+                                .Item()
+                                .MinimalBox()
+                                .Border(1)
+                                .Padding(5)
+                                .Width(50 + i * 25)
+                                .Height(25 + i * 5)
+                                .ScaleToFit()
+                                .Text(text);
+                        }
+                    });
+                });
+        }
+    }
+}

+ 78 - 0
QuestPDF/Elements/ScaleToFit.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Linq;
+using QuestPDF.Drawing;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements
+{
+    internal class ScaleToFit : ContainerElement
+    {
+        internal override SpacePlan Measure(Size availableSpace)
+        {
+            if (Child == null)
+                return SpacePlan.FullRender(Size.Zero);
+
+            var perfectScale = FindPerfectScale(Child, availableSpace);
+
+            if (perfectScale == null)
+                return SpacePlan.Wrap();
+
+            var targetSpace = ScaleSize(availableSpace, perfectScale.Value);
+            return SpacePlan.FullRender(targetSpace);
+        }
+        
+        internal override void Draw(Size availableSpace)
+        {
+            var perfectScale = FindPerfectScale(Child, availableSpace);
+            
+            if (!perfectScale.HasValue)
+                return;
+
+            var targetScale = perfectScale.Value;
+            var targetSpace = ScaleSize(availableSpace, 1 / targetScale);
+            
+            Canvas.Scale(targetScale, targetScale);
+            Child?.Draw(targetSpace);
+            Canvas.Scale(1 / targetScale, 1 / targetScale);
+        }
+
+        private static Size ScaleSize(Size size, float factor)
+        {
+            return new Size(size.Width * factor, size.Height * factor);
+        }
+        
+        private static float? FindPerfectScale(Element child, Size availableSpace)
+        {
+            if (ChildFits(1))
+                return 1;
+            
+            var maxScale = 1f;
+            var minScale = Size.Epsilon;
+
+            var lastWorkingScale = (float?)null;
+            
+            foreach (var _ in Enumerable.Range(0, 8))
+            {
+                var halfScale = (maxScale + minScale) / 2;
+
+                if (ChildFits(halfScale))
+                {
+                    minScale = halfScale;
+                    lastWorkingScale = halfScale;
+                }
+                else
+                {
+                    maxScale = halfScale;
+                }
+            }
+            
+            return lastWorkingScale;
+            
+            bool ChildFits(float scale)
+            {
+                var scaledSpace = ScaleSize(availableSpace, 1 / scale);
+                return child.Measure(scaledSpace).Type == SpacePlanType.FullRender;
+            }
+        }
+    }
+}

+ 5 - 0
QuestPDF/Fluent/ElementExtensions.cs

@@ -165,5 +165,10 @@ namespace QuestPDF.Fluent
         {
         {
             return element.Element(new PreventPaging());
             return element.Element(new PreventPaging());
         }
         }
+        
+        public static IContainer ScaleToFit(this IContainer element)
+        {
+            return element.Element(new ScaleToFit());
+        }
     }
     }
 }
 }