Fixed: corner cases of paging support
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<TableCellRenderingCommand> PlanLayout(Size availableSpace)
|
||||
{
|
||||
var cellRenderingCommands = new List<TableCellRenderingCommand>();
|
||||
|
||||
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<int, float>();
|
||||
|
||||
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];
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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<TableCellRenderingCommand> CellRenderingCommands { get; set; }
|
||||
public int MaxRowRendered { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -17,5 +17,7 @@
|
||||
{
|
||||
return new Position(-X, -Y);
|
||||
}
|
||||
|
||||
public override string ToString() => $"(Left: {X:N3}, Top: {Y:N3})";
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,6 @@
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public override string ToString() => $"(Width: {Width}, Height: {Height})";
|
||||
public override string ToString() => $"(Width: {Width:N3}, Height: {Height:N3})";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user