From 2fcf54381784f57fbffb67f1374f3c2565c138ee Mon Sep 17 00:00:00 2001 From: MarcinZiabek Date: Thu, 30 Dec 2021 18:40:54 +0100 Subject: [PATCH] Fixed: corner cases of paging support --- QuestPDF.Examples/FrameExample.cs | 6 +- QuestPDF.Examples/TableExamples.cs | 8 +- QuestPDF/Elements/Table/Table.cs | 103 ++++++++++++------ .../Table/TableCellRenderingCommand.cs | 2 + QuestPDF/Elements/Table/TableRenderingPlan.cs | 12 -- QuestPDF/Elements/Text/TextBlock.cs | 2 +- QuestPDF/Helpers/Placeholders.cs | 2 +- QuestPDF/Infrastructure/Position.cs | 2 + QuestPDF/Infrastructure/Size.cs | 2 +- 9 files changed, 84 insertions(+), 55 deletions(-) delete mode 100644 QuestPDF/Elements/Table/TableRenderingPlan.cs diff --git a/QuestPDF.Examples/FrameExample.cs b/QuestPDF.Examples/FrameExample.cs index c77bfed..29cfa1d 100644 --- a/QuestPDF.Examples/FrameExample.cs +++ b/QuestPDF.Examples/FrameExample.cs @@ -12,9 +12,9 @@ namespace QuestPDF.Examples { return container .Border(1) - .Background(dark ? Colors.Grey.Lighten2 : Colors.White) - .Padding(5) - .EnsureSpace(10); + .BorderColor(Colors.Grey.Medium) + .Background(dark ? Colors.Grey.Lighten3 : Colors.White) + .Padding(5); } public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.SemiBold()); diff --git a/QuestPDF.Examples/TableExamples.cs b/QuestPDF.Examples/TableExamples.cs index 3f62bde..9d952b9 100644 --- a/QuestPDF.Examples/TableExamples.cs +++ b/QuestPDF.Examples/TableExamples.cs @@ -117,15 +117,17 @@ namespace QuestPDF.Examples .EnableCaching() .EnableDebugging(false) .ShowResults() - .Render(container => GeneratePerformanceStructure(container, 250)); + .Render(container => GeneratePerformanceStructure(container, 2500)); } public static void GeneratePerformanceStructure(IContainer container, int repeats) { container .Padding(25) + .Background(Colors.Blue.Lighten2) .Box() - .Border(2) + .Border(1) + .Background(Colors.Red.Lighten2) .Table(table => { table.ColumnsDefinition(columns => @@ -139,7 +141,7 @@ namespace QuestPDF.Examples foreach (var i in Enumerable.Range(0, repeats)) { table.Cell().RowSpan(3).LabelCell("Project"); - table.Cell().RowSpan(3).ValueCell(Placeholders.Sentence()); + table.Cell().RowSpan(3).ShowEntire().ValueCell(Placeholders.Sentence()); table.Cell().LabelCell("Report number"); table.Cell().ValueCell(i.ToString()); diff --git a/QuestPDF/Elements/Table/Table.cs b/QuestPDF/Elements/Table/Table.cs index 71ac21d..029aed8 100644 --- a/QuestPDF/Elements/Table/Table.cs +++ b/QuestPDF/Elements/Table/Table.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using QuestPDF.Drawing; using QuestPDF.Infrastructure; @@ -75,30 +76,64 @@ namespace QuestPDF.Elements.Table { UpdateColumnsWidth(availableSpace.Width); - var layout = PlanLayout(availableSpace); + var renderingCommands = PlanLayout(availableSpace).ToList(); - return layout.MaxRowRendered < RowsCount - ? SpacePlan.PartialRender(layout.Size) - : SpacePlan.FullRender(layout.Size); + if (!renderingCommands.Any()) + return SpacePlan.Wrap(); + + var lastFullyRenderedRow = renderingCommands + .GroupBy(x => x.Cell.Row) + .Where(x => x.All(y => y.Measurement.Type == SpacePlanType.FullRender)) + .Select(x => x.Key) + .DefaultIfEmpty(0) + .Max(); + + var width = Columns.Sum(x => x.Width); + var height = renderingCommands.Max(x => x.Offset.Y + x.Size.Height); + + if (width > availableSpace.Width + Size.Epsilon) + return SpacePlan.Wrap(); + + var tableSize = new Size(width, height); + + var result = lastFullyRenderedRow == RowsCount + ? SpacePlan.FullRender(tableSize) + : SpacePlan.PartialRender(tableSize); + + var x = renderingCommands.Select(x => $"{x.Cell.Row} {x.Cell.Column} {x.Offset} {x.Size} {x.Measurement}"); + var res = string.Join("\n", x); + + Console.WriteLine($"Measure: {availableSpace} / {result}"); + return result; } internal override void Draw(Size availableSpace) { UpdateColumnsWidth(availableSpace.Width); - var layout = PlanLayout(availableSpace); - CurrentRow = layout.MaxRowRendered; - - foreach (var command in layout.CellRenderingCommands) - { - var measurementResult = command.Cell.Measure(command.Size); + var renderingCommands = PlanLayout(new Size(availableSpace.Width, availableSpace.Height + 1)).ToList(); - if (measurementResult.Type == SpacePlanType.FullRender) + foreach (var command in renderingCommands) + { + if (command.Measurement.Type == SpacePlanType.FullRender) command.Cell.IsRendered = true; Canvas.Translate(command.Offset); command.Cell.Draw(command.Size); Canvas.Translate(command.Offset.Reverse()); } + + CurrentRow = renderingCommands + .GroupBy(x => x.Cell.Row) + .Where(x => x.All(y => y.Measurement.Type == SpacePlanType.FullRender)) + .Select(x => x.Key) + .DefaultIfEmpty(0) + .Max() + 1; + + var x = renderingCommands.Select(x => $"{x.Cell.Row} {x.Cell.Column} {x.Offset} {x.Size} {x.Measurement}"); + var res = string.Join("\n", x); + + var height = renderingCommands.Max(x => x.Offset.Y + x.Size.Height); + Console.WriteLine($"Draw: {availableSpace} / height {height}"); } private void UpdateColumnsWidth(float availableWidth) @@ -113,11 +148,9 @@ namespace QuestPDF.Elements.Table column.Width = column.ConstantSize + column.RelativeSize * widthPerRelativeUnit; } } - - private TableRenderingPlan PlanLayout(Size availableSpace) + + private IEnumerable PlanLayout(Size availableSpace) { - var cellRenderingCommands = new List(); - var cellOffsets = new float[Columns.Count + 1]; cellOffsets[0] = 0; @@ -135,22 +168,25 @@ namespace QuestPDF.Elements.Table foreach (var child in childrenToTry) { - if (child.Row > maxRenderingRow) - break; - if (child.Row > currentRow) { rowBottomOffsets[currentRow] = Math.Max(rowBottomOffsets[currentRow], rowBottomOffsets[currentRow - 1]); - if (rowBottomOffsets[currentRow - 1] > availableSpace.Height + Single.Epsilon) + if (rowBottomOffsets[currentRow - 1] > availableSpace.Height + Size.Epsilon) break; currentRow = child.Row; } - var topOffset = rowBottomOffsets[child.Row - 1]; - var availableHeight = availableSpace.Height - topOffset; + if (child.Row > maxRenderingRow) + break; + if (child.IsRendered) + continue; + + var topOffset = rowBottomOffsets[child.Row - 1]; + var availableHeight = availableSpace.Height - topOffset + Size.Epsilon; + var cellSize = GetCellSize(child, availableHeight); if (cellSize.Type == SpacePlanType.PartialRender) @@ -161,6 +197,7 @@ namespace QuestPDF.Elements.Table if (cellSize.Type == SpacePlanType.Wrap) { maxRenderingRow = Math.Min(maxRenderingRow, child.Row - 1); + continue; } var cellBottomOffset = cellSize.Height + topOffset; @@ -169,6 +206,8 @@ namespace QuestPDF.Elements.Table rowBottomOffsets[targetRowId] = Math.Max(rowBottomOffsets[targetRowId], cellBottomOffset); } + Console.WriteLine($"{CurrentRow} / {maxRenderingRow}"); + var rowHeights = new DynamicDictionary(); Enumerable @@ -177,9 +216,6 @@ namespace QuestPDF.Elements.Table .ForEach(x => rowHeights[x] = rowBottomOffsets[x] - rowBottomOffsets[x-1]); // find rows count to render in this pass - var totalHeight = rowHeights.Items.Where(x => x.Key <= maxRenderingRow).Sum(x => x.Value); - var totalWidth = Columns.Sum(x => x.Width); - var childrenToDraw = Enumerable .Range(CurrentRow, maxRenderingRow - CurrentRow + 1) .SelectMany(x => OrderedChildren[x]); @@ -190,23 +226,22 @@ namespace QuestPDF.Elements.Table var topOffset = cell.Row == 1 ? 0 : rowBottomOffsets[cell.Row - 1]; var width = GetCellWidth(cell); - var height = Enumerable.Range(cell.Row, cell.RowSpan).Select(x => rowHeights[x]).Sum(); + var height = Enumerable.Range(cell.Row, cell.RowSpan).Where(x => x <= maxRenderingRow).Select(x => rowHeights[x]).Sum(); - cellRenderingCommands.Add(new TableCellRenderingCommand() + var measurement = GetCellSize(cell, height); + + if (measurement.Type == SpacePlanType.Wrap) + continue; + + yield return new TableCellRenderingCommand() { Cell = cell, + Measurement = measurement, Size = new Size(width, height), Offset = new Position(leftOffset, topOffset) - }); + }; } - return new TableRenderingPlan - { - Size = new Size(totalWidth, totalHeight), - CellRenderingCommands = cellRenderingCommands, - MaxRowRendered = maxRenderingRow - }; - float GetCellWidth(TableCell cell) { return cellOffsets[cell.Column + cell.ColumnSpan - 1] - cellOffsets[cell.Column - 1]; diff --git a/QuestPDF/Elements/Table/TableCellRenderingCommand.cs b/QuestPDF/Elements/Table/TableCellRenderingCommand.cs index b74114e..46d8630 100644 --- a/QuestPDF/Elements/Table/TableCellRenderingCommand.cs +++ b/QuestPDF/Elements/Table/TableCellRenderingCommand.cs @@ -1,3 +1,4 @@ +using QuestPDF.Drawing; using QuestPDF.Infrastructure; namespace QuestPDF.Elements.Table @@ -5,6 +6,7 @@ namespace QuestPDF.Elements.Table internal class TableCellRenderingCommand { public TableCell Cell { get; set; } + public SpacePlan Measurement { get; set; } public Size Size { get; set; } public Position Offset { get; set; } } diff --git a/QuestPDF/Elements/Table/TableRenderingPlan.cs b/QuestPDF/Elements/Table/TableRenderingPlan.cs deleted file mode 100644 index 009da09..0000000 --- a/QuestPDF/Elements/Table/TableRenderingPlan.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using QuestPDF.Infrastructure; - -namespace QuestPDF.Elements.Table -{ - internal class TableRenderingPlan - { - public Size Size { get; set; } - public List CellRenderingCommands { get; set; } - public int MaxRowRendered { get; set; } - } -} \ No newline at end of file diff --git a/QuestPDF/Elements/Text/TextBlock.cs b/QuestPDF/Elements/Text/TextBlock.cs index b2953a7..3143976 100644 --- a/QuestPDF/Elements/Text/TextBlock.cs +++ b/QuestPDF/Elements/Text/TextBlock.cs @@ -32,7 +32,7 @@ namespace QuestPDF.Elements.Text var lines = DivideTextItemsIntoLines(availableSpace.Width, availableSpace.Height).ToList(); if (!lines.Any()) - return SpacePlan.PartialRender(Size.Zero); + return SpacePlan.Wrap(); var width = lines.Max(x => x.Width); var height = lines.Sum(x => x.LineHeight); diff --git a/QuestPDF/Helpers/Placeholders.cs b/QuestPDF/Helpers/Placeholders.cs index bd11a6a..6a6d5b5 100644 --- a/QuestPDF/Helpers/Placeholders.cs +++ b/QuestPDF/Helpers/Placeholders.cs @@ -7,7 +7,7 @@ namespace QuestPDF.Helpers { public static class Placeholders { - public static readonly Random Random = new Random(); + public static readonly Random Random = new Random(3); // 3 #region Word Cache diff --git a/QuestPDF/Infrastructure/Position.cs b/QuestPDF/Infrastructure/Position.cs index 9cb8b1a..2a28972 100644 --- a/QuestPDF/Infrastructure/Position.cs +++ b/QuestPDF/Infrastructure/Position.cs @@ -17,5 +17,7 @@ { return new Position(-X, -Y); } + + public override string ToString() => $"(Left: {X:N3}, Top: {Y:N3})"; } } \ No newline at end of file diff --git a/QuestPDF/Infrastructure/Size.cs b/QuestPDF/Infrastructure/Size.cs index 0636149..53758a9 100644 --- a/QuestPDF/Infrastructure/Size.cs +++ b/QuestPDF/Infrastructure/Size.cs @@ -17,6 +17,6 @@ Height = height; } - public override string ToString() => $"(Width: {Width}, Height: {Height})"; + public override string ToString() => $"(Width: {Width:N3}, Height: {Height:N3})"; } } \ No newline at end of file