SnapshotRecorder.cs 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. using System;
  2. using System.Collections.Generic;
  3. using QuestPDF.Helpers;
  4. using QuestPDF.Infrastructure;
  5. using QuestPDF.Skia;
  6. namespace QuestPDF.Drawing.Proxy;
  7. internal class SnapshotRecorder : ElementProxy, IDisposable
  8. {
  9. SnapshotRecorderCanvas RecorderCanvas { get; } = new();
  10. SkPictureRecorder PictureRecorder { get; } = new();
  11. Dictionary<(int pageNumber, float availableWidth, float availableHeight), SpacePlan> MeasureCache { get; } = new();
  12. Dictionary<int, SkPicture> DrawCache { get; } = new();
  13. ~SnapshotRecorder()
  14. {
  15. Dispose();
  16. }
  17. public void Dispose()
  18. {
  19. PictureRecorder.Dispose();
  20. foreach (var cacheValue in DrawCache.Values)
  21. cacheValue.Dispose();
  22. GC.SuppressFinalize(this);
  23. }
  24. public SnapshotRecorder(Element child)
  25. {
  26. Child = child;
  27. }
  28. private void Initialize()
  29. {
  30. if (Child.Canvas is SnapshotRecorderCanvas)
  31. return;
  32. Child.VisitChildren(x => x.Canvas = RecorderCanvas);
  33. }
  34. internal override SpacePlan Measure(Size availableSpace)
  35. {
  36. Initialize();
  37. var cacheItem = (PageContext.CurrentPage, availableSpace.Width, availableSpace.Height);
  38. if (MeasureCache.TryGetValue(cacheItem, out var measurement))
  39. return measurement;
  40. var result = base.Measure(availableSpace);
  41. MeasureCache[cacheItem] = result;
  42. return result;
  43. }
  44. internal override void Draw(Size availableSpace)
  45. {
  46. // element may overflow the available space
  47. // capture as much as possible around the origin point
  48. var cachePictureSize = Size.Max;
  49. var cachePictureOffset = new Position(cachePictureSize.Width / 2, cachePictureSize.Height / 2);
  50. if (DrawCache.TryGetValue(PageContext.CurrentPage, out var snapshot))
  51. {
  52. Canvas.Translate(cachePictureOffset.Reverse());
  53. Canvas.DrawPicture(snapshot);
  54. Canvas.Translate(cachePictureOffset);
  55. snapshot.Dispose();
  56. DrawCache.Remove(PageContext.CurrentPage);
  57. return;
  58. }
  59. using var canvas = PictureRecorder.BeginRecording(Size.Max.Width, Size.Max.Height);
  60. RecorderCanvas.Canvas = canvas;
  61. RecorderCanvas.Translate(cachePictureOffset);
  62. base.Draw(availableSpace);
  63. DrawCache[PageContext.CurrentPage] = PictureRecorder.EndRecording();
  64. RecorderCanvas.Canvas = null;
  65. }
  66. }