Column.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using QuestPDF.Drawing;
  5. using QuestPDF.Infrastructure;
  6. namespace QuestPDF.Elements
  7. {
  8. internal sealed class ColumnItem : Container
  9. {
  10. public bool IsRendered { get; set; }
  11. }
  12. internal sealed class ColumnItemRenderingCommand
  13. {
  14. public ColumnItem ColumnItem { get; set; }
  15. public SpacePlan Measurement { get; set; }
  16. public Size Size { get; set; }
  17. public Position Offset { get; set; }
  18. }
  19. internal sealed class Column : Element, ICacheable, IStateResettable
  20. {
  21. internal List<ColumnItem> Items { get; } = new();
  22. internal float Spacing { get; set; }
  23. public void ResetState()
  24. {
  25. Items.ForEach(x => x.IsRendered = false);
  26. }
  27. internal override IEnumerable<Element?> GetChildren()
  28. {
  29. return Items;
  30. }
  31. internal override void CreateProxy(Func<Element?, Element?> create)
  32. {
  33. Items.ForEach(x => x.Child = create(x.Child));
  34. }
  35. internal override SpacePlan Measure(Size availableSpace)
  36. {
  37. if (!Items.Any())
  38. return SpacePlan.FullRender(Size.Zero);
  39. var renderingCommands = PlanLayout(availableSpace);
  40. if (!renderingCommands.Any())
  41. return SpacePlan.Wrap();
  42. var width = renderingCommands.Max(x => x.Size.Width);
  43. var height = renderingCommands.Last().Offset.Y + renderingCommands.Last().Size.Height;
  44. var size = new Size(width, height);
  45. if (width > availableSpace.Width + Size.Epsilon || height > availableSpace.Height + Size.Epsilon)
  46. return SpacePlan.Wrap();
  47. var totalRenderedItems = Items.Count(x => x.IsRendered) + renderingCommands.Count(x => x.Measurement.Type == SpacePlanType.FullRender);
  48. var willBeFullyRendered = totalRenderedItems == Items.Count;
  49. return willBeFullyRendered
  50. ? SpacePlan.FullRender(size)
  51. : SpacePlan.PartialRender(size);
  52. }
  53. internal override void Draw(Size availableSpace)
  54. {
  55. var renderingCommands = PlanLayout(availableSpace);
  56. foreach (var command in renderingCommands)
  57. {
  58. if (command.Measurement.Type == SpacePlanType.FullRender)
  59. command.ColumnItem.IsRendered = true;
  60. var targetSize = new Size(availableSpace.Width, command.Size.Height);
  61. Canvas.Translate(command.Offset);
  62. command.ColumnItem.Draw(targetSize);
  63. Canvas.Translate(command.Offset.Reverse());
  64. }
  65. if (Items.All(x => x.IsRendered))
  66. ResetState();
  67. }
  68. private ICollection<ColumnItemRenderingCommand> PlanLayout(Size availableSpace)
  69. {
  70. var topOffset = 0f;
  71. var targetWidth = 0f;
  72. var commands = new List<ColumnItemRenderingCommand>();
  73. foreach (var item in Items)
  74. {
  75. if (item.IsRendered)
  76. continue;
  77. var availableHeight = availableSpace.Height - topOffset;
  78. if (availableHeight < -Size.Epsilon)
  79. break;
  80. var itemSpace = new Size(availableSpace.Width, availableHeight);
  81. var measurement = item.Measure(itemSpace);
  82. if (measurement.Type == SpacePlanType.Wrap)
  83. break;
  84. commands.Add(new ColumnItemRenderingCommand
  85. {
  86. ColumnItem = item,
  87. Size = measurement,
  88. Measurement = measurement,
  89. Offset = new Position(0, topOffset)
  90. });
  91. if (measurement.Width > targetWidth)
  92. targetWidth = measurement.Width;
  93. if (measurement.Type == SpacePlanType.PartialRender)
  94. break;
  95. topOffset += measurement.Height + Spacing;
  96. }
  97. foreach (var command in commands)
  98. command.Size = new Size(targetWidth, command.Size.Height);
  99. return commands;
  100. }
  101. }
  102. }