Decoration.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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 DecorationItemRenderingCommand
  9. {
  10. public Element Element { get; set; }
  11. public SpacePlan Measurement { get; set; }
  12. public Position Offset { get; set; }
  13. }
  14. internal sealed class Decoration : Element, ICacheable, IContentDirectionAware
  15. {
  16. public ContentDirection ContentDirection { get; set; }
  17. internal Element Before { get; set; } = new Empty();
  18. internal Element Content { get; set; } = new Empty();
  19. internal Element After { get; set; } = new Empty();
  20. internal override IEnumerable<Element?> GetChildren()
  21. {
  22. yield return Before;
  23. yield return Content;
  24. yield return After;
  25. }
  26. internal override void CreateProxy(Func<Element?, Element?> create)
  27. {
  28. Before = create(Before);
  29. Content = create(Content);
  30. After = create(After);
  31. }
  32. internal override SpacePlan Measure(Size availableSpace)
  33. {
  34. var renderingCommands = PlanLayout(availableSpace).ToList();
  35. if (renderingCommands.Any(x => x.Measurement.Type == SpacePlanType.Wrap))
  36. return SpacePlan.Wrap();
  37. var width = renderingCommands.Max(x => x.Measurement.Width);
  38. var height = renderingCommands.Sum(x => x.Measurement.Height);
  39. var size = new Size(width, height);
  40. if (width > availableSpace.Width + Size.Epsilon || height > availableSpace.Height + Size.Epsilon)
  41. return SpacePlan.Wrap();
  42. var willBeFullyRendered = renderingCommands.All(x => x.Measurement.Type == SpacePlanType.FullRender);
  43. return willBeFullyRendered
  44. ? SpacePlan.FullRender(size)
  45. : SpacePlan.PartialRender(size);
  46. }
  47. internal override void Draw(Size availableSpace)
  48. {
  49. var renderingCommands = PlanLayout(availableSpace).ToList();
  50. var width = renderingCommands.Max(x => x.Measurement.Width);
  51. foreach (var command in renderingCommands)
  52. {
  53. var elementSize = new Size(width, command.Measurement.Height);
  54. var offset = ContentDirection == ContentDirection.LeftToRight
  55. ? command.Offset
  56. : new Position(availableSpace.Width - width, command.Offset.Y);
  57. Canvas.Translate(offset);
  58. command.Element.Draw(elementSize);
  59. Canvas.Translate(offset.Reverse());
  60. }
  61. }
  62. private IEnumerable<DecorationItemRenderingCommand> PlanLayout(Size availableSpace)
  63. {
  64. SpacePlan GetDecorationMeasurement(Element element)
  65. {
  66. var measurement = element.Measure(availableSpace);
  67. return measurement.Type == SpacePlanType.FullRender
  68. ? measurement
  69. : SpacePlan.Wrap();
  70. }
  71. var beforeMeasurement = GetDecorationMeasurement(Before);
  72. var afterMeasurement = GetDecorationMeasurement(After);
  73. var contentSpace = new Size(availableSpace.Width, availableSpace.Height - beforeMeasurement.Height - afterMeasurement.Height);
  74. var contentMeasurement = Content.Measure(contentSpace);
  75. yield return new DecorationItemRenderingCommand
  76. {
  77. Element = Before,
  78. Measurement = beforeMeasurement,
  79. Offset = Position.Zero
  80. };
  81. yield return new DecorationItemRenderingCommand
  82. {
  83. Element = Content,
  84. Measurement = contentMeasurement,
  85. Offset = new Position(0, beforeMeasurement.Height)
  86. };
  87. yield return new DecorationItemRenderingCommand
  88. {
  89. Element = After,
  90. Measurement = afterMeasurement,
  91. Offset = new Position(0, beforeMeasurement.Height + contentMeasurement.Height)
  92. };
  93. }
  94. }
  95. }