diff --git a/QuestPDF.Examples/DebuggingTesting.cs b/QuestPDF.Examples/DebuggingTesting.cs new file mode 100644 index 0000000..8ccb7ad --- /dev/null +++ b/QuestPDF.Examples/DebuggingTesting.cs @@ -0,0 +1,33 @@ +using NUnit.Framework; +using QuestPDF.Examples.Engine; +using QuestPDF.Fluent; +using QuestPDF.Helpers; + +namespace QuestPDF.Examples +{ + public class DebuggingTesting + { + [Test] + public void Stack() + { + RenderingTest + .Create() + .PageSize(500, 360) + .FileName() + .Render(container => + { + container + .Background(Colors.White) + .Padding(15) + .Grid(grid => + { + grid.Spacing(15); + + grid.Item().Background(Colors.Grey.Medium).Height(50); + grid.Item().Background(Colors.Grey.Lighten1).Height(1000); // <-- problem + grid.Item().Background(Colors.Grey.Lighten2).Height(150); + }); + }); + } + } +} \ No newline at end of file diff --git a/QuestPDF/Drawing/CacheProxy.cs b/QuestPDF/Drawing/CacheProxy.cs index 9924250..15a2579 100644 --- a/QuestPDF/Drawing/CacheProxy.cs +++ b/QuestPDF/Drawing/CacheProxy.cs @@ -1,4 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using QuestPDF.Elements; using QuestPDF.Infrastructure; namespace QuestPDF.Drawing @@ -30,7 +34,7 @@ namespace QuestPDF.Drawing } internal override void Draw(Size availableSpace) - { + { AvailableSpace = null; MeasurementResult = null; @@ -42,4 +46,106 @@ namespace QuestPDF.Drawing return Math.Abs(x - y) < Size.Epsilon; } } + + internal class DebuggingState + { + private DebugStackItem Root { get; set; } + private Stack Stack { get; set; } + + public DebuggingState() + { + Reset(); + } + + public void Reset() + { + Root = null; + Stack = new Stack(); + } + + public void RegisterMeasure(IElement element, Size availableSpace) + { + if (element.GetType() == typeof(Container)) + return; + + var item = new DebugStackItem + { + Element = element, + AvailableSpace = availableSpace + }; + + Root ??= item; + + if (Stack.Any()) + Stack.Peek().Stack.Add(item); + + Stack.Push(item); + } + + public void RegisterMeasureResult(IElement element, SpacePlan spacePlan) + { + if (element.GetType() == typeof(Container)) + return; + + var item = Stack.Pop(); + + if (item.Element != element) + throw new Exception(); + + item.SpacePlan = spacePlan; + } + + public string BuildTrace() + { + var builder = new StringBuilder(); + var nestingLevel = 0; + + Traverse(Root); + return builder.ToString(); + + void Traverse(DebugStackItem item) + { + var spaceIndent = new string(' ', nestingLevel * 4); + var wrapIndent = new string('>', nestingLevel * 4); + + var firstIndent = item.SpacePlan.Type == SpacePlanType.Wrap ? wrapIndent : spaceIndent; + + builder.AppendLine(firstIndent + item.Element); + builder.AppendLine(spaceIndent + "Available space: " + item.AvailableSpace); + builder.AppendLine(spaceIndent + "Take space: " + item.SpacePlan); + + nestingLevel++; + item.Stack.ToList().ForEach(Traverse); + nestingLevel--; + } + } + } + + public class DebugStackItem + { + public IElement Element { get; internal set; } + public Size AvailableSpace { get; internal set; } + public SpacePlan SpacePlan { get; internal set; } + + public ICollection Stack { get; internal set; } = new List(); + } + + internal class DebuggingProxy : ElementProxy + { + private DebuggingState DebuggingState { get; } + + public DebuggingProxy(DebuggingState debuggingState) + { + DebuggingState = debuggingState; + } + + internal override SpacePlan Measure(Size availableSpace) + { + DebuggingState.RegisterMeasure(Child, availableSpace); + var spacePlan = base.Measure(availableSpace); + DebuggingState.RegisterMeasureResult(Child, spacePlan); + + return spacePlan; + } + } } \ No newline at end of file diff --git a/QuestPDF/Drawing/DocumentGenerator.cs b/QuestPDF/Drawing/DocumentGenerator.cs index a4e5de9..5b0830b 100644 --- a/QuestPDF/Drawing/DocumentGenerator.cs +++ b/QuestPDF/Drawing/DocumentGenerator.cs @@ -35,13 +35,14 @@ namespace QuestPDF.Drawing var metadata = document.GetMetadata(); var pageContext = new PageContext(); - ApplyElementProxy(content); - - RenderPass(pageContext, new FreeCanvas(), content, metadata); - RenderPass(pageContext, canvas, content, metadata); + //ApplyCaching(content); + var debuggingState = ApplyDebugging(content); + + RenderPass(pageContext, new FreeCanvas(), content, metadata, debuggingState); + RenderPass(pageContext, canvas, content, metadata, debuggingState); } - private static void RenderPass(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata) + internal static void RenderPass(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata, DebuggingState? debuggingState) where TCanvas : ICanvas, IRenderingCanvas { content.HandleVisitor(x => x?.Initialize(pageContext, canvas)); @@ -54,10 +55,15 @@ namespace QuestPDF.Drawing while(true) { pageContext.SetPageNumber(currentPage); + debuggingState?.Reset(); + var spacePlan = content.Measure(Size.Max); if (spacePlan.Type == SpacePlanType.Wrap) - break; + { + canvas.EndDocument(); + throw new DocumentDrawingException("An exception occured during document drawing."); + } try { @@ -87,12 +93,24 @@ namespace QuestPDF.Drawing canvas.EndDocument(); } - private static void ApplyElementProxy(Container content) where TProxy : ElementProxy, new() + private static void ApplyCaching(Container content) { - content.HandleVisitor(x => x.CreateProxy(y => new TProxy() + content.HandleVisitor(x => x.CreateProxy(y => new CacheProxy { Child = y })); } + + private static DebuggingState ApplyDebugging(Container content) + { + var debuggingState = new DebuggingState(); + + content.HandleVisitor(x => x.CreateProxy(y => new DebuggingProxy(debuggingState) + { + Child = y + })); + + return debuggingState; + } } } \ No newline at end of file diff --git a/QuestPDF/Drawing/SpacePlan.cs b/QuestPDF/Drawing/SpacePlan.cs index aaedfb9..e307d12 100644 --- a/QuestPDF/Drawing/SpacePlan.cs +++ b/QuestPDF/Drawing/SpacePlan.cs @@ -1,4 +1,5 @@ -using QuestPDF.Infrastructure; +using System.Collections.Generic; +using QuestPDF.Infrastructure; namespace QuestPDF.Drawing { @@ -15,23 +16,28 @@ namespace QuestPDF.Drawing public readonly float Width; public readonly float Height; - public SpacePlan(SpacePlanType type, float width, float height) + internal SpacePlan(SpacePlanType type, float width, float height) { Type = type; Width = width; Height = height; } - public static SpacePlan Wrap() => new SpacePlan(SpacePlanType.Wrap, 0, 0); + internal static SpacePlan Wrap() => new SpacePlan(SpacePlanType.Wrap, 0, 0); - public static SpacePlan PartialRender(float width, float height) => new SpacePlan(SpacePlanType.PartialRender, width, height); + internal static SpacePlan PartialRender(float width, float height) => new SpacePlan(SpacePlanType.PartialRender, width, height); - public static SpacePlan PartialRender(Size size) => PartialRender(size.Width, size.Height); + internal static SpacePlan PartialRender(Size size) => PartialRender(size.Width, size.Height); - public static SpacePlan FullRender(float width, float height) => new SpacePlan(SpacePlanType.FullRender, width, height); + internal static SpacePlan FullRender(float width, float height) => new SpacePlan(SpacePlanType.FullRender, width, height); + + internal static SpacePlan FullRender(Size size) => FullRender(size.Width, size.Height); + + public override string ToString() + { + return $"{Type}({Width:N2};{Height:N2})"; + } - public static SpacePlan FullRender(Size size) => FullRender(size.Width, size.Height); - public static implicit operator Size(SpacePlan spacePlan) { return new Size(spacePlan.Width, spacePlan.Height); diff --git a/QuestPDF/Elements/Border.cs b/QuestPDF/Elements/Border.cs index 6564b28..cbb6c8b 100644 --- a/QuestPDF/Elements/Border.cs +++ b/QuestPDF/Elements/Border.cs @@ -36,5 +36,10 @@ namespace QuestPDF.Elements new Size(Right, availableSpace.Height + Top/2 + Bottom/2), Color); } + + public override string ToString() + { + return $"Border: Top({Top}) Right({Right}) Bottom({Bottom}) Left({Left}) Color({Color})"; + } } } \ No newline at end of file diff --git a/QuestPDF/Elements/Padding.cs b/QuestPDF/Elements/Padding.cs index 5463629..74cca61 100644 --- a/QuestPDF/Elements/Padding.cs +++ b/QuestPDF/Elements/Padding.cs @@ -57,5 +57,10 @@ namespace QuestPDF.Elements availableSpace.Width - Left - Right, availableSpace.Height - Top - Bottom); } + + public override string ToString() + { + return $"Padding: Top({Top}) Right({Right}) Bottom({Bottom}) Left({Left})"; + } } } \ No newline at end of file diff --git a/QuestPDF/Elements/Row.cs b/QuestPDF/Elements/Row.cs index 8718895..f1e15ca 100644 --- a/QuestPDF/Elements/Row.cs +++ b/QuestPDF/Elements/Row.cs @@ -24,6 +24,11 @@ namespace QuestPDF.Elements Size = size; SetWidth(size); } + + public override string ToString() + { + return $"RowColumn: Constant({Size})"; + } } internal class RelativeRowElement : RowElement @@ -32,6 +37,11 @@ namespace QuestPDF.Elements { Size = size; } + + public override string ToString() + { + return $"RowColumn: Relative({Size})"; + } } internal class SimpleRow : Element diff --git a/QuestPDF/Infrastructure/Element.cs b/QuestPDF/Infrastructure/Element.cs index 4bef86d..6d948a5 100644 --- a/QuestPDF/Infrastructure/Element.cs +++ b/QuestPDF/Infrastructure/Element.cs @@ -26,5 +26,10 @@ namespace QuestPDF.Infrastructure internal abstract SpacePlan Measure(Size availableSpace); internal abstract void Draw(Size availableSpace); + + public override string ToString() + { + return GetType().Name; + } } } \ No newline at end of file diff --git a/QuestPDF/Infrastructure/Size.cs b/QuestPDF/Infrastructure/Size.cs index 9ce4b8e..d7bf147 100644 --- a/QuestPDF/Infrastructure/Size.cs +++ b/QuestPDF/Infrastructure/Size.cs @@ -16,6 +16,6 @@ Height = height; } - public override string ToString() => $"(W: {Width}, H: {Height})"; + public override string ToString() => $"{Width:N2} {Height:N2}"; } } \ No newline at end of file diff --git a/QuestPDF/QuestPDF.csproj b/QuestPDF/QuestPDF.csproj index 04c4152..3100c33 100644 --- a/QuestPDF/QuestPDF.csproj +++ b/QuestPDF/QuestPDF.csproj @@ -36,8 +36,4 @@ - - - -