LayoutOverflowVisualization.cs 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. using System;
  2. using QuestPDF.Helpers;
  3. using QuestPDF.Infrastructure;
  4. using QuestPDF.Skia;
  5. namespace QuestPDF.Drawing.Proxy;
  6. internal sealed class LayoutOverflowVisualization : ElementProxy, IContentDirectionAware
  7. {
  8. private const float BorderThickness = 1.5f;
  9. private readonly Color LineColor = Colors.Red.Medium;
  10. private readonly Color AvailableAreaColor = Colors.Green.Medium;
  11. private const byte AreaOpacity = 64;
  12. public ContentDirection ContentDirection { get; set; }
  13. internal override SpacePlan Measure(Size availableSpace)
  14. {
  15. if (Size.Equal(availableSpace, Size.Zero))
  16. return SpacePlan.Wrap("There is no available space.");
  17. var childSize = base.Measure(availableSpace);
  18. if (childSize.Type == SpacePlanType.FullRender)
  19. return childSize;
  20. var minimalSize = Child.TryMeasureWithOverflow(availableSpace);
  21. if (minimalSize.Type is SpacePlanType.Wrap)
  22. return minimalSize;
  23. var width = Math.Min(availableSpace.Width, minimalSize.Width);
  24. var height = Math.Min(availableSpace.Height, minimalSize.Height);
  25. return new SpacePlan(minimalSize.Type, width, height);
  26. }
  27. internal override void Draw(Size availableSpace)
  28. {
  29. // measure content area
  30. var childSize = base.Measure(availableSpace);
  31. if (childSize.Type is SpacePlanType.Empty or SpacePlanType.FullRender)
  32. {
  33. Child?.Draw(availableSpace);
  34. return;
  35. }
  36. Canvas = Child.Canvas;
  37. if (Canvas is SkiaCanvasBase skiaCanvasBase)
  38. skiaCanvasBase.MarkCurrentPageAsHavingLayoutIssues();
  39. // check overflow area
  40. var contentArea = Child.TryMeasureWithOverflow(availableSpace);
  41. var contentSize = contentArea.Type is SpacePlanType.Wrap
  42. ? Size.Max
  43. : contentArea;
  44. // draw content
  45. var translate = ContentDirection == ContentDirection.RightToLeft
  46. ? new Position(availableSpace.Width - contentSize.Width, 0)
  47. : Position.Zero;
  48. Canvas.Translate(translate);
  49. Child?.Draw(contentSize);
  50. Canvas.Translate(translate.Reverse());
  51. // draw overflow area
  52. var overflowTranslate = ContentDirection == ContentDirection.RightToLeft ? new Position(availableSpace.Width, 0) : Position.Zero;
  53. var overflowScale = ContentDirection == ContentDirection.RightToLeft ? -1 : 1;
  54. Canvas.Translate(overflowTranslate);
  55. Canvas.Scale(overflowScale, 1);
  56. DrawOverflowArea(availableSpace, contentSize);
  57. Canvas.Scale(overflowScale, 1);
  58. Canvas.Translate(overflowTranslate.Reverse());
  59. }
  60. private void DrawOverflowArea(Size availableSpace, Size contentSize)
  61. {
  62. var availableSpaceColor = AvailableAreaColor.WithAlpha(AreaOpacity);
  63. Canvas.DrawFilledRectangle(Position.Zero, availableSpace, availableSpaceColor);
  64. Canvas.Save();
  65. Canvas.ClipOverflowArea(new SkRect(0, 0, availableSpace.Width, availableSpace.Height), new SkRect(0, 0, contentSize.Width, contentSize.Height));
  66. Canvas.DrawOverflowArea(new SkRect(0, 0, contentSize.Width, contentSize.Height));
  67. Canvas.Restore();
  68. Canvas.DrawStrokeRectangle(Position.Zero, contentSize, BorderThickness, LineColor);
  69. }
  70. }