From 1c9bccbeb2444da508bdb2d5298c218404b4dd8f Mon Sep 17 00:00:00 2001 From: MarcinZiabek Date: Mon, 17 Apr 2023 02:08:04 +0200 Subject: [PATCH] Feature: added support for shared/global images --- Source/QuestPDF.Examples/ImageExamples.cs | 50 +++++++----------- Source/QuestPDF.UnitTests/ImageTests.cs | 9 ++-- Source/QuestPDF/Elements/Image.cs | 9 ++-- Source/QuestPDF/Fluent/ImageExtensions.cs | 13 +++-- Source/QuestPDF/Infrastructure/Image.cs | 64 +++++++++++++++++++++++ 5 files changed, 99 insertions(+), 46 deletions(-) create mode 100644 Source/QuestPDF/Infrastructure/Image.cs diff --git a/Source/QuestPDF.Examples/ImageExamples.cs b/Source/QuestPDF.Examples/ImageExamples.cs index 91bf3af..27c97ca 100644 --- a/Source/QuestPDF.Examples/ImageExamples.cs +++ b/Source/QuestPDF.Examples/ImageExamples.cs @@ -1,10 +1,12 @@ using System; using System.IO; +using System.Linq; using NUnit.Framework; using QuestPDF.Drawing.Exceptions; using QuestPDF.Examples.Engine; using QuestPDF.Fluent; using QuestPDF.Helpers; +using QuestPDF.Infrastructure; namespace QuestPDF.Examples { @@ -67,39 +69,25 @@ namespace QuestPDF.Examples [Test] public void ReusingTheSameImageFileShouldBePossible() { - var fileName = Path.GetTempFileName() + ".jpg"; - - try - { - var image = Placeholders.Image(300, 100); + var image = Image.FromBinaryData(Placeholders.Image(300, 100)).DisposeAfterDocumentGeneration(); - using var file = File.Create(fileName); - file.Write(image); - file.Dispose(); - - RenderingTest - .Create() - .ProducePdf() - .PageSize(PageSizes.A4) - .ShowResults() - .Render(container => - { - container - .Padding(20) - .Column(column => - { - column.Spacing(20); + RenderingTest + .Create() + .ProducePdf() + .PageSize(PageSizes.A4) + .ShowResults() + .Render(container => + { + container + .Padding(20) + .Column(column => + { + column.Spacing(20); - column.Item().Image(fileName); - column.Item().Image(fileName); - column.Item().Image(fileName); - }); - }); - } - finally - { - File.Delete(fileName); - } + foreach (var i in Enumerable.Range(0, 1000)) + column.Item().Image(image); + }); + }); } } } \ No newline at end of file diff --git a/Source/QuestPDF.UnitTests/ImageTests.cs b/Source/QuestPDF.UnitTests/ImageTests.cs index 785abb4..b4db73b 100644 --- a/Source/QuestPDF.UnitTests/ImageTests.cs +++ b/Source/QuestPDF.UnitTests/ImageTests.cs @@ -5,6 +5,7 @@ using QuestPDF.Fluent; using QuestPDF.Infrastructure; using QuestPDF.UnitTests.TestEngine; using SkiaSharp; +using Image = QuestPDF.Infrastructure.Image; namespace QuestPDF.UnitTests { @@ -15,9 +16,9 @@ namespace QuestPDF.UnitTests public void Measure_TakesAvailableSpaceRegardlessOfSize() { TestPlan - .For(x => new Image + .For(x => new QuestPDF.Elements.Image { - InternalImage = GenerateImage(400, 300) + DocumentImage = Image.FromSkImage(GenerateImage(400, 300)) }) .MeasureElement(new Size(300, 200)) .CheckMeasureResult(SpacePlan.FullRender(300, 200)); @@ -27,9 +28,9 @@ namespace QuestPDF.UnitTests public void Draw_TakesAvailableSpaceRegardlessOfSize() { TestPlan - .For(x => new Image + .For(x => new QuestPDF.Elements.Image { - InternalImage = GenerateImage(400, 300) + DocumentImage = Image.FromSkImage(GenerateImage(400, 300)) }) .DrawElement(new Size(300, 200)) .ExpectCanvasDrawImage(new Position(0, 0), new Size(300, 200)) diff --git a/Source/QuestPDF/Elements/Image.cs b/Source/QuestPDF/Elements/Image.cs index e38475c..74ba5a6 100644 --- a/Source/QuestPDF/Elements/Image.cs +++ b/Source/QuestPDF/Elements/Image.cs @@ -7,11 +7,12 @@ namespace QuestPDF.Elements { internal class Image : Element, ICacheable { - public SKImage? InternalImage { get; set; } + public Infrastructure.Image? DocumentImage { get; set; } ~Image() { - InternalImage?.Dispose(); + if (DocumentImage is { IsDocumentScoped: true }) + DocumentImage?.Dispose(); } internal override SpacePlan Measure(Size availableSpace) @@ -23,10 +24,10 @@ namespace QuestPDF.Elements internal override void Draw(Size availableSpace) { - if (InternalImage == null) + if (DocumentImage == null) return; - Canvas.DrawImage(InternalImage, Position.Zero, availableSpace); + Canvas.DrawImage(DocumentImage.SkImage, Position.Zero, availableSpace); } } } \ No newline at end of file diff --git a/Source/QuestPDF/Fluent/ImageExtensions.cs b/Source/QuestPDF/Fluent/ImageExtensions.cs index 7d46a26..bc820d9 100644 --- a/Source/QuestPDF/Fluent/ImageExtensions.cs +++ b/Source/QuestPDF/Fluent/ImageExtensions.cs @@ -3,7 +3,6 @@ using System.IO; using QuestPDF.Drawing.Exceptions; using QuestPDF.Elements; using QuestPDF.Infrastructure; -using SkiaSharp; namespace QuestPDF.Fluent { @@ -11,30 +10,30 @@ namespace QuestPDF.Fluent { public static void Image(this IContainer parent, byte[] imageData, ImageScaling scaling = ImageScaling.FitWidth) { - var image = SKImage.FromEncodedData(imageData); + var image = Infrastructure.Image.FromBinaryData(imageData).DisposeAfterDocumentGeneration(); parent.Image(image, scaling); } public static void Image(this IContainer parent, string filePath, ImageScaling scaling = ImageScaling.FitWidth) { - var image = SKImage.FromEncodedData(filePath); + var image = Infrastructure.Image.FromFile(filePath).DisposeAfterDocumentGeneration(); parent.Image(image, scaling); } public static void Image(this IContainer parent, Stream fileStream, ImageScaling scaling = ImageScaling.FitWidth) { - var image = SKImage.FromEncodedData(fileStream); + var image = Infrastructure.Image.FromStream(fileStream).DisposeAfterDocumentGeneration(); parent.Image(image, scaling); } - private static void Image(this IContainer parent, SKImage image, ImageScaling scaling = ImageScaling.FitWidth) + public static void Image(this IContainer parent, Infrastructure.Image image, ImageScaling scaling = ImageScaling.FitWidth) { if (image == null) throw new DocumentComposeException("Cannot load or decode provided image."); - var imageElement = new Image + var imageElement = new QuestPDF.Elements.Image { - InternalImage = image + DocumentImage = image }; if (scaling != ImageScaling.Resize) diff --git a/Source/QuestPDF/Infrastructure/Image.cs b/Source/QuestPDF/Infrastructure/Image.cs new file mode 100644 index 0000000..64f5906 --- /dev/null +++ b/Source/QuestPDF/Infrastructure/Image.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using QuestPDF.Drawing.Exceptions; +using SkiaSharp; + +namespace QuestPDF.Infrastructure +{ + public class Image : IDisposable + { + internal SKImage SkImage { get; } + internal bool IsDocumentScoped { get; set; } + + public int Width => SkImage.Width; + public int Height => SkImage.Height; + + private Image(SKImage image) + { + SkImage = image; + } + + public Image DisposeAfterDocumentGeneration() + { + IsDocumentScoped = true; + return this; + } + + public void Dispose() + { + SkImage.Dispose(); + } + + #region public constructors + + internal static Image FromSkImage(SKImage image) + { + return CreateImage(image); + } + + public static Image FromBinaryData(byte[] imageData) + { + return CreateImage(SKImage.FromEncodedData(imageData)); + } + + public static Image FromFile(string filePath) + { + return CreateImage(SKImage.FromEncodedData(filePath)); + } + + public static Image FromStream(Stream fileStream) + { + return CreateImage(SKImage.FromEncodedData(fileStream)); + } + + private static Image CreateImage(SKImage? image) + { + if (image == null) + throw new DocumentComposeException("Cannot load or decode provided image."); + + return new Image(image); + } + + #endregion + } +} \ No newline at end of file