diff --git a/QuestPDF.Examples/DebuggingTesting.cs b/QuestPDF.Examples/DebuggingTesting.cs index 8ccb7ad..4021eb2 100644 --- a/QuestPDF.Examples/DebuggingTesting.cs +++ b/QuestPDF.Examples/DebuggingTesting.cs @@ -23,8 +23,8 @@ namespace QuestPDF.Examples { 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.Medium).Text(Placeholders.LoremIpsum()); + grid.Item().DebugPointer("Test").Background(Colors.Grey.Lighten1).Height(1000); // <-- problem grid.Item().Background(Colors.Grey.Lighten2).Height(150); }); }); diff --git a/QuestPDF.Examples/ElementExamples.cs b/QuestPDF.Examples/ElementExamples.cs index 5494050..9adf8d8 100644 --- a/QuestPDF.Examples/ElementExamples.cs +++ b/QuestPDF.Examples/ElementExamples.cs @@ -155,7 +155,7 @@ namespace QuestPDF.Examples { container .Padding(25) - .Debug("Grid example", Colors.Blue.Medium) + .DebugArea("Grid example", Colors.Blue.Medium) .Grid(grid => { grid.Columns(3); diff --git a/QuestPDF.ReportSample/Layouts/SectionTemplate.cs b/QuestPDF.ReportSample/Layouts/SectionTemplate.cs index 147862e..2ae3484 100644 --- a/QuestPDF.ReportSample/Layouts/SectionTemplate.cs +++ b/QuestPDF.ReportSample/Layouts/SectionTemplate.cs @@ -66,13 +66,13 @@ namespace QuestPDF.ReportSample.Layouts void PhotosElement(IContainer container, ReportSectionPhotos model) { - if (model.Photos.Count == 0) + if (model.Photos.Count == 0) { container.Text("No photos", Typography.Normal); return; } - container.Debug("Photos").Grid(grid => + container.DebugArea("Photos").Grid(grid => { grid.Spacing(5); grid.Columns(3); diff --git a/QuestPDF.UnitTests/DecorationTests.cs b/QuestPDF.UnitTests/DecorationTests.cs index 749cada..b1ea8b4 100644 --- a/QuestPDF.UnitTests/DecorationTests.cs +++ b/QuestPDF.UnitTests/DecorationTests.cs @@ -15,7 +15,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsWrap_WhenDecorationReturnsWrap() { TestPlan - .For(x => new SimpleDecoration + .For(x => new BinaryDecoration { Type = DecorationType.Append, DecorationElement = x.CreateChild("decoration"), @@ -30,7 +30,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsWrap_WhenDecorationReturnsPartialRender() { TestPlan - .For(x => new SimpleDecoration + .For(x => new BinaryDecoration { Type = DecorationType.Append, DecorationElement = x.CreateChild("decoration"), @@ -45,7 +45,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsWrap_WhenContentReturnsWrap() { TestPlan - .For(x => new SimpleDecoration + .For(x => new BinaryDecoration { Type = DecorationType.Append, DecorationElement = x.CreateChild("decoration"), @@ -61,7 +61,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsPartialRender_WhenContentReturnsPartialRender() { TestPlan - .For(x => new SimpleDecoration + .For(x => new BinaryDecoration { Type = DecorationType.Append, DecorationElement = x.CreateChild("decoration"), @@ -77,7 +77,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsFullRender_WhenContentReturnsFullRender() { TestPlan - .For(x => new SimpleDecoration + .For(x => new BinaryDecoration { Type = DecorationType.Append, DecorationElement = x.CreateChild("decoration"), @@ -97,7 +97,7 @@ namespace QuestPDF.UnitTests public void Draw_Prepend() { TestPlan - .For(x => new SimpleDecoration + .For(x => new BinaryDecoration { Type = DecorationType.Prepend, DecorationElement = x.CreateChild("decoration"), @@ -116,7 +116,7 @@ namespace QuestPDF.UnitTests public void Draw_Append() { TestPlan - .For(x => new SimpleDecoration + .For(x => new BinaryDecoration { Type = DecorationType.Append, DecorationElement = x.CreateChild("decoration"), diff --git a/QuestPDF.UnitTests/RowTests.cs b/QuestPDF.UnitTests/RowTests.cs index db0e47b..b9ef66c 100644 --- a/QuestPDF.UnitTests/RowTests.cs +++ b/QuestPDF.UnitTests/RowTests.cs @@ -15,7 +15,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsWrap_WhenLeftChildReturnsWrap() { TestPlan - .For(x => new SimpleRow + .For(x => new BinaryRow { Left = x.CreateChild("left"), Right = x.CreateChild("right") @@ -29,7 +29,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsWrap_WhenRightChildReturnsWrap() { TestPlan - .For(x => new SimpleRow + .For(x => new BinaryRow { Left = x.CreateChild("left"), Right = x.CreateChild("right") @@ -44,7 +44,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsPartialRender_WhenLeftChildReturnsPartialRender() { TestPlan - .For(x => new SimpleRow + .For(x => new BinaryRow { Left = x.CreateChild("left"), Right = x.CreateChild("right") @@ -59,7 +59,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsPartialRender_WhenRightChildReturnsPartialRender() { TestPlan - .For(x => new SimpleRow + .For(x => new BinaryRow { Left = x.CreateChild("left"), Right = x.CreateChild("right") @@ -74,7 +74,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsFullRender_WhenBothChildrenReturnFullRender() { TestPlan - .For(x => new SimpleRow + .For(x => new BinaryRow { Left = x.CreateChild("left"), Right = x.CreateChild("right") @@ -93,7 +93,7 @@ namespace QuestPDF.UnitTests public void Draw() { TestPlan - .For(x => new SimpleRow + .For(x => new BinaryRow { Left = x.CreateChild("left"), Right = x.CreateChild("right") diff --git a/QuestPDF.UnitTests/StackTests.cs b/QuestPDF.UnitTests/StackTests.cs index 9910321..9f79509 100644 --- a/QuestPDF.UnitTests/StackTests.cs +++ b/QuestPDF.UnitTests/StackTests.cs @@ -15,7 +15,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsWrap_WhenFirstChildWraps() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -29,7 +29,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -43,7 +43,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsPartialRender_WhenSecondChildWraps() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -58,7 +58,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -73,7 +73,7 @@ namespace QuestPDF.UnitTests public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -88,7 +88,7 @@ namespace QuestPDF.UnitTests public void Measure_UsesEmpty_WhenFirstChildIsRendered() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second"), @@ -108,7 +108,7 @@ namespace QuestPDF.UnitTests public void Draw_WhenFirstChildWraps() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -122,7 +122,7 @@ namespace QuestPDF.UnitTests public void Draw_WhenFirstChildPartiallyRenders() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -137,7 +137,7 @@ namespace QuestPDF.UnitTests public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -153,7 +153,7 @@ namespace QuestPDF.UnitTests public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -172,7 +172,7 @@ namespace QuestPDF.UnitTests public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second") @@ -191,7 +191,7 @@ namespace QuestPDF.UnitTests public void Draw_UsesEmpty_WhenFirstChildIsRendered() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second"), @@ -203,7 +203,7 @@ namespace QuestPDF.UnitTests .ExpectCanvasTranslate(0, 0) .ExpectChildDraw("second", new Size(400, 300)) .ExpectCanvasTranslate(0, 0) - .CheckState(x => x.IsFirstRendered) + .CheckState(x => x.IsFirstRendered) .CheckDrawResult(); } @@ -211,7 +211,7 @@ namespace QuestPDF.UnitTests public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders() { TestPlan - .For(x => new SimpleStack + .For(x => new BinaryStack { First = x.CreateChild("first"), Second = x.CreateChild("second"), @@ -224,7 +224,7 @@ namespace QuestPDF.UnitTests .ExpectChildDraw("second", new Size(400, 300)) .ExpectCanvasTranslate(0, 0) .CheckDrawResult() - .CheckState(x => !x.IsFirstRendered); + .CheckState(x => !x.IsFirstRendered); } #endregion diff --git a/QuestPDF/Drawing/CacheProxy.cs b/QuestPDF/Drawing/CacheProxy.cs deleted file mode 100644 index 15a2579..0000000 --- a/QuestPDF/Drawing/CacheProxy.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using QuestPDF.Elements; -using QuestPDF.Infrastructure; - -namespace QuestPDF.Drawing -{ - internal class ElementProxy : ContainerElement - { - - } - - internal class CacheProxy : ElementProxy - { - public Size? AvailableSpace { get; set; } - public SpacePlan? MeasurementResult { get; set; } - - internal override SpacePlan Measure(Size availableSpace) - { - if (MeasurementResult != null && - AvailableSpace != null && - IsClose(AvailableSpace.Value.Width, availableSpace.Width) && - IsClose(AvailableSpace.Value.Height, availableSpace.Height)) - { - return MeasurementResult.Value; - } - - AvailableSpace = availableSpace; - MeasurementResult = base.Measure(availableSpace); - - return MeasurementResult.Value; - } - - internal override void Draw(Size availableSpace) - { - AvailableSpace = null; - MeasurementResult = null; - - base.Draw(availableSpace); - } - - private bool IsClose(float x, float y) - { - 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 5b0830b..d53934c 100644 --- a/QuestPDF/Drawing/DocumentGenerator.cs +++ b/QuestPDF/Drawing/DocumentGenerator.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using QuestPDF.Drawing.Exceptions; +using QuestPDF.Drawing.Proxy; using QuestPDF.Elements; using QuestPDF.Infrastructure; @@ -35,8 +36,16 @@ namespace QuestPDF.Drawing var metadata = document.GetMetadata(); var pageContext = new PageContext(); - //ApplyCaching(content); - var debuggingState = ApplyDebugging(content); + DebuggingState debuggingState = null; + + if (System.Diagnostics.Debugger.IsAttached) + { + debuggingState = ApplyDebugging(content); + } + else + { + ApplyCaching(content); + } RenderPass(pageContext, new FreeCanvas(), content, metadata, debuggingState); RenderPass(pageContext, canvas, content, metadata, debuggingState); @@ -62,7 +71,7 @@ namespace QuestPDF.Drawing if (spacePlan.Type == SpacePlanType.Wrap) { canvas.EndDocument(); - throw new DocumentDrawingException("An exception occured during document drawing."); + ThrowLayoutException(); } try @@ -81,7 +90,7 @@ namespace QuestPDF.Drawing if (currentPage >= documentMetadata.DocumentLayoutExceptionThreshold) { canvas.EndDocument(); - throw new DocumentLayoutException("Composed layout generates infinite document."); + ThrowLayoutException(); } if (spacePlan.Type == SpacePlanType.FullRender) @@ -91,6 +100,14 @@ namespace QuestPDF.Drawing } canvas.EndDocument(); + + void ThrowLayoutException() + { + throw new DocumentLayoutException("Composed layout generates infinite document.") + { + ElementTrace = debuggingState?.BuildTrace() ?? "Debug trace is available only in the DEBUG mode." + }; + } } private static void ApplyCaching(Container content) diff --git a/QuestPDF/Drawing/Exceptions/DocumentLayoutException.cs b/QuestPDF/Drawing/Exceptions/DocumentLayoutException.cs index 4f8c6c2..ff83755 100644 --- a/QuestPDF/Drawing/Exceptions/DocumentLayoutException.cs +++ b/QuestPDF/Drawing/Exceptions/DocumentLayoutException.cs @@ -4,6 +4,8 @@ namespace QuestPDF.Drawing.Exceptions { public class DocumentLayoutException : Exception { + public string ElementTrace { get; set; } + public DocumentLayoutException() { diff --git a/QuestPDF/Drawing/Proxy/CacheProxy.cs b/QuestPDF/Drawing/Proxy/CacheProxy.cs new file mode 100644 index 0000000..92d6020 --- /dev/null +++ b/QuestPDF/Drawing/Proxy/CacheProxy.cs @@ -0,0 +1,40 @@ +using System; +using QuestPDF.Infrastructure; + +namespace QuestPDF.Drawing.Proxy +{ + internal class CacheProxy : ElementProxy + { + public Size? AvailableSpace { get; set; } + public SpacePlan? MeasurementResult { get; set; } + + internal override SpacePlan Measure(Size availableSpace) + { + if (MeasurementResult != null && + AvailableSpace != null && + IsClose(AvailableSpace.Value.Width, availableSpace.Width) && + IsClose(AvailableSpace.Value.Height, availableSpace.Height)) + { + return MeasurementResult.Value; + } + + AvailableSpace = availableSpace; + MeasurementResult = base.Measure(availableSpace); + + return MeasurementResult.Value; + } + + internal override void Draw(Size availableSpace) + { + AvailableSpace = null; + MeasurementResult = null; + + base.Draw(availableSpace); + } + + private bool IsClose(float x, float y) + { + return Math.Abs(x - y) < Size.Epsilon; + } + } +} \ No newline at end of file diff --git a/QuestPDF/Drawing/Proxy/DebugStackItem.cs b/QuestPDF/Drawing/Proxy/DebugStackItem.cs new file mode 100644 index 0000000..20cd22e --- /dev/null +++ b/QuestPDF/Drawing/Proxy/DebugStackItem.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using QuestPDF.Infrastructure; + +namespace QuestPDF.Drawing.Proxy +{ + 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(); + } +} \ No newline at end of file diff --git a/QuestPDF/Drawing/Proxy/DebuggingProxy.cs b/QuestPDF/Drawing/Proxy/DebuggingProxy.cs new file mode 100644 index 0000000..d10bd9b --- /dev/null +++ b/QuestPDF/Drawing/Proxy/DebuggingProxy.cs @@ -0,0 +1,23 @@ +using QuestPDF.Infrastructure; + +namespace QuestPDF.Drawing.Proxy +{ + 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/Proxy/DebuggingState.cs b/QuestPDF/Drawing/Proxy/DebuggingState.cs new file mode 100644 index 0000000..eaf41b5 --- /dev/null +++ b/QuestPDF/Drawing/Proxy/DebuggingState.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using QuestPDF.Elements; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace QuestPDF.Drawing.Proxy +{ + 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 indent = new string(' ', nestingLevel * 4); + var title = item.Element.GetType().Name; + + if (item.Element is DebugPointer debugPointer) + { + title = debugPointer.Target; + title += debugPointer.Highlight ? " 🌟" : string.Empty; + } + + if (item.SpacePlan.Type == SpacePlanType.Wrap) + title = "🔥 " + title; + + builder.AppendLine(indent + title); + builder.AppendLine(indent + new string('-', title.Length)); + + builder.AppendLine(indent + "Available space: " + item.AvailableSpace); + builder.AppendLine(indent + "Requested space: " + item.SpacePlan); + + foreach (var configuration in GetElementConfiguration(item.Element)) + builder.AppendLine(indent + configuration); + + builder.AppendLine(); + + nestingLevel++; + item.Stack.ToList().ForEach(Traverse); + nestingLevel--; + } + + static IEnumerable GetElementConfiguration(IElement element) + { + if (element is DebugPointer) + return Enumerable.Empty(); + + return element + .GetType() + .GetProperties() + .Select(x => new + { + Property = x.Name.PrettifyName(), + Value = x.GetValue(element) + }) + .Where(x => !(x.Value is IElement)) + .Where(x => !(x.Value is IEnumerable)) + .Where(x => !(x.Value is TextStyle)) + .Select(x => $"{x.Property}: {FormatValue(x.Value)}"); + + string FormatValue(object value) + { + const int maxLength = 100; + + var text = value?.ToString() ?? "-"; + + if (text.Length < maxLength) + return text; + + return text.AsSpan(0, maxLength).ToString() + "..."; + } + } + } + } +} \ No newline at end of file diff --git a/QuestPDF/Drawing/Proxy/ElementProxy.cs b/QuestPDF/Drawing/Proxy/ElementProxy.cs new file mode 100644 index 0000000..6e7da3d --- /dev/null +++ b/QuestPDF/Drawing/Proxy/ElementProxy.cs @@ -0,0 +1,9 @@ +using QuestPDF.Infrastructure; + +namespace QuestPDF.Drawing.Proxy +{ + internal class ElementProxy : ContainerElement + { + + } +} \ No newline at end of file diff --git a/QuestPDF/Drawing/SpacePlan.cs b/QuestPDF/Drawing/SpacePlan.cs index e307d12..9d71c9e 100644 --- a/QuestPDF/Drawing/SpacePlan.cs +++ b/QuestPDF/Drawing/SpacePlan.cs @@ -3,13 +3,6 @@ using QuestPDF.Infrastructure; namespace QuestPDF.Drawing { - public enum SpacePlanType - { - Wrap, - PartialRender, - FullRender - } - public readonly struct SpacePlan { public readonly SpacePlanType Type; @@ -35,7 +28,10 @@ namespace QuestPDF.Drawing public override string ToString() { - return $"{Type}({Width:N2};{Height:N2})"; + if (Type == SpacePlanType.Wrap) + return Type.ToString(); + + return $"{Type} | width {Width:N2} | height {Height:N2}"; } public static implicit operator Size(SpacePlan spacePlan) diff --git a/QuestPDF/Drawing/SpacePlanType.cs b/QuestPDF/Drawing/SpacePlanType.cs new file mode 100644 index 0000000..ea13eb7 --- /dev/null +++ b/QuestPDF/Drawing/SpacePlanType.cs @@ -0,0 +1,9 @@ +namespace QuestPDF.Drawing +{ + public enum SpacePlanType + { + Wrap, + PartialRender, + FullRender + } +} \ No newline at end of file diff --git a/QuestPDF/Elements/Debug.cs b/QuestPDF/Elements/DebugArea.cs similarity index 93% rename from QuestPDF/Elements/Debug.cs rename to QuestPDF/Elements/DebugArea.cs index e1b7e6a..e61bdc2 100644 --- a/QuestPDF/Elements/Debug.cs +++ b/QuestPDF/Elements/DebugArea.cs @@ -5,7 +5,7 @@ using SkiaSharp; namespace QuestPDF.Elements { - internal class Debug : IComponent + internal class DebugArea : IComponent { public IElement? Child { get; set; } @@ -30,7 +30,7 @@ namespace QuestPDF.Elements .Box() .Background(Colors.White) .Padding(2) - .Text(Text, TextStyle.Default.Color(Color).FontType("Consolas").Size(8)); + .Text(Text, TextStyle.Default.Color(Color).FontType(Fonts.Consolas).Size(8)); }); } } diff --git a/QuestPDF/Elements/DebugPointer.cs b/QuestPDF/Elements/DebugPointer.cs new file mode 100644 index 0000000..ea7d342 --- /dev/null +++ b/QuestPDF/Elements/DebugPointer.cs @@ -0,0 +1,10 @@ +using System.ComponentModel; + +namespace QuestPDF.Elements +{ + internal class DebugPointer : Container + { + public string Target { get; set; } + public bool Highlight { get; set; } + } +} \ No newline at end of file diff --git a/QuestPDF/Elements/Decoration.cs b/QuestPDF/Elements/Decoration.cs index 934e972..c946f4b 100644 --- a/QuestPDF/Elements/Decoration.cs +++ b/QuestPDF/Elements/Decoration.cs @@ -11,7 +11,7 @@ namespace QuestPDF.Elements Append } - internal class SimpleDecoration : Element + internal class BinaryDecoration : Element { public Element DecorationElement { get; set; } = Empty.Instance; public Element ContentElement { get; set; } = Empty.Instance; @@ -83,11 +83,11 @@ namespace QuestPDF.Elements public void Compose(IContainer container) { - container.Element(new SimpleDecoration + container.Element(new BinaryDecoration { Type = DecorationType.Prepend, DecorationElement = Header, - ContentElement = new SimpleDecoration + ContentElement = new BinaryDecoration { Type = DecorationType.Append, ContentElement = Content, diff --git a/QuestPDF/Elements/Row.cs b/QuestPDF/Elements/Row.cs index f1e15ca..f779f6c 100644 --- a/QuestPDF/Elements/Row.cs +++ b/QuestPDF/Elements/Row.cs @@ -24,11 +24,6 @@ namespace QuestPDF.Elements Size = size; SetWidth(size); } - - public override string ToString() - { - return $"RowColumn: Constant({Size})"; - } } internal class RelativeRowElement : RowElement @@ -37,14 +32,9 @@ namespace QuestPDF.Elements { Size = size; } - - public override string ToString() - { - return $"RowColumn: Relative({Size})"; - } } - internal class SimpleRow : Element + internal class BinaryRow : Element { internal Element Left { get; set; } internal Element Right { get; set; } @@ -179,7 +169,7 @@ namespace QuestPDF.Elements var half = elements.Length / 2; - return new SimpleRow + return new BinaryRow { Left = BuildTree(elements.Slice(0, half)), Right = BuildTree(elements.Slice(half)) diff --git a/QuestPDF/Elements/Stack.cs b/QuestPDF/Elements/Stack.cs index e917da6..758735a 100644 --- a/QuestPDF/Elements/Stack.cs +++ b/QuestPDF/Elements/Stack.cs @@ -9,7 +9,7 @@ using IContainer = QuestPDF.Infrastructure.IContainer; namespace QuestPDF.Elements { - internal class SimpleStack : Element, IStateResettable + internal class BinaryStack : Element, IStateResettable { internal Element First { get; set; } = Empty.Instance; internal Element Second { get; set; } = Empty.Instance; @@ -135,7 +135,7 @@ namespace QuestPDF.Elements var half = elements.Length / 2; - return new SimpleStack + return new BinaryStack { First = BuildTree(elements.Slice(0, half)), Second = BuildTree(elements.Slice(half)) diff --git a/QuestPDF/Fluent/ComponentExtentions.cs b/QuestPDF/Fluent/ComponentExtentions.cs index fe57ea1..cb68962 100644 --- a/QuestPDF/Fluent/ComponentExtentions.cs +++ b/QuestPDF/Fluent/ComponentExtentions.cs @@ -22,7 +22,7 @@ namespace QuestPDF.Fluent var existingValue = Component.GetPropertyValue(selector); if (existingValue != null) - throw new DocumentComposeException($"The slot {selector.GetPropertyName()} of the component {(typeof( T).Name)} was already used."); + throw new DocumentComposeException($"The slot {selector.GetPropertyName()} of the component {(typeof(T).Name)} was already used."); var slot = new Slot(); Component.SetPropertyValue(selector, slot); @@ -55,7 +55,10 @@ namespace QuestPDF.Fluent { var descriptor = new ComponentDescriptor(component); handler?.Invoke(descriptor); - + + if (System.Diagnostics.Debugger.IsAttached) + element = element.DebugPointer(component.GetType().Name, highlight: false); + component.Compose(element.Container()); } diff --git a/QuestPDF/Fluent/DebugExtensions.cs b/QuestPDF/Fluent/DebugExtensions.cs index a1660cb..5473a35 100644 --- a/QuestPDF/Fluent/DebugExtensions.cs +++ b/QuestPDF/Fluent/DebugExtensions.cs @@ -6,11 +6,11 @@ namespace QuestPDF.Fluent { public static class DebugExtensions { - public static IContainer Debug(this IContainer parent, string text, string color) + public static IContainer DebugArea(this IContainer parent, string text, string color) { var container = new Container(); - parent.Component(new Debug + parent.Component(new DebugArea { Child = container, Text = text, @@ -20,14 +20,32 @@ namespace QuestPDF.Fluent return container; } - public static IContainer Debug(this IContainer parent, string text) + public static IContainer DebugArea(this IContainer parent, string text) { - return parent.Debug(text, Colors.Red.Medium); + return parent.DebugArea(text, Colors.Red.Medium); } - public static IContainer Debug(this IContainer parent) + public static IContainer DebugArea(this IContainer parent) { - return parent.Debug(string.Empty, Colors.Red.Medium); + return parent.DebugArea(string.Empty, Colors.Red.Medium); + } + + /// + /// Creates a virtual element that is visible on the elements trace when the layout overflow exception is thrown. + /// This can be used to easily identify elements inside the elements trace tree and faster find issue root cause. + /// + public static IContainer DebugPointer(this IContainer parent, string elementTraceText) + { + return parent.DebugPointer(elementTraceText, true); + } + + internal static IContainer DebugPointer(this IContainer parent, string elementTraceText, bool highlight) + { + return parent.Element(new DebugPointer + { + Target = elementTraceText, + Highlight = highlight + }); } } } \ No newline at end of file diff --git a/QuestPDF/Helpers/Helpers.cs b/QuestPDF/Helpers/Helpers.cs index 2c6f40a..f610ab2 100644 --- a/QuestPDF/Helpers/Helpers.cs +++ b/QuestPDF/Helpers/Helpers.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq.Expressions; using System.Reflection; +using System.Text.RegularExpressions; namespace QuestPDF.Helpers { @@ -35,5 +36,10 @@ namespace QuestPDF.Helpers var property = selector.ToPropertyInfo() ?? throw new Exception("Expected property with getter and setter."); property?.SetValue(target, value); } + + internal static string PrettifyName(this string text) + { + return Regex.Replace(text, @"([a-z])([A-Z])", "$1 $2"); + } } } \ No newline at end of file diff --git a/QuestPDF/Infrastructure/Element.cs b/QuestPDF/Infrastructure/Element.cs index 6d948a5..a77f3b8 100644 --- a/QuestPDF/Infrastructure/Element.cs +++ b/QuestPDF/Infrastructure/Element.cs @@ -1,4 +1,6 @@ using System; +using System.Collections; +using System.Collections.Generic; using QuestPDF.Drawing; namespace QuestPDF.Infrastructure @@ -27,9 +29,9 @@ namespace QuestPDF.Infrastructure internal abstract SpacePlan Measure(Size availableSpace); internal abstract void Draw(Size availableSpace); - public override string ToString() + protected virtual IEnumerable GetDebugInformation() { - return GetType().Name; + yield break; } } } \ No newline at end of file