diff --git a/Source/QuestPDF/Elements/Table/Table.cs b/Source/QuestPDF/Elements/Table/Table.cs index c83dc0560..a57576855 100644 --- a/Source/QuestPDF/Elements/Table/Table.cs +++ b/Source/QuestPDF/Elements/Table/Table.cs @@ -12,6 +12,10 @@ internal sealed class Table : Element, IStateResettable, IContentDirectionAware public List Columns { get; set; } = new(); public List Cells { get; set; } = new(); + public List AllCells { get; set; } = new(); + public Dictionary ColumnsWidth { get; set; } = new(); + public Action> AfterUpdateColumnsWidth { get; set; } + public bool ExtendLastCellsToTableBottom { get; set; } private bool CacheInitialized { get; set; } @@ -146,6 +150,15 @@ private int CalculateCurrentRow(ICollection commands) private void UpdateColumnsWidth(float availableWidth) { + if (ColumnsWidth.Any()) + { + foreach (var column in Columns) + { + column.Width = ColumnsWidth[Columns.IndexOf(column)]; + } + return; + } + var constantWidth = Columns.Sum(x => x.ConstantSize); var relativeWidth = Columns.Sum(x => x.RelativeSize); @@ -155,6 +168,64 @@ private void UpdateColumnsWidth(float availableWidth) { column.Width = column.ConstantSize + column.RelativeSize * widthPerRelativeUnit; } + + var cells = AllCells.Where(c => c is { ColumnSpan: 1, RowSpan: 1 }); + + foreach (var column in Columns.Where(c => c.AllowShrink)) + { + var index = Columns.IndexOf(column); + var cellsInColumn = cells.Where(c => c.Column == index + 1); + if (cellsInColumn.Any()) + { + ColumnsWidth.Add(index, cellsInColumn.Max(c => c.Measure(Size.Max).Width)); + column.Width = Math.Min(column.Width, ColumnsWidth[index]); + } + } + + var remainingWidth = availableWidth - Columns.Sum(c => c.Width); + while (remainingWidth > 0) + { + var columnsThatGrow = Columns.Where(c => c.AllowGrow).ToList(); + var growStep = remainingWidth / columnsThatGrow.Count; + var anyColumnHasGrown = false; + foreach (var column in columnsThatGrow) + { + var index = Columns.IndexOf(column); + var cellsInColumn = cells.Where(c => c.Column == index + 1); + if (!ColumnsWidth.ContainsKey(index)) + { + ColumnsWidth.Add(index, cellsInColumn.Max(c => c.Measure(Size.Max).Width)); + } + var newWidth = Math.Min(column.Width + growStep, ColumnsWidth[index]); + if (newWidth > column.Width) + { + anyColumnHasGrown = true; + } + column.Width = newWidth; + remainingWidth = availableWidth - Columns.Sum(c => c.Width); + if (remainingWidth <= 0) + { + break; + } + } + if (!anyColumnHasGrown) + { + break; + } + } + + if (remainingWidth > 0) + { + Columns.Last().Width += remainingWidth; + } + + foreach (var column in Columns) + { + // Add missing columns, that don't auto size. + ColumnsWidth[Columns.IndexOf(column)] = column.Width; + } + + AfterUpdateColumnsWidth?.Invoke(ColumnsWidth); } private ICollection PlanLayout(Size availableSpace) diff --git a/Source/QuestPDF/Elements/Table/TableColumnDefinition.cs b/Source/QuestPDF/Elements/Table/TableColumnDefinition.cs index bc1211561..1dd0b7eca 100644 --- a/Source/QuestPDF/Elements/Table/TableColumnDefinition.cs +++ b/Source/QuestPDF/Elements/Table/TableColumnDefinition.cs @@ -7,10 +7,15 @@ internal sealed class TableColumnDefinition internal float Width { get; set; } - public TableColumnDefinition(float constantSize, float relativeSize) + public bool AllowShrink { get; set; } + public bool AllowGrow { get; set; } + + public TableColumnDefinition(float constantSize, float relativeSize, bool allowShrink, bool allowGrow) { ConstantSize = constantSize; RelativeSize = relativeSize; + AllowShrink = allowShrink; + AllowGrow = allowGrow; } } } \ No newline at end of file diff --git a/Source/QuestPDF/Fluent/TableExtensions.cs b/Source/QuestPDF/Fluent/TableExtensions.cs index f22da1a76..851d4187d 100644 --- a/Source/QuestPDF/Fluent/TableExtensions.cs +++ b/Source/QuestPDF/Fluent/TableExtensions.cs @@ -16,9 +16,9 @@ public class TableColumnsDefinitionDescriptor /// Defines a column of constant size that occupies the specified horizontal space. /// /// The container of the newly created column. - public void ConstantColumn(float width, Unit unit = Unit.Point) + public void ConstantColumn(float width, Unit unit = Unit.Point, bool allowShrink = false, bool allowGrow = false) { - ComplexColumn(constantWidth: width.ToPoints(unit)); + ComplexColumn(allowShrink, allowGrow, constantWidth: width.ToPoints(unit)); } /// @@ -29,14 +29,14 @@ public void ConstantColumn(float width, Unit unit = Unit.Point) /// For a table 100 points wide with three columns: a relative size of 1, a relative size of 5, and a constant size of 10 points, they will span 15 points, 75 points, and 10 points respectively. /// /// The container for the newly defined column. - public void RelativeColumn(float width = 1) + public void RelativeColumn(float width = 1, bool allowShrink = false, bool allowGrow = false) { - ComplexColumn(relativeWidth: width); + ComplexColumn(allowShrink, allowGrow, relativeWidth: width); } - - private void ComplexColumn(float constantWidth = 0, float relativeWidth = 0) + + private void ComplexColumn(bool allowShrink, bool allowGrow, float constantWidth = 0, float relativeWidth = 0) { - var columnDefinition = new TableColumnDefinition(constantWidth, relativeWidth); + var columnDefinition = new TableColumnDefinition(constantWidth, relativeWidth, allowShrink, allowGrow); Columns.Add(columnDefinition); } } @@ -135,6 +135,25 @@ internal IElement CreateElement() { var container = new Container(); + List allTableCells = new(); + var tables = new List() { HeaderTable, ContentTable, FooterTable }; + foreach (var table in tables) + { + allTableCells.AddRange(table.Cells); + table.AfterUpdateColumnsWidth += (columnsWidth) => + { + foreach (var table in tables) + { + table.ColumnsWidth = columnsWidth; + } + }; + } + + foreach (var table in tables) + { + table.AllCells = allTableCells; + } + ConfigureTable(HeaderTable); ConfigureTable(ContentTable); ConfigureTable(FooterTable);