Implemented the Inlined document

This commit is contained in:
Marcin Ziąbek
2021-10-11 22:48:44 +02:00
parent 479915b68f
commit 9ef444462a
3 changed files with 156 additions and 89 deletions
+55 -19
View File
@@ -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));
});
}
});
});
});
}
+87 -25
View File
@@ -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<InlinedElement> Elements { get; internal set; } = new List<InlinedElement>();
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; }
public void ResetState()
{
Elements.ForEach(x => x.Size ??= x.Measure(Size.Max));
ChildrenQueue = new Queue<InlinedElement>(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<InlinedElement> 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<InlinedElement> 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<InlinedElement> GetNextLine()
{
var result = new List<InlinedElement>();
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;
}
}
}
}
+14 -45
View File
@@ -8,11 +8,7 @@ namespace QuestPDF.Fluent
{
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)
{
@@ -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);
}
}
}