Browse Source

Added scaling options for AspectRatio and Image component. Added Debug component to the fluent API

Marcin Ziąbek 4 years ago
parent
commit
5e9ff7ec1e

+ 2 - 2
QuestPDF.ReportSample/DataSource.cs

@@ -19,8 +19,8 @@ namespace QuestPDF.ReportSample
                 HeaderFields = HeaderFields(),
                 HeaderFields = HeaderFields(),
                 
                 
                 LogoData = Helpers.GetImage("logo.png"),
                 LogoData = Helpers.GetImage("logo.png"),
-                Sections = Enumerable.Range(0, 10).Select(x => GenerateSection()).ToList(),
-                Photos = Enumerable.Range(0, 10).Select(x => GetReportPhotos()).ToList()
+                Sections = Enumerable.Range(0, 8).Select(x => GenerateSection()).ToList(),
+                Photos = Enumerable.Range(0, 8).Select(x => GetReportPhotos()).ToList()
             };
             };
 
 
             List<ReportHeaderField> HeaderFields()
             List<ReportHeaderField> HeaderFields()

+ 4 - 4
QuestPDF.ReportSample/Layouts/SectionTemplate.cs

@@ -38,10 +38,10 @@ namespace QuestPDF.ReportSample.Layouts
                                     frame.Text(text.Text, Typography.Normal);
                                     frame.Text(text.Text, Typography.Normal);
                         
                         
                                 if (part is ReportSectionMap map)
                                 if (part is ReportSectionMap map)
-                                    frame.Element(container => MapElement(container, map));
+                                    frame.Element(x => MapElement(x, map));
                         
                         
                                 if (part is ReportSectionPhotos photos)
                                 if (part is ReportSectionPhotos photos)
-                                    frame.Element(container => PhotosElement(container, photos));
+                                    frame.Element(x => PhotosElement(x, photos));
                             });
                             });
                         }
                         }
                     });
                     });
@@ -56,7 +56,7 @@ namespace QuestPDF.ReportSample.Layouts
                 return;
                 return;
             }
             }
 
 
-            container.Stack(stack =>
+            container.PageableStack(stack =>
             {
             {
                 stack.Spacing(5);
                 stack.Spacing(5);
                 
                 
@@ -75,7 +75,7 @@ namespace QuestPDF.ReportSample.Layouts
 
 
             var rowCount = (int) Math.Ceiling(model.Photos.Count / 3f);
             var rowCount = (int) Math.Ceiling(model.Photos.Count / 3f);
 
 
-            container.Padding(-2).Stack(stack =>
+            container.Debug().Padding(-2).PageableStack(stack =>
             {
             {
                 foreach (var rowId in Enumerable.Range(0, rowCount))
                 foreach (var rowId in Enumerable.Range(0, rowCount))
                 {
                 {

+ 1 - 1
QuestPDF.ReportSample/Tests.cs

@@ -27,7 +27,7 @@ namespace QuestPDF.ReportSample
         public void PerformanceBenchmark()
         public void PerformanceBenchmark()
         {
         {
             // test size
             // test size
-            const int testSize = 1000;
+            const int testSize = 250;
             const decimal performanceTarget = 5; // documents per second
             const decimal performanceTarget = 5; // documents per second
 
 
             // create report models
             // create report models

+ 2 - 2
QuestPDF.ReportSample/Typography.cs

@@ -5,8 +5,8 @@ namespace QuestPDF.ReportSample
 {
 {
     public static class Typography
     public static class Typography
     {
     {
-        public static TextStyle Title => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(20).Bold();
-        public static TextStyle Headline => TextStyle.Default.FontType("Helvetica").Color("#047AED").Size(14);
+        public static TextStyle Title => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(22).SemiBold();
+        public static TextStyle Headline => TextStyle.Default.FontType("Helvetica").Color("#047AED").Size(14).SemiBold();
         public static TextStyle Normal => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(10).LineHeight(1.25f).AlignLeft();
         public static TextStyle Normal => TextStyle.Default.FontType("Helvetica").Color("#000000").Size(10).LineHeight(1.25f).AlignLeft();
     }
     }
 }
 }

+ 1 - 1
QuestPDF.UnitTests/ImageTests.cs

@@ -15,7 +15,7 @@ namespace QuestPDF.UnitTests
             {
             {
                 var image = new Image()
                 var image = new Image()
                 {
                 {
-                    Data = null
+                    InternalImage = null
                 };
                 };
                 
                 
                 image.Draw(It.IsAny<ICanvas>(), Size.Zero);
                 image.Draw(It.IsAny<ICanvas>(), Size.Zero);

+ 20 - 4
QuestPDF/Elements/AspectRatio.cs

@@ -1,3 +1,4 @@
+using System;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
 
 
@@ -6,17 +7,21 @@ namespace QuestPDF.Elements
     internal class AspectRatio : ContainerElement
     internal class AspectRatio : ContainerElement
     {
     {
         public float Ratio { get; set; } = 1;
         public float Ratio { get; set; } = 1;
+        public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth;
         
         
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
             if(Child == null)
             if(Child == null)
                 return new FullRender(Size.Zero);
                 return new FullRender(Size.Zero);
             
             
-            var size = GetSize(availableSpace);
+            var size = GetTargetSize(availableSpace);
             
             
             if (size.Height > availableSpace.Height + Size.Epsilon)
             if (size.Height > availableSpace.Height + Size.Epsilon)
                 return new Wrap();
                 return new Wrap();
             
             
+            if (size.Width > availableSpace.Width + Size.Epsilon)
+                return new Wrap();
+            
             return new FullRender(size);
             return new FullRender(size);
         }
         }
 
 
@@ -25,7 +30,7 @@ namespace QuestPDF.Elements
             if (Child == null)
             if (Child == null)
                 return;
                 return;
             
             
-            var size = GetSize(availableSpace);
+            var size = GetTargetSize(availableSpace);
             
             
             if (size.Height > availableSpace.Height)
             if (size.Height > availableSpace.Height)
                 return;
                 return;
@@ -33,9 +38,20 @@ namespace QuestPDF.Elements
             Child.Draw(canvas, size);
             Child.Draw(canvas, size);
         }
         }
         
         
-        private Size GetSize(Size availableSpace)
+        private Size GetTargetSize(Size availableSpace)
         {
         {
-            return new Size(availableSpace.Width, (int)(availableSpace.Width / Ratio));
+            var spaceRatio = availableSpace.Width / availableSpace.Height;
+
+            var fitHeight = new Size(availableSpace.Height * Ratio, availableSpace.Height) ;
+            var fitWidth = new Size(availableSpace.Width, availableSpace.Width / Ratio);
+
+            return Option switch
+            {
+                AspectRatioOption.FitWidth => fitWidth,
+                AspectRatioOption.FitHeight => fitHeight,
+                AspectRatioOption.FitArea => Ratio < spaceRatio ? fitHeight : fitWidth,
+                _ => throw new ArgumentOutOfRangeException()
+            };
         }
         }
     }
     }
 }
 }

+ 1 - 1
QuestPDF/Elements/Container.cs

@@ -6,7 +6,7 @@ namespace QuestPDF.Elements
     {
     {
         internal Container()
         internal Container()
         {
         {
-            Child = new Empty();
+            
         }
         }
     }
     }
 }
 }

+ 24 - 14
QuestPDF/Elements/Debug.cs

@@ -1,25 +1,35 @@
-using QuestPDF.Infrastructure;
+using QuestPDF.Fluent;
+using QuestPDF.Infrastructure;
 
 
 namespace QuestPDF.Elements
 namespace QuestPDF.Elements
 {
 {
     internal class Debug : ContainerElement
     internal class Debug : ContainerElement
     {
     {
+        private static readonly TextStyle TextStyle = TextStyle.Default.Color("#FF0000").FontType("Consolas").Size(10);
+        
         internal override void Draw(ICanvas canvas, Size availableSpace)
         internal override void Draw(ICanvas canvas, Size availableSpace)
         {
         {
-            var textStyle = new TextStyle
-            {
-                Color = "#FF0000",
-                FontType = "Consolas",
-                Size = 10
-            };
-            
             Child?.Draw(canvas, availableSpace);
             Child?.Draw(canvas, availableSpace);
-            
-            canvas.DrawRectangle(Position.Zero, availableSpace, "#FF0000");
-            canvas.DrawRectangle(Position.Zero, availableSpace, "#FF00FF");
-            
-            canvas.DrawText($"W: {availableSpace.Width}", new Position(5, 12), textStyle);
-            canvas.DrawText($"H: {availableSpace.Height}", new Position(5, 22), textStyle);
+            DrawBoundingBox();
+            DrawDimensions();
+                
+            void DrawBoundingBox()
+            {
+                var container = new Container();
+
+                container
+                    .Border(1)
+                    .BorderColor("#FF0000")
+                    .Background("#33FF0000");
+
+                container.Draw(canvas, availableSpace);
+            }
+
+            void DrawDimensions()
+            {
+                canvas.DrawText($"W: {availableSpace.Width:F1}", new Position(5, 12), TextStyle);
+                canvas.DrawText($"H: {availableSpace.Height:F1}", new Position(5, 22), TextStyle);
+            }
         }
         }
     }
     }
 }
 }

+ 8 - 2
QuestPDF/Elements/DynamicImage.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Drawing.SpacePlan;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
+using SkiaSharp;
 
 
 namespace QuestPDF.Elements
 namespace QuestPDF.Elements
 {
 {
@@ -18,9 +19,14 @@ namespace QuestPDF.Elements
 
 
         internal override void Draw(ICanvas canvas, Size availableSpace)
         internal override void Draw(ICanvas canvas, Size availableSpace)
         {
         {
-            var imageElement = new Image()
+            var imageData = Source?.Invoke(availableSpace);
+            
+            if (imageData == null)
+                return;
+
+            var imageElement = new Image
             {
             {
-                Data = Source?.Invoke(availableSpace)
+                InternalImage = SKImage.FromEncodedData(imageData)
             };
             };
             
             
             imageElement.Draw(canvas, availableSpace);
             imageElement.Draw(canvas, availableSpace);

+ 3 - 32
QuestPDF/Elements/Image.cs

@@ -6,53 +6,24 @@ namespace QuestPDF.Elements
 {
 {
     internal class Image : Element
     internal class Image : Element
     {
     {
-        public byte[]? Data { get; set; }
-        private SKImage? InternalImage { get; set; }
+        public SKImage? InternalImage { get; set; }
 
 
         ~Image()
         ~Image()
         {
         {
             InternalImage?.Dispose();
             InternalImage?.Dispose();
         }
         }
-
-        private void Initialize()
-        {
-            if (Data != null)
-                InternalImage ??= SKImage.FromEncodedData(Data);
-        }
         
         
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {
-            Initialize();
-            
-            if (InternalImage == null)
-                return new FullRender(Size.Zero);
-            
-            if (availableSpace.Width < 0 || availableSpace.Height < 0)
-                return new Wrap();
-            
-            var size = GetTargetSize(availableSpace);
-            return new FullRender(size);
+            return new FullRender(availableSpace);
         }
         }
 
 
         internal override void Draw(ICanvas canvas, Size availableSpace)
         internal override void Draw(ICanvas canvas, Size availableSpace)
         {
         {
-            Initialize();
-            
             if (InternalImage == null)
             if (InternalImage == null)
                 return;
                 return;
 
 
-            var size = GetTargetSize(availableSpace);
-            canvas.DrawImage(InternalImage, Position.Zero, size);
-        }
-
-        private Size GetTargetSize(Size availableSpace)
-        {
-            var imageRatio = InternalImage.Width / (float)InternalImage.Height;
-            var spaceRatio = availableSpace.Width / (float) availableSpace.Height;
-
-            return imageRatio < spaceRatio 
-                ? new Size((int)(availableSpace.Height * imageRatio), availableSpace.Height) 
-                : new Size(availableSpace.Width, (int)(availableSpace.Width / imageRatio));
+            canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
         }
         }
     }
     }
 }
 }

+ 2 - 2
QuestPDF/Elements/Section.cs

@@ -6,8 +6,8 @@ namespace QuestPDF.Elements
 {
 {
     internal class Section : Element
     internal class Section : Element
     {
     {
-        public Element? Header { get; set; }
-        public Element? Content { get; set; }
+        public ContainerElement? Header { get; set; }
+        public ContainerElement? Content { get; set; }
 
 
         internal override ISpacePlan Measure(Size availableSpace)
         internal override ISpacePlan Measure(Size availableSpace)
         {
         {

+ 44 - 12
QuestPDF/Fluent/ElementExtensions.cs

@@ -4,6 +4,7 @@ using QuestPDF.Drawing;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Elements;
 using QuestPDF.Elements;
 using QuestPDF.Infrastructure;
 using QuestPDF.Infrastructure;
+using SkiaSharp;
 
 
 namespace QuestPDF.Fluent
 namespace QuestPDF.Fluent
 {
 {
@@ -33,21 +34,51 @@ namespace QuestPDF.Fluent
             return child;
             return child;
         }
         }
         
         
-        public static void Element(this IContainer element, Action<IContainer> handler)
+        public static void Element<TParent>(this TParent parent, Action<IContainer> handler) where TParent : IContainer
         {
         {
-            var container = new Container();
-            element.Element(container);
-            handler?.Invoke(container);
+            handler(parent.Container());
         }
         }
-
-        public static void Image(this IContainer element, byte[] data)
+        
+        public static IContainer Element<TParent>(this TParent parent, Func<IContainer, IContainer> handler) where TParent : IContainer
         {
         {
-            element.Element(new Image
-            {
-                Data = data
-            });
+            return handler(parent.Container()).Container();
         }
         }
         
         
+        public static IContainer Debug(this IContainer parent)
+        {
+            return parent.Element(new Debug());
+        }
+        
+        public static void Image(this IContainer parent, byte[] data, ImageScaling scaling = ImageScaling.FitWidth)
+        {
+            if (data == null)
+                return;
+            
+            var image = SKImage.FromEncodedData(data);
+            var aspectRatio = image.Width / (float)image.Height;
+            
+            var imageElement = new Image
+            {
+                InternalImage = image
+            };
+
+            if (scaling != ImageScaling.Resize)
+                parent = parent.AspectRatio(aspectRatio, Map(scaling));
+            
+            parent.Element(imageElement);
+
+            static AspectRatioOption Map(ImageScaling scaling)
+            {
+                return scaling switch
+                {
+                    ImageScaling.FitWidth => AspectRatioOption.FitWidth,
+                    ImageScaling.FitHeight => AspectRatioOption.FitHeight,
+                    ImageScaling.FitArea => AspectRatioOption.FitArea,
+                    _ => throw new ArgumentOutOfRangeException()
+                };
+            }
+        }
+
         public static void DynamicImage(this IContainer element, Func<Size, byte[]> imageSource)
         public static void DynamicImage(this IContainer element, Func<Size, byte[]> imageSource)
         {
         {
             element.Element(new DynamicImage
             element.Element(new DynamicImage
@@ -65,11 +96,12 @@ namespace QuestPDF.Fluent
             });
             });
         }
         }
         
         
-        public static IContainer AspectRatio(this IContainer element, float ratio)
+        public static IContainer AspectRatio(this IContainer element, float ratio, AspectRatioOption option = AspectRatioOption.FitWidth)
         {
         {
             return element.Element(new AspectRatio
             return element.Element(new AspectRatio
             {
             {
-                Ratio = ratio
+                Ratio = ratio,
+                Option = option
             });
             });
         }
         }
 
 

+ 9 - 0
QuestPDF/Infrastructure/AspectRatioOption.cs

@@ -0,0 +1,9 @@
+namespace QuestPDF.Infrastructure
+{
+    public enum AspectRatioOption
+    {
+        FitWidth,
+        FitHeight,
+        FitArea
+    }
+}

+ 10 - 0
QuestPDF/Infrastructure/ImageScaling.cs

@@ -0,0 +1,10 @@
+namespace QuestPDF.Infrastructure
+{
+    public enum ImageScaling
+    {
+        FitWidth,
+        FitHeight,
+        FitArea,
+        Resize
+    }
+}