Compare commits

...

1 Commits

Author SHA1 Message Date
MarcinZiabek 09e642295f Repeat content experiment 2022-10-28 23:22:11 +02:00
13 changed files with 217 additions and 39 deletions
@@ -0,0 +1,38 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
namespace QuestPDF.Examples
{
public class RepeatContentExamples
{
[Test]
public void ItemTypes()
{
RenderingTest
.Create()
.ProducePdf()
.PageSize(PageSizes.A4)
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.Decoration(decoration =>
{
decoration.Before().Text("Test").FontSize(22);
decoration.Content().Column(column =>
{
column.Spacing(20);
foreach (var _ in Enumerable.Range(0, 10))
column.Item().Background(Colors.Grey.Medium).ExtendHorizontal().Height(80);
});
});
});
}
}
}
+47
View File
@@ -65,6 +65,7 @@ namespace QuestPDF.Drawing
document.Compose(container); document.Compose(container);
var content = container.Compose(); var content = container.Compose();
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault); ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
ApplyContentRepeatState(content, false);
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null; var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
@@ -82,6 +83,8 @@ namespace QuestPDF.Drawing
content.VisitChildren(x => x?.Initialize(pageContext, canvas)); content.VisitChildren(x => x?.Initialize(pageContext, canvas));
content.VisitChildren(x => (x as IStateResettable)?.ResetState()); content.VisitChildren(x => (x as IStateResettable)?.ResetState());
ResetIsRenderedState(content);
canvas.BeginDocument(); canvas.BeginDocument();
var currentPage = 1; var currentPage = 1;
@@ -161,6 +164,50 @@ namespace QuestPDF.Drawing
return debuggingState; return debuggingState;
} }
private static void ApplyContentRepeatState(Element? content, bool repeatContent)
{
if (content == null)
return;
if (content is IVisual visual)
visual.RepeatContent = repeatContent;
if (content is TextBlock textBlock)
{
foreach (var textBlockItem in textBlock.Items)
{
if (textBlockItem is TextBlockElement textElement)
{
ApplyContentRepeatState(textElement.Element, true);
}
}
return;
}
// TODO: apply RepeatState in dynamic content
//if (content is DynamicHost dynamicHost)
// dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
if (content is RepeatContentSetter repeatContentSetter)
repeatContent = repeatContentSetter.RepeatContent;
foreach (var child in content.GetChildren())
ApplyContentRepeatState(child, repeatContent);
}
private static void ResetIsRenderedState(Element? content)
{
if (content == null)
return;
if (content is IVisual visual)
visual.IsRendered = false;
foreach (var child in content.GetChildren())
ResetIsRenderedState(child);
}
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle) internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
{ {
if (content == null) if (content == null)
+13 -4
View File
@@ -7,19 +7,28 @@ namespace QuestPDF.Elements
{ {
public delegate void DrawOnCanvas(SKCanvas canvas, Size availableSpace); public delegate void DrawOnCanvas(SKCanvas canvas, Size availableSpace);
internal class Canvas : Element, ICacheable internal class Canvas : Element, IVisual, ICacheable
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public DrawOnCanvas Handler { get; set; } public DrawOnCanvas Handler { get; set; }
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
return availableSpace.IsNegative() if (availableSpace.IsNegative())
? SpacePlan.Wrap() return SpacePlan.Wrap();
: SpacePlan.FullRender(availableSpace);
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
IsRendered = true;
var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas; var skiaCanvas = (Canvas as Drawing.SkiaCanvasBase)?.Canvas;
if (Handler == null || skiaCanvas == null) if (Handler == null || skiaCanvas == null)
+16 -4
View File
@@ -6,19 +6,31 @@ using SkiaSharp;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class DynamicImage : Element internal class DynamicImage : Element, IVisual
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public Func<Size, byte[]>? Source { get; set; } public Func<Size, byte[]>? Source { get; set; }
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
return availableSpace.IsNegative() if (availableSpace.IsNegative())
? SpacePlan.Wrap() return SpacePlan.Wrap();
: SpacePlan.FullRender(availableSpace);
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
IsRendered = true;
if (availableSpace.Width < Size.Epsilon || availableSpace.Height < Size.Epsilon)
return;
var imageData = Source?.Invoke(availableSpace); var imageData = Source?.Invoke(availableSpace);
if (imageData == null) if (imageData == null)
+12 -4
View File
@@ -5,8 +5,11 @@ using SkiaSharp;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
internal class Image : Element, ICacheable internal class Image : Element, IVisual, ICacheable
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public SKImage? InternalImage { get; set; } public SKImage? InternalImage { get; set; }
~Image() ~Image()
@@ -16,9 +19,13 @@ namespace QuestPDF.Elements
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
return availableSpace.IsNegative() if (availableSpace.IsNegative())
? SpacePlan.Wrap() return SpacePlan.Wrap();
: SpacePlan.FullRender(availableSpace);
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return SpacePlan.FullRender(availableSpace);
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
@@ -26,6 +33,7 @@ namespace QuestPDF.Elements
if (InternalImage == null) if (InternalImage == null)
return; return;
IsRendered = true;
Canvas.DrawImage(InternalImage, Position.Zero, availableSpace); Canvas.DrawImage(InternalImage, Position.Zero, availableSpace);
} }
} }
+14 -6
View File
@@ -15,34 +15,42 @@ namespace QuestPDF.Elements
Horizontal Horizontal
} }
internal class Line : Element, ILine, ICacheable internal class Line : Element, ILine, IVisual, ICacheable
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public LineType Type { get; set; } = LineType.Vertical; public LineType Type { get; set; } = LineType.Vertical;
public string Color { get; set; } = Colors.Black; public string Color { get; set; } = Colors.Black;
public float Size { get; set; } = 1; public float Thickness { get; set; } = 1;
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
if (availableSpace.IsNegative()) if (availableSpace.IsNegative())
return SpacePlan.Wrap(); return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
return Type switch return Type switch
{ {
LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(Size, 0), LineType.Vertical when availableSpace.Width + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(Thickness, 0),
LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Size => SpacePlan.FullRender(0, Size), LineType.Horizontal when availableSpace.Height + Infrastructure.Size.Epsilon >= Thickness => SpacePlan.FullRender(0, Thickness),
_ => SpacePlan.Wrap() _ => SpacePlan.Wrap()
}; };
} }
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
IsRendered = true;
if (Type == LineType.Vertical) if (Type == LineType.Vertical)
{ {
Canvas.DrawRectangle(new Position(-Size/2, 0), new Size(Size, availableSpace.Height), Color); Canvas.DrawRectangle(new Position(-Thickness/2, 0), new Size(Thickness, availableSpace.Height), Color);
} }
else if (Type == LineType.Horizontal) else if (Type == LineType.Horizontal)
{ {
Canvas.DrawRectangle(new Position(0, -Size/2), new Size(availableSpace.Width, Size), Color); Canvas.DrawRectangle(new Position(0, -Thickness/2), new Size(availableSpace.Width, Thickness), Color);
} }
} }
} }
+9
View File
@@ -0,0 +1,9 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class RepeatContentSetter : ContainerElement
{
public bool RepeatContent { get; set; }
}
}
+17 -1
View File
@@ -4,12 +4,16 @@ using System.Linq;
using QuestPDF.Drawing; using QuestPDF.Drawing;
using QuestPDF.Elements.Text.Calculation; using QuestPDF.Elements.Text.Calculation;
using QuestPDF.Elements.Text.Items; using QuestPDF.Elements.Text.Items;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure; using QuestPDF.Infrastructure;
namespace QuestPDF.Elements.Text namespace QuestPDF.Elements.Text
{ {
internal class TextBlock : Element, IStateResettable internal class TextBlock : Element, IVisual, IStateResettable
{ {
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left; public HorizontalAlignment Alignment { get; set; } = HorizontalAlignment.Left;
public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>(); public List<ITextBlockItem> Items { get; set; } = new List<ITextBlockItem>();
@@ -53,6 +57,12 @@ namespace QuestPDF.Elements.Text
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
if (availableSpace.IsNegative())
return SpacePlan.Wrap();
if (IsRendered && !RepeatContent)
return SpacePlan.FullRender(Size.Zero);
if (!RenderingQueue.Any()) if (!RenderingQueue.Any())
return SpacePlan.FullRender(Size.Zero); return SpacePlan.FullRender(Size.Zero);
@@ -80,6 +90,9 @@ namespace QuestPDF.Elements.Text
internal override void Draw(Size availableSpace) internal override void Draw(Size availableSpace)
{ {
if (IsRendered && !RepeatContent)
return;
var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList(); var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList();
if (!lines.Any()) if (!lines.Any())
@@ -138,7 +151,10 @@ namespace QuestPDF.Elements.Text
CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex; CurrentElementIndex = lastElementMeasurement.IsLast ? 0 : lastElementMeasurement.NextIndex;
if (!RenderingQueue.Any()) if (!RenderingQueue.Any())
{
ResetState(); ResetState();
IsRendered = true;
}
float GetAlignmentOffset(float lineWidth) float GetAlignmentOffset(float lineWidth)
{ {
+4 -8
View File
@@ -12,7 +12,7 @@ namespace QuestPDF.Fluent
{ {
var container = new Container(); var container = new Container();
Decoration.Before = container; Decoration.Before = container;
return container; return container.RepeatContentWhenPaging();
} }
public void Before(Action<IContainer> handler) public void Before(Action<IContainer> handler)
@@ -36,7 +36,7 @@ namespace QuestPDF.Fluent
{ {
var container = new Container(); var container = new Container();
Decoration.After = container; Decoration.After = container;
return container; return container.RepeatContentWhenPaging();
} }
public void After(Action<IContainer> handler) public void After(Action<IContainer> handler)
@@ -49,9 +49,7 @@ namespace QuestPDF.Fluent
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
public IContainer Header() public IContainer Header()
{ {
var container = new Container(); return Before();
Decoration.Before = container;
return container;
} }
[Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'Before' method.")]
@@ -63,9 +61,7 @@ namespace QuestPDF.Fluent
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
public IContainer Footer() public IContainer Footer()
{ {
var container = new Container(); return After();
Decoration.After = container;
return container;
} }
[Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")] [Obsolete("This element has been renamed since version 2022.2. Please use the 'After' method.")]
+1 -1
View File
@@ -24,7 +24,7 @@ namespace QuestPDF.Fluent
return container; return container;
} }
public IContainer Layer() => Layer(false); public IContainer Layer() => Layer(false).RepeatContentWhenPaging();
public IContainer PrimaryLayer() => Layer(true); public IContainer PrimaryLayer() => Layer(true);
internal void Validate() internal void Validate()
+6 -6
View File
@@ -6,11 +6,11 @@ namespace QuestPDF.Fluent
{ {
public static class LineExtensions public static class LineExtensions
{ {
private static ILine Line(this IContainer element, LineType type, float size) private static ILine Line(this IContainer element, LineType type, float thickness)
{ {
var line = new Line var line = new Line
{ {
Size = size, Thickness = thickness,
Type = type Type = type
}; };
@@ -18,14 +18,14 @@ namespace QuestPDF.Fluent
return line; return line;
} }
public static ILine LineVertical(this IContainer element, float size, Unit unit = Unit.Point) public static ILine LineVertical(this IContainer element, float thickness, Unit unit = Unit.Point)
{ {
return element.Line(LineType.Vertical, size.ToPoints(unit)); return element.Line(LineType.Vertical, thickness.ToPoints(unit));
} }
public static ILine LineHorizontal(this IContainer element, float size, Unit unit = Unit.Point) public static ILine LineHorizontal(this IContainer element, float thickness, Unit unit = Unit.Point)
{ {
return element.Line(LineType.Horizontal, size.ToPoints(unit)); return element.Line(LineType.Horizontal, thickness.ToPoints(unit));
} }
public static void LineColor(this ILine descriptor, string value) public static void LineColor(this ILine descriptor, string value)
@@ -0,0 +1,27 @@
using System;
using QuestPDF.Elements;
using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent
{
public static class RepeatContentExtensions
{
private static IContainer RepeatContent(this IContainer element, Action<RepeatContentSetter> handler)
{
var repeatContentSetter = element as RepeatContentSetter ?? new RepeatContentSetter();
handler(repeatContentSetter);
return element.Element(repeatContentSetter);
}
public static IContainer RepeatContentWhenPaging(this IContainer element)
{
return element.RepeatContent(x => x.RepeatContent = true);
}
public static IContainer DoNotRepeatContentWhenPaging(this IContainer element)
{
return element.RepeatContent(x => x.RepeatContent = false);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace QuestPDF.Infrastructure
{
internal interface IVisual
{
public bool IsRendered { get; set; }
public bool RepeatContent { get; set; }
}
}