Browse Source

Implemented the Inlined document

Marcin Ziąbek 4 years ago
parent
commit
9ef444462a
3 changed files with 154 additions and 87 deletions
  1. 54 18
      QuestPDF.Examples/InlinedExamples.cs
  2. 86 24
      QuestPDF/Elements/Inlined.cs
  3. 14 45
      QuestPDF/Fluent/InlinedExtensions.cs

+ 54 - 18
QuestPDF.Examples/InlinedExamples.cs

@@ -4,6 +4,7 @@ using NUnit.Framework;
 using QuestPDF.Examples.Engine;
 using QuestPDF.Examples.Engine;
 using QuestPDF.Fluent;
 using QuestPDF.Fluent;
 using QuestPDF.Helpers;
 using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
 
 
 namespace QuestPDF.Examples
 namespace QuestPDF.Examples
 {
 {
@@ -14,7 +15,7 @@ namespace QuestPDF.Examples
         {
         {
             RenderingTest
             RenderingTest
                 .Create()
                 .Create()
-                .PageSize(800, 575)
+                .PageSize(800, 650)
                 .FileName()
                 .FileName()
                 .ProduceImages()
                 .ProduceImages()
                 .ShowResults()
                 .ShowResults()
@@ -22,26 +23,61 @@ namespace QuestPDF.Examples
                 {
                 {
                     container
                     container
                         .Padding(25)
                         .Padding(25)
-                        .Border(1)
-                        .Background(Colors.Grey.Lighten2)
-                        .Inlined(inlined =>
+                        .Decoration(decoration =>
                         {
                         {
-                            inlined.Spacing(25);
+                            decoration.Header().Text(text =>
+                            {
+                                text.DefaultTextStyle(TextStyle.Default.Size(20));
+                                
+                                text.CurrentPageNumber();
+                                text.Span(" / ");
+                                text.TotalPages();
+                            });
                             
                             
-                            inlined.AlignCenter();
-                            inlined.BaselineMiddle();
+                            decoration
+                                .Content()
+                                .PaddingTop(25)
+                                //.Box()
+                                .Border(1)
+                                .Background(Colors.Grey.Lighten2)
+                                .Inlined(inlined =>
+                                {
+                                    inlined.Spacing(25);
 
 
-                            var random = new Random();
-                            
-                            foreach (var _ in Enumerable.Range(0, 50))
-                            {
-                                inlined
-                                    .Item()
-                                    .Border(1)
-                                    .Width(random.Next(1, 5) * 25)
-                                    .Height(random.Next(1, 5) * 25)
-                                    .Background(Placeholders.BackgroundColor());
-                            }
+                                    inlined.AlignSpaceAround();
+                                    inlined.BaselineMiddle();
+
+                                    var random = new Random(123);
+
+                                    foreach (var _ in Enumerable.Range(0, 50))
+                                    {
+                                        var width = random.Next(2, 7);
+                                        var height = random.Next(2, 7);
+
+                                        var sizeText = $"{width}×{height}";
+                                        
+                                        inlined
+                                            .Item()
+                                            .Border(1)
+                                            .Width(width * 25)
+                                            .Height(height * 25)
+                                            .Background(Placeholders.BackgroundColor())
+                                            .Layers(layers =>
+                                            {
+                                                layers.Layer().Grid(grid =>
+                                                {
+                                                    grid.Columns(width);
+                                                    Enumerable.Range(0, width * height).ToList().ForEach(x => grid.Item().Border(1).BorderColor(Colors.White).Width(25).Height(25));
+                                                });
+                                                
+                                                layers
+                                                    .PrimaryLayer()
+                                                    .AlignCenter()
+                                                    .AlignMiddle()
+                                                    .Text(sizeText, TextStyle.Default.Size(15));
+                                            });
+                                    }
+                                });
                         });
                         });
                 });
                 });
         }
         }

+ 86 - 24
QuestPDF/Elements/Inlined.cs

@@ -8,7 +8,24 @@ namespace QuestPDF.Elements
 {
 {
     internal class InlinedElement : Container
     internal class InlinedElement : Container
     {
     {
-        public ISpacePlan? Size { get; set; }
+        public ISpacePlan? MeasureCache { get; set; }
+
+        internal override ISpacePlan Measure(Size availableSpace)
+        {
+            // TODO: once element caching proxy is introduces, this can be removed
+            
+            MeasureCache ??= Child.Measure(Size.Max);
+            return MeasureCache;
+        }
+    }
+
+    internal enum InlinedAlignment
+    {
+        Left,
+        Center,
+        Right,
+        Justify,
+        SpaceAround
     }
     }
     
     
     internal class Inlined : Element, IStateResettable
     internal class Inlined : Element, IStateResettable
@@ -16,12 +33,14 @@ namespace QuestPDF.Elements
         public List<InlinedElement> Elements { get; internal set; } = new List<InlinedElement>();
         public List<InlinedElement> Elements { get; internal set; } = new List<InlinedElement>();
         private Queue<InlinedElement> ChildrenQueue { get; set; }
         private Queue<InlinedElement> ChildrenQueue { get; set; }
 
 
-        internal HorizontalAlignment HorizontalAlignment { get; set; }
+        internal float VerticalSpacing { get; set; }
+        internal float HorizontalSpacing { get; set; }
+        
+        internal InlinedAlignment ElementsAlignment { get; set; }
         internal VerticalAlignment BaselineAlignment { get; set; }
         internal VerticalAlignment BaselineAlignment { get; set; }
         
         
         public void ResetState()
         public void ResetState()
         {
         {
-            Elements.ForEach(x => x.Size ??= x.Measure(Size.Max));
             ChildrenQueue = new Queue<InlinedElement>(Elements);
             ChildrenQueue = new Queue<InlinedElement>(Elements);
         }
         }
         
         
@@ -41,10 +60,17 @@ namespace QuestPDF.Elements
             if (!lines.Any())
             if (!lines.Any())
                 return new Wrap();
                 return new Wrap();
 
 
-            var lineSizes = lines.Select(GetLineSize).ToList();
+            var lineSizes = lines
+                .Select(line =>
+                {
+                    var size = GetLineSize(line);
+                    var heightWithSpacing = size.Height + (line.Count - 1) * HorizontalSpacing;
+                    return new Size(size.Width, heightWithSpacing);
+                })
+                .ToList();
             
             
             var width = lineSizes.Max(x => x.Width);
             var width = lineSizes.Max(x => x.Width);
-            var height = lineSizes.Sum(x => x.Height);
+            var height = lineSizes.Sum(x => x.Height) + (lines.Count - 1) * VerticalSpacing;
             var targetSize = new Size(width, height);
             var targetSize = new Size(width, height);
 
 
             var isPartiallyRendered = lines.Sum(x => x.Count) != ChildrenQueue.Count;
             var isPartiallyRendered = lines.Sum(x => x.Count) != ChildrenQueue.Count;
@@ -62,11 +88,11 @@ namespace QuestPDF.Elements
             
             
             foreach (var line in lines)
             foreach (var line in lines)
             {
             {
-                var height = line.Select(x => x.Size as Size).Where(x => x != null).Max(x => x.Height);
+                var height = line.Select(x => x.Measure(Size.Max) as Size).Where(x => x != null).Max(x => x.Height);
                 DrawLine(line);
                 DrawLine(line);
 
 
-                topOffset += height;
-                Canvas.Translate(new Position(0, height));
+                topOffset += height + VerticalSpacing;
+                Canvas.Translate(new Position(0, height + VerticalSpacing));
             }
             }
             
             
             Canvas.Translate(new Position(0, -topOffset));
             Canvas.Translate(new Position(0, -topOffset));
@@ -75,36 +101,62 @@ namespace QuestPDF.Elements
             void DrawLine(ICollection<InlinedElement> elements)
             void DrawLine(ICollection<InlinedElement> elements)
             {
             {
                 var lineSize = GetLineSize(elements);
                 var lineSize = GetLineSize(elements);
-                
+
+                var elementOffset = ElementOffset();
                 var leftOffset = AlignOffset();
                 var leftOffset = AlignOffset();
                 Canvas.Translate(new Position(leftOffset, 0));
                 Canvas.Translate(new Position(leftOffset, 0));
                 
                 
                 foreach (var element in elements)
                 foreach (var element in elements)
                 {
                 {
-                    var size = element.Size as Size;
+                    var size = element.Measure(Size.Max) as Size;
                     var baselineOffset = BaselineOffset(size, lineSize.Height);
                     var baselineOffset = BaselineOffset(size, lineSize.Height);
                     
                     
                     Canvas.Translate(new Position(0, baselineOffset));
                     Canvas.Translate(new Position(0, baselineOffset));
                     element.Draw(size);
                     element.Draw(size);
                     Canvas.Translate(new Position(0, -baselineOffset));
                     Canvas.Translate(new Position(0, -baselineOffset));
 
 
-                    leftOffset += size.Width;
-                    Canvas.Translate(new Position(size.Width, 0));
+                    leftOffset += size.Width + elementOffset;
+                    Canvas.Translate(new Position(size.Width + elementOffset, 0));
                 }
                 }
                 
                 
                 Canvas.Translate(new Position(-leftOffset, 0));
                 Canvas.Translate(new Position(-leftOffset, 0));
 
 
+                float ElementOffset()
+                {
+                    var difference = availableSpace.Width - lineSize.Width;
+
+                    if (elements.Count == 1)
+                        return 0;
+
+                    if (ElementsAlignment == InlinedAlignment.Justify)
+                        return difference / (elements.Count - 1);
+                    
+                    if (ElementsAlignment == InlinedAlignment.SpaceAround)
+                        return difference / (elements.Count + 1);
+                    
+                    return HorizontalSpacing;
+                }
+
                 float AlignOffset()
                 float AlignOffset()
                 {
                 {
-                    if (HorizontalAlignment == HorizontalAlignment.Left)
+                    if (ElementsAlignment == InlinedAlignment.Left)
+                        return 0;
+                    
+                    if (ElementsAlignment == InlinedAlignment.Justify)
                         return 0;
                         return 0;
+                    
+                    if (ElementsAlignment == InlinedAlignment.SpaceAround)
+                        return elementOffset;
 
 
-                    var difference = availableSpace.Width - lineSize.Width;
+                    var difference = availableSpace.Width - lineSize.Width - (elements.Count - 1) * HorizontalSpacing;
                     
                     
-                    if (HorizontalAlignment == HorizontalAlignment.Center)
+                    if (ElementsAlignment == InlinedAlignment.Center)
                         return difference / 2;
                         return difference / 2;
 
 
-                    return difference;
+                    if (ElementsAlignment == InlinedAlignment.Right)
+                        return difference;
+
+                    return 0;
                 }
                 }
                 
                 
                 float BaselineOffset(Size elementSize, float lineHeight)
                 float BaselineOffset(Size elementSize, float lineHeight)
@@ -125,7 +177,7 @@ namespace QuestPDF.Elements
         Size GetLineSize(ICollection<InlinedElement> elements)
         Size GetLineSize(ICollection<InlinedElement> elements)
         {
         {
             var sizes = elements
             var sizes = elements
-                .Select(x => x.Size as Size)
+                .Select(x => x.Measure(Size.Max) as Size)
                 .Where(x => x != null)
                 .Where(x => x != null)
                 .ToList();
                 .ToList();
             
             
@@ -151,14 +203,14 @@ namespace QuestPDF.Elements
                     break;
                     break;
 
 
                 var height = line
                 var height = line
-                    .Select(x => x.Size as Size)
+                    .Select(x => x.Measure(availableSize) as Size)
                     .Where(x => x != null)
                     .Where(x => x != null)
                     .Max(x => x.Height);
                     .Max(x => x.Height);
                 
                 
-                if (topOffset + height > availableSize.Height)
+                if (topOffset + height > availableSize.Height + Size.Epsilon)
                     break;
                     break;
 
 
-                topOffset += height;
+                topOffset += height + VerticalSpacing;
                 result.Add(line);
                 result.Add(line);
             }
             }
 
 
@@ -167,7 +219,7 @@ namespace QuestPDF.Elements
             ICollection<InlinedElement> GetNextLine()
             ICollection<InlinedElement> GetNextLine()
             {
             {
                 var result = new List<InlinedElement>();
                 var result = new List<InlinedElement>();
-                var leftOffset = 0f;
+                var leftOffset = GetInitialAlignmentOffset();
                 
                 
                 while (true)
                 while (true)
                 {
                 {
@@ -175,21 +227,31 @@ namespace QuestPDF.Elements
                         break;
                         break;
                     
                     
                     var element = queue.Peek();
                     var element = queue.Peek();
-                    var size = element.Size as Size;
+                    var size = element.Measure(Size.Max) as Size;
                     
                     
                     if (size == null)
                     if (size == null)
                         break;
                         break;
                     
                     
-                    if (leftOffset + size.Width > availableSize.Width)
+                    if (leftOffset + size.Width > availableSize.Width + Size.Epsilon)
                         break;
                         break;
 
 
                     queue.Dequeue();
                     queue.Dequeue();
-                    leftOffset += size.Width;
+                    leftOffset += size.Width + HorizontalSpacing;
                     result.Add(element);    
                     result.Add(element);    
                 }
                 }
 
 
                 return result;
                 return result;
             }
             }
+
+            float GetInitialAlignmentOffset()
+            {
+                // this method makes sure that the spacing between elements is no lesser than configured
+                
+                if (ElementsAlignment == InlinedAlignment.SpaceAround)
+                    return HorizontalSpacing * 2;
+
+                return 0;
+            }
         }
         }
     }
     }
 }
 }

+ 14 - 45
QuestPDF/Fluent/InlinedExtensions.cs

@@ -8,11 +8,7 @@ namespace QuestPDF.Fluent
 {
 {
     public class InlinedDescriptor
     public class InlinedDescriptor
     {
     {
-        private ICollection<Element> Children = new List<Element>();
-        private float VerticalSpacingValue { get; set; }
-        private VerticalAlignment BaselineAlignmentValue { get; set; }
-        private float HorizontalSpacingValue { get; set; }
-        private HorizontalAlignment HorizontalAlignmentValue { get; set; }
+        internal Inlined Inlined { get; } = new Inlined();
         
         
         public void Spacing(float value)
         public void Spacing(float value)
         {
         {
@@ -20,52 +16,25 @@ namespace QuestPDF.Fluent
             HorizontalSpacing(value);
             HorizontalSpacing(value);
         }
         }
         
         
-        public void VerticalSpacing(float value) => VerticalSpacingValue = value;
-        public void HorizontalSpacing(float value) => HorizontalSpacingValue = value;
+        public void VerticalSpacing(float value) => Inlined.VerticalSpacing = value;
+        public void HorizontalSpacing(float value) => Inlined.HorizontalSpacing = value;
 
 
-        public void BaselineTop() => BaselineAlignmentValue = VerticalAlignment.Top;
-        public void BaselineMiddle() => BaselineAlignmentValue = VerticalAlignment.Middle;
-        public void BaselineBottom() => BaselineAlignmentValue = VerticalAlignment.Bottom;
+        public void BaselineTop() => Inlined.BaselineAlignment = VerticalAlignment.Top;
+        public void BaselineMiddle() => Inlined.BaselineAlignment = VerticalAlignment.Middle;
+        public void BaselineBottom() => Inlined.BaselineAlignment = VerticalAlignment.Bottom;
 
 
-        public void AlignLeft() => HorizontalAlignmentValue = HorizontalAlignment.Left;
-        public void AlignCenter() => HorizontalAlignmentValue = HorizontalAlignment.Center;
-        public void AlignRight() => HorizontalAlignmentValue = HorizontalAlignment.Right;
+        public void AlignLeft() => Inlined.ElementsAlignment = InlinedAlignment.Left;
+        public void AlignCenter() => Inlined.ElementsAlignment = InlinedAlignment.Center;
+        public void AlignRight() => Inlined.ElementsAlignment = InlinedAlignment.Right;
+        public void AlignJustify() => Inlined.ElementsAlignment = InlinedAlignment.Justify;
+        public void AlignSpaceAround() => Inlined.ElementsAlignment = InlinedAlignment.SpaceAround;
         
         
         public IContainer Item()
         public IContainer Item()
         {
         {
-            var container = new Container();
-            Children.Add(container);
+            var container = new InlinedElement();
+            Inlined.Elements.Add(container);
             return container;
             return container;
         }
         }
-
-        internal Element Compose()
-        {
-            var elements = Children
-                .Select(x => new InlinedElement
-                {
-                    Child = new Padding
-                    {
-                        Left = HorizontalSpacingValue,
-                        Top = VerticalSpacingValue,
-                        Child = x
-                    }
-                })
-                .ToList();
-            
-            return new Padding
-            {
-                Left = -HorizontalSpacingValue,
-                Top = -VerticalSpacingValue,
-                
-                Child = new Inlined
-                {
-                    Elements = elements,
-                    
-                    HorizontalAlignment = HorizontalAlignmentValue,
-                    BaselineAlignment = BaselineAlignmentValue
-                }
-            };
-        }
     }
     }
     
     
     public static class InlinedExtensions
     public static class InlinedExtensions
@@ -75,7 +44,7 @@ namespace QuestPDF.Fluent
             var descriptor = new InlinedDescriptor();
             var descriptor = new InlinedDescriptor();
             handler(descriptor);
             handler(descriptor);
             
             
-            element.Element(descriptor.Compose());
+            element.Element(descriptor.Inlined);
         }
         }
     }
     }
 }
 }