Improved infinite layout debugging experience

This commit is contained in:
Marcin Ziąbek
2021-09-07 00:32:51 +02:00
parent 5e7f2a3adf
commit 6a3bfd855e
25 changed files with 343 additions and 225 deletions
+2 -2
View File
@@ -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);
});
});
+1 -1
View File
@@ -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);
@@ -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);
+7 -7
View File
@@ -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"),
+6 -6
View File
@@ -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")
+15 -15
View File
@@ -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<SimpleStack>(x => x.IsFirstRendered)
.CheckState<BinaryStack>(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<SimpleStack>(x => !x.IsFirstRendered);
.CheckState<BinaryStack>(x => !x.IsFirstRendered);
}
#endregion
-151
View File
@@ -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<DebugStackItem> Stack { get; set; }
public DebuggingState()
{
Reset();
}
public void Reset()
{
Root = null;
Stack = new Stack<DebugStackItem>();
}
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<DebugStackItem> Stack { get; internal set; } = new List<DebugStackItem>();
}
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;
}
}
}
+21 -4
View File
@@ -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)
@@ -4,6 +4,8 @@ namespace QuestPDF.Drawing.Exceptions
{
public class DocumentLayoutException : Exception
{
public string ElementTrace { get; set; }
public DocumentLayoutException()
{
+40
View File
@@ -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;
}
}
}
+14
View File
@@ -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<DebugStackItem> Stack { get; internal set; } = new List<DebugStackItem>();
}
}
+23
View File
@@ -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;
}
}
}
+130
View File
@@ -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<DebugStackItem> Stack { get; set; }
public DebuggingState()
{
Reset();
}
public void Reset()
{
Root = null;
Stack = new Stack<DebugStackItem>();
}
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<string> GetElementConfiguration(IElement element)
{
if (element is DebugPointer)
return Enumerable.Empty<string>();
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<IElement>))
.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() + "...";
}
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.Drawing.Proxy
{
internal class ElementProxy : ContainerElement
{
}
}
+4 -8
View File
@@ -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)
+9
View File
@@ -0,0 +1,9 @@
namespace QuestPDF.Drawing
{
public enum SpacePlanType
{
Wrap,
PartialRender,
FullRender
}
}
@@ -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));
});
}
}
+10
View File
@@ -0,0 +1,10 @@
using System.ComponentModel;
namespace QuestPDF.Elements
{
internal class DebugPointer : Container
{
public string Target { get; set; }
public bool Highlight { get; set; }
}
}
+3 -3
View File
@@ -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,
+2 -12
View File
@@ -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))
+2 -2
View File
@@ -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))
+5 -2
View File
@@ -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<T>(component);
handler?.Invoke(descriptor);
if (System.Diagnostics.Debugger.IsAttached)
element = element.DebugPointer(component.GetType().Name, highlight: false);
component.Compose(element.Container());
}
+24 -6
View File
@@ -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);
}
/// <summary>
/// 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.
/// </summary>
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
});
}
}
}
+6
View File
@@ -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");
}
}
}
+4 -2
View File
@@ -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<string> GetDebugInformation()
{
return GetType().Name;
yield break;
}
}
}