From 9ef444462aeaf2f8e8d18bf1ccd557480d5136be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zi=C4=85bek?= Date: Mon, 11 Oct 2021 22:48:44 +0200 Subject: [PATCH] Implemented the Inlined document --- QuestPDF.Examples/InlinedExamples.cs | 74 +++++++++++++----- QuestPDF/Elements/Inlined.cs | 112 +++++++++++++++++++++------ QuestPDF/Fluent/InlinedExtensions.cs | 59 ++++---------- 3 files changed, 156 insertions(+), 89 deletions(-) diff --git a/QuestPDF.Examples/InlinedExamples.cs b/QuestPDF.Examples/InlinedExamples.cs index a9e3a63..5b8f4a2 100644 --- a/QuestPDF.Examples/InlinedExamples.cs +++ b/QuestPDF.Examples/InlinedExamples.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using QuestPDF.Examples.Engine; using QuestPDF.Fluent; using QuestPDF.Helpers; +using QuestPDF.Infrastructure; namespace QuestPDF.Examples { @@ -14,7 +15,7 @@ namespace QuestPDF.Examples { RenderingTest .Create() - .PageSize(800, 575) + .PageSize(800, 650) .FileName() .ProduceImages() .ShowResults() @@ -22,26 +23,61 @@ namespace QuestPDF.Examples { container .Padding(25) - .Border(1) - .Background(Colors.Grey.Lighten2) - .Inlined(inlined => + .Decoration(decoration => { - inlined.Spacing(25); - - inlined.AlignCenter(); - inlined.BaselineMiddle(); - - var random = new Random(); - - foreach (var _ in Enumerable.Range(0, 50)) + decoration.Header().Text(text => { - inlined - .Item() - .Border(1) - .Width(random.Next(1, 5) * 25) - .Height(random.Next(1, 5) * 25) - .Background(Placeholders.BackgroundColor()); - } + text.DefaultTextStyle(TextStyle.Default.Size(20)); + + text.CurrentPageNumber(); + text.Span(" / "); + text.TotalPages(); + }); + + decoration + .Content() + .PaddingTop(25) + //.Box() + .Border(1) + .Background(Colors.Grey.Lighten2) + .Inlined(inlined => + { + inlined.Spacing(25); + + 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)); + }); + } + }); }); }); } diff --git a/QuestPDF/Elements/Inlined.cs b/QuestPDF/Elements/Inlined.cs index 0ab3352..8f0cb0b 100644 --- a/QuestPDF/Elements/Inlined.cs +++ b/QuestPDF/Elements/Inlined.cs @@ -8,7 +8,24 @@ namespace QuestPDF.Elements { 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 @@ -16,12 +33,14 @@ namespace QuestPDF.Elements public List Elements { get; internal set; } = new List(); private Queue 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; } public void ResetState() { - Elements.ForEach(x => x.Size ??= x.Measure(Size.Max)); ChildrenQueue = new Queue(Elements); } @@ -41,10 +60,17 @@ namespace QuestPDF.Elements if (!lines.Any()) 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 height = lineSizes.Sum(x => x.Height); + var height = lineSizes.Sum(x => x.Height) + (lines.Count - 1) * VerticalSpacing; var targetSize = new Size(width, height); var isPartiallyRendered = lines.Sum(x => x.Count) != ChildrenQueue.Count; @@ -62,11 +88,11 @@ namespace QuestPDF.Elements 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); - topOffset += height; - Canvas.Translate(new Position(0, height)); + topOffset += height + VerticalSpacing; + Canvas.Translate(new Position(0, height + VerticalSpacing)); } Canvas.Translate(new Position(0, -topOffset)); @@ -75,36 +101,62 @@ namespace QuestPDF.Elements void DrawLine(ICollection elements) { var lineSize = GetLineSize(elements); - + + var elementOffset = ElementOffset(); var leftOffset = AlignOffset(); Canvas.Translate(new Position(leftOffset, 0)); 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); Canvas.Translate(new Position(0, baselineOffset)); element.Draw(size); 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)); - float AlignOffset() + float ElementOffset() { - if (HorizontalAlignment == HorizontalAlignment.Left) + var difference = availableSpace.Width - lineSize.Width; + + if (elements.Count == 1) return 0; - var difference = availableSpace.Width - lineSize.Width; + if (ElementsAlignment == InlinedAlignment.Justify) + return difference / (elements.Count - 1); - if (HorizontalAlignment == HorizontalAlignment.Center) + if (ElementsAlignment == InlinedAlignment.SpaceAround) + return difference / (elements.Count + 1); + + return HorizontalSpacing; + } + + float AlignOffset() + { + if (ElementsAlignment == InlinedAlignment.Left) + return 0; + + if (ElementsAlignment == InlinedAlignment.Justify) + return 0; + + if (ElementsAlignment == InlinedAlignment.SpaceAround) + return elementOffset; + + var difference = availableSpace.Width - lineSize.Width - (elements.Count - 1) * HorizontalSpacing; + + if (ElementsAlignment == InlinedAlignment.Center) return difference / 2; - return difference; + if (ElementsAlignment == InlinedAlignment.Right) + return difference; + + return 0; } float BaselineOffset(Size elementSize, float lineHeight) @@ -125,7 +177,7 @@ namespace QuestPDF.Elements Size GetLineSize(ICollection elements) { var sizes = elements - .Select(x => x.Size as Size) + .Select(x => x.Measure(Size.Max) as Size) .Where(x => x != null) .ToList(); @@ -151,14 +203,14 @@ namespace QuestPDF.Elements break; var height = line - .Select(x => x.Size as Size) + .Select(x => x.Measure(availableSize) as Size) .Where(x => x != null) .Max(x => x.Height); - if (topOffset + height > availableSize.Height) + if (topOffset + height > availableSize.Height + Size.Epsilon) break; - topOffset += height; + topOffset += height + VerticalSpacing; result.Add(line); } @@ -167,7 +219,7 @@ namespace QuestPDF.Elements ICollection GetNextLine() { var result = new List(); - var leftOffset = 0f; + var leftOffset = GetInitialAlignmentOffset(); while (true) { @@ -175,21 +227,31 @@ namespace QuestPDF.Elements break; var element = queue.Peek(); - var size = element.Size as Size; + var size = element.Measure(Size.Max) as Size; if (size == null) break; - if (leftOffset + size.Width > availableSize.Width) + if (leftOffset + size.Width > availableSize.Width + Size.Epsilon) break; queue.Dequeue(); - leftOffset += size.Width; + leftOffset += size.Width + HorizontalSpacing; result.Add(element); } 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; + } } } } \ No newline at end of file diff --git a/QuestPDF/Fluent/InlinedExtensions.cs b/QuestPDF/Fluent/InlinedExtensions.cs index 7075826..b250c49 100644 --- a/QuestPDF/Fluent/InlinedExtensions.cs +++ b/QuestPDF/Fluent/InlinedExtensions.cs @@ -8,11 +8,7 @@ namespace QuestPDF.Fluent { public class InlinedDescriptor { - private ICollection Children = new List(); - 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) { @@ -20,52 +16,25 @@ namespace QuestPDF.Fluent 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() { - var container = new Container(); - Children.Add(container); + var container = new InlinedElement(); + Inlined.Elements.Add(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 @@ -75,7 +44,7 @@ namespace QuestPDF.Fluent var descriptor = new InlinedDescriptor(); handler(descriptor); - element.Element(descriptor.Compose()); + element.Element(descriptor.Inlined); } } } \ No newline at end of file