Implemented the Inlined document
This commit is contained in:
@@ -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));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user