Kaynağa Gözat

Improved image handling

MarcinZiabek 2 yıl önce
ebeveyn
işleme
bdb61aa545

+ 3 - 3
Source/QuestPDF.UnitTests/DynamicImageTests.cs

@@ -51,7 +51,7 @@ namespace QuestPDF.UnitTests
         [Test]
         public void Draw_PassesCorrectSizeToSource()
         {
-            Size passedSize = default;
+            ImageSize passedSize = default;
 
             TestPlan
                 .For(x => new DynamicImage
@@ -69,9 +69,9 @@ namespace QuestPDF.UnitTests
             passedSize.Should().BeEquivalentTo(new Size(400, 300));
         }
         
-        byte[] GenerateImage(Size size)
+        byte[] GenerateImage(ImageSize size)
         {
-            var image = GenerateImage((int) size.Width, (int) size.Height);
+            var image = GenerateImage(size.Width, size.Height);
             return image.Encode(SKEncodedImageFormat.Png, 100).ToArray();
         }
         

+ 12 - 7
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -232,13 +232,18 @@ namespace QuestPDF.Drawing
         {
             content.VisitChildren(x =>
             {
-                if (x is not QuestPDF.Elements.Image { DocumentImage: { } image })
-                    return;
-                
-                image.TargetDpi ??= settings.RasterDpi;
-                image.ScalingQuality ??= settings.ImageScalingQuality;
-                image.ScalingStrategy ??= settings.ImageScalingStrategy;
-                image.CompressionQuality ??= settings.ImageCompressionQuality;
+                if (x is QuestPDF.Elements.Image image)
+                {
+                    image.TargetDpi ??= settings.RasterDpi;
+                    image.ResizeStrategy ??= settings.ImageResizeStrategy;
+                    image.CompressionQuality ??= settings.ImageCompressionQuality;
+                }
+
+                if (x is QuestPDF.Elements.DynamicImage dynamicImage)
+                {
+                    dynamicImage.TargetDpi ??= settings.RasterDpi;
+                    dynamicImage.CompressionQuality ??= settings.ImageCompressionQuality;
+                }
             });
         }
 

+ 7 - 1
Source/QuestPDF/Elements/AspectRatio.cs

@@ -8,11 +8,14 @@ namespace QuestPDF.Elements
     {
         public ContentDirection ContentDirection { get; set; }
         
-        public float Ratio { get; set; } = 1;
+        public float Ratio { get; set; }
         public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth;
         
         internal override SpacePlan Measure(Size availableSpace)
         {
+            if (Ratio == 0)
+                return SpacePlan.FullRender(0, 0);
+            
             if(Child == null)
                 return SpacePlan.FullRender(0, 0);
             
@@ -56,6 +59,9 @@ namespace QuestPDF.Elements
         
         private Size GetTargetSize(Size availableSpace)
         {
+            if (Ratio == 0)
+                return availableSpace;
+            
             var spaceRatio = availableSpace.Width / availableSpace.Height;
 
             var fitHeight = new Size(availableSpace.Height * Ratio, availableSpace.Height) ;

+ 18 - 2
Source/QuestPDF/Elements/DynamicImage.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Security;
 using QuestPDF.Drawing;
 using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
@@ -6,9 +7,13 @@ using SkiaSharp;
 
 namespace QuestPDF.Elements
 {
+    public delegate byte[] GenerateDynamicImageDelegate(ImageSize size);
+    
     internal class DynamicImage : Element
     {
-        public Func<Size, byte[]>? Source { get; set; }
+        internal int? TargetDpi { get; set; }
+        internal ImageCompressionQuality? CompressionQuality { get; set; }
+        public GenerateDynamicImageDelegate? Source { get; set; }
         
         internal override SpacePlan Measure(Size availableSpace)
         {
@@ -19,7 +24,8 @@ namespace QuestPDF.Elements
 
         internal override void Draw(Size availableSpace)
         {
-            var imageData = Source?.Invoke(availableSpace);
+            var targetResolution = GetTargetResolution(availableSpace, TargetDpi.Value);
+            var imageData = Source?.Invoke(targetResolution);
             
             if (imageData == null)
                 return;
@@ -27,5 +33,15 @@ namespace QuestPDF.Elements
             using var image = SKImage.FromEncodedData(imageData);
             Canvas.DrawImage(image, Position.Zero, availableSpace);
         }
+
+        private static ImageSize GetTargetResolution(Size availableSize, int targetDpi)
+        {
+            var scalingFactor = targetDpi / (float)DocumentSettings.DefaultRasterDpi;
+
+            return new ImageSize(
+                (int)(availableSize.Width * scalingFactor),
+                (int)(availableSize.Height * scalingFactor)
+            );
+        }
     }
 }

+ 62 - 1
Source/QuestPDF/Elements/Image.cs

@@ -9,6 +9,13 @@ namespace QuestPDF.Elements
     {
         public Infrastructure.Image? DocumentImage { get; set; }
 
+        internal bool UseOriginalImage { get; set; }
+        internal int? TargetDpi { get; set; }
+        internal ImageCompressionQuality? CompressionQuality { get; set; }
+        internal ImageResizeStrategy? ResizeStrategy { get; set; }
+        
+        private const float ImageSizeSimilarityToleranceMax = 0.75f;
+        
         ~Image()
         {
             if (DocumentImage is { IsDocumentScoped: true })
@@ -27,8 +34,62 @@ namespace QuestPDF.Elements
             if (DocumentImage == null)
                 return;
 
-            var image = DocumentImage.GetVersionOfSize(availableSpace);
+            var image = GetImageToDraw(availableSpace);
             Canvas.DrawImage(image, Position.Zero, availableSpace);
         }
+
+        private SKImage GetImageToDraw(Size availableSpace)
+        {
+            if (UseOriginalImage)
+                return DocumentImage.SkImage;
+            
+            var request = new GetImageVersionRequest
+            {
+                Resolution = GetTargetResolution(ResizeStrategy.Value, DocumentImage.Size, availableSpace, TargetDpi.Value),
+                CompressionQuality = CompressionQuality.Value,
+                ResizeStrategy = ResizeStrategy.Value
+            };
+            
+            return DocumentImage.GetVersionOfSize(request);
+        }
+        
+        private static ImageSize GetTargetResolution(ImageResizeStrategy resizeStrategy, ImageSize imageResolution, Size availableAreaSize, int targetDpi)
+        {
+            if (resizeStrategy == ImageResizeStrategy.Never)
+                return imageResolution;
+
+            var scalingFactor = targetDpi / (float)DocumentSettings.DefaultRasterDpi;
+            var targetResolution = new ImageSize(
+                (int)(availableAreaSize.Width * scalingFactor), 
+                (int)(availableAreaSize.Height * scalingFactor));
+
+            if (resizeStrategy == ImageResizeStrategy.Always)
+                return targetResolution;
+            
+            var isSignificantlySmaller = IsImageSignificantlySmallerThanDrawingArea(imageResolution, targetResolution);
+            
+            if (resizeStrategy == ImageResizeStrategy.ScaleOnlyToSignificantlySmallerResolution && isSignificantlySmaller)
+                return targetResolution;
+
+            var isSmaller = IsImageSmallerThanDrawingArea(imageResolution, targetResolution);
+
+            if (resizeStrategy == ImageResizeStrategy.ScaleOnlyToSmallerResolution && isSmaller)
+                return targetResolution;
+
+            return imageResolution;
+        }
+        
+        private static bool IsImageSmallerThanDrawingArea(ImageSize imageResolution, ImageSize targetResolution)
+        {
+            return imageResolution.Width < targetResolution.Width || imageResolution.Height < targetResolution.Height;
+        }
+        
+        private static bool IsImageSignificantlySmallerThanDrawingArea(ImageSize imageResolution, ImageSize targetResolution, float sizeSimilarityThreshold = ImageSizeSimilarityToleranceMax)
+        {
+            var widthRatio = targetResolution.Width / imageResolution.Width;
+            var heightRatio = targetResolution.Height / imageResolution.Height;
+        
+            return widthRatio < sizeSimilarityThreshold && heightRatio < sizeSimilarityThreshold;
+        }
     }
 }

+ 58 - 26
Source/QuestPDF/Fluent/ImageElementExtensions.cs

@@ -9,25 +9,25 @@ namespace QuestPDF.Fluent
 {
     public static class ImageExtensions
     {
-        public static void Image(this IContainer parent, byte[] imageData, ImageScaling scaling = ImageScaling.FitWidth)
+        public static ImageDescriptor Image(this IContainer parent, byte[] imageData)
         {
             var image = Infrastructure.Image.FromBinaryData(imageData).DisposeAfterDocumentGeneration();
-            parent.Image(image, scaling);
+            return parent.Image(image);
         }
         
-        public static void Image(this IContainer parent, string filePath, ImageScaling scaling = ImageScaling.FitWidth)
+        public static ImageDescriptor Image(this IContainer parent, string filePath)
         {
             var image = Infrastructure.Image.FromFile(filePath).DisposeAfterDocumentGeneration();
-            parent.Image(image, scaling);
+            return parent.Image(image);
         }
         
-        public static void Image(this IContainer parent, Stream fileStream, ImageScaling scaling = ImageScaling.FitWidth)
+        public static ImageDescriptor Image(this IContainer parent, Stream fileStream)
         {
             var image = Infrastructure.Image.FromStream(fileStream).DisposeAfterDocumentGeneration();
-            parent.Image(image, scaling);
+            return parent.Image(image);
         }
         
-        internal static void Image(this IContainer parent, Infrastructure.Image image, ImageScaling scaling = ImageScaling.FitWidth)
+        internal static ImageDescriptor Image(this IContainer parent, Infrastructure.Image image)
         {
             if (image == null)
                 throw new DocumentComposeException("Cannot load or decode provided image.");
@@ -37,32 +37,64 @@ namespace QuestPDF.Fluent
                 DocumentImage = image
             };
 
-            if (scaling != ImageScaling.Resize)
+            var aspectRationElement = new AspectRatio
             {
-                var aspectRatio = image.Width / (float)image.Height;
-                parent = parent.AspectRatio(aspectRatio, Map(scaling));
-            }
-
-            parent.Element(imageElement);
-
-            static AspectRatioOption Map(ImageScaling scaling)
-            {
-                return scaling switch
-                {
-                    ImageScaling.FitWidth => AspectRatioOption.FitWidth,
-                    ImageScaling.FitHeight => AspectRatioOption.FitHeight,
-                    ImageScaling.FitArea => AspectRatioOption.FitArea,
-                    _ => throw new ArgumentOutOfRangeException()
-                };
-            }
+                Child = imageElement
+            };
+            
+            parent.Element(aspectRationElement);
+            return new ImageDescriptor(imageElement, aspectRationElement).FitWidth();
         }
 
-        public static void Image(this IContainer element, Func<Size, byte[]> imageSource)
+        public static void Image(this IContainer element, GenerateDynamicImageDelegate dynamicImageSource)
         {
             element.Element(new DynamicImage
             {
-                Source = imageSource
+                Source = dynamicImageSource
             });
         }
+        
+        #region Obsolete
+        
+        [Obsolete("This element has been changed since version 2023.5. Please use the Image method overload that takes the GenerateDynamicImageDelegate as an argument.")]
+        public static void Image(this IContainer element, Func<Size, byte[]> imageSource)
+        {
+            element.Image((ImageSize x) => imageSource(new Size(x.Width, x.Height)));
+        }
+        
+        [Obsolete("This element has been changed since version 2023.5. Please use the Image method overload that returns the ImageDescriptor object.")]
+        public static void Image(this IContainer parent, byte[] imageData, ImageScaling scaling)
+        {
+            parent.Image(imageData).ApplyScaling(scaling);
+        }
+        
+        [Obsolete("This element has been changed since version 2023.5. Please use the Image method overload that returns the ImageDescriptor object.")]
+        public static void Image(this IContainer parent, string filePath, ImageScaling scaling)
+        {
+            parent.Image(filePath).ApplyScaling(scaling);
+        }
+        
+        [Obsolete("This element has been changed since version 2023.5. Please use the Image method overload that returns the ImageDescriptor object.")]
+        public static void Image(this IContainer parent, Stream fileStream, ImageScaling scaling)
+        {
+            parent.Image(fileStream).ApplyScaling(scaling);
+        }
+        
+        internal static void ApplyScaling(this ImageDescriptor descriptor, ImageScaling scaling)
+        {
+            if (scaling == ImageScaling.Resize)
+                descriptor.FitUnproportionally();
+
+            else if (scaling == ImageScaling.FitWidth)
+                descriptor.FitWidth();
+            
+            else if (scaling == ImageScaling.FitHeight)
+                descriptor.FitHeight();
+            
+            else if (scaling == ImageScaling.FitArea)
+                descriptor.FitArea();
+        }
+        
+        #endregion
     }
 }

+ 54 - 23
Source/QuestPDF/Fluent/ImageModelExtensions.cs

@@ -1,26 +1,31 @@
 using QuestPDF.Drawing.Exceptions;
+using QuestPDF.Elements;
 using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Fluent
 {
-    public static class ImageModelExtensions
+    public class ImageDescriptor
     {
-        /// <summary>
-        /// When enabled, the image object is disposed automatically after document generation, and you don't need to call the Dispose method yourself.
-        /// </summary>
-        public static Image DisposeAfterDocumentGeneration(this Image image)
+        private Elements.Image ImageElement { get; }
+        private AspectRatio AspectRatioElement { get; }
+        private float ImageAspectRatio { get; }
+
+        internal ImageDescriptor(Elements.Image imageElement, Elements.AspectRatio aspectRatioElement)
         {
-            image.IsDocumentScoped = true;
-            return image;
-        }
+            ImageElement = imageElement;
+            AspectRatioElement = aspectRatioElement;
 
+            var imageSize = ImageElement.DocumentImage.Size;
+            ImageAspectRatio = imageSize.Width / (float)imageSize.Height;
+        }
+        
         /// <summary>
         /// When enabled, the library will not attempt to resize the image to fit the target DPI, nor save it with target image quality.
         /// </summary>
-        public static Image UseOriginalImage(this Image image)
+        public ImageDescriptor UseOriginalImage(bool value = true)
         {
-            image.UseOriginalImage = true;
-            return image;
+            ImageElement.UseOriginalImage = value;
+            return this;
         }
         
         /// <summary>
@@ -29,10 +34,10 @@ namespace QuestPDF.Fluent
         /// When generating images, this parameter also controls the resolution of the generated content.
         /// Default value is 72.
         /// </summary>
-        public static Image WithRasterDpi(this Image image, int dpi)
+        public ImageDescriptor WithRasterDpi(int dpi)
         {
-            image.TargetDpi = dpi;
-            return image;
+            ImageElement.TargetDpi = dpi;
+            return this;
         }
 
         /// <summary>
@@ -41,22 +46,48 @@ namespace QuestPDF.Fluent
         /// If this value is set to a value between 1 and 100, and the image is opaque, it will be encoded using the JPEG format with that quality setting.
         /// The default value is 90 (very high quality).
         /// </summary>
-        public static Image WithCompressionQuality(this Image image, ImageCompressionQuality quality)
+        public ImageDescriptor WithCompressionQuality(ImageCompressionQuality quality)
+        {
+            ImageElement.CompressionQuality = quality;
+            return this;
+        }
+
+        public ImageDescriptor WithResizeStrategy(ImageResizeStrategy strategy)
+        {
+            ImageElement.ResizeStrategy = strategy;
+            return this;
+        }
+        
+        #region Aspect Ratio
+        
+        public ImageDescriptor FitWidth()
+        {
+            return SetAspectRatio(AspectRatioOption.FitWidth);
+        }
+        
+        public ImageDescriptor FitHeight()
         {
-            image.CompressionQuality = quality;
-            return image;
+            return SetAspectRatio(AspectRatioOption.FitHeight);
         }
         
-        public static Image WithScalingQuality(this Image image, ImageScalingQuality strategy)
+        public ImageDescriptor FitArea()
         {
-            image.ScalingQuality = strategy;
-            return image;
+            return SetAspectRatio(AspectRatioOption.FitArea);
         }
         
-        public static Image WithScalingStrategy(this Image image, ImageScalingStrategy strategy)
+        public ImageDescriptor FitUnproportionally()
         {
-            image.ScalingStrategy = strategy;
-            return image;
+            AspectRatioElement.Ratio = 0;
+            return this;
         }
+        
+        private ImageDescriptor SetAspectRatio(AspectRatioOption option)
+        {
+            AspectRatioElement.Ratio = ImageAspectRatio;
+            AspectRatioElement.Option = option;
+            return this;
+        }
+        
+        #endregion
     }
 }

+ 0 - 12
Source/QuestPDF/Helpers/Helpers.cs

@@ -75,17 +75,5 @@ namespace QuestPDF.Helpers
                 _ => throw new ArgumentOutOfRangeException(nameof(quality), quality, null)
             };
         }
-
-        internal static SKFilterQuality ToFilterQuality(this ImageScalingQuality quality)
-        {
-            return quality switch
-            {
-                ImageScalingQuality.Low => SKFilterQuality.None,
-                ImageScalingQuality.Medium => SKFilterQuality.Low,
-                ImageScalingQuality.High => SKFilterQuality.Medium,
-                ImageScalingQuality.Best => SKFilterQuality.High,
-                _ => throw new ArgumentOutOfRangeException(nameof(quality), quality, null)
-            };
-        }
     }
 }

+ 3 - 3
Source/QuestPDF/Helpers/Placeholders.cs

@@ -213,10 +213,10 @@ namespace QuestPDF.Helpers
 
         public static byte[] Image(int width, int height)
         {
-            return Image(new Size(width, height));
+            return Image(new ImageSize(width, height));
         }
         
-        public static byte[] Image(Size size)
+        public static byte[] Image(ImageSize size)
         {
             // shuffle corner positions
             var targetPositions = new[]
@@ -239,7 +239,7 @@ namespace QuestPDF.Helpers
                 .ToArray();
             
             // create image with white background
-            var imageInfo = new SKImageInfo((int)size.Width, (int)size.Height);
+            var imageInfo = new SKImageInfo(size.Width, size.Height);
             using var surface = SKSurface.Create(imageInfo);
    
             using var backgroundPaint = new SKPaint

+ 1 - 3
Source/QuestPDF/Infrastructure/DocumentSettings.cs

@@ -20,9 +20,7 @@
         public ImageCompressionQuality ImageCompressionQuality { get; set; } = ImageCompressionQuality.VeryHigh;
         
         // TODO: add comments
-        public ImageScalingStrategy ImageScalingStrategy { get; set; } = ImageScalingStrategy.ScaleOnlyToSignificantlySmallerResolution;
-        
-        public ImageScalingQuality ImageScalingQuality { get; set; } = ImageScalingQuality.High;
+        public ImageResizeStrategy ImageResizeStrategy { get; set; } = ImageResizeStrategy.ScaleOnlyToSignificantlySmallerResolution;
         
         /// <summary>
         /// The DPI (pixels-per-inch) at which images and features without native PDF support will be rasterized.

+ 40 - 65
Source/QuestPDF/Infrastructure/Image.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.IO;
 using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Helpers;
@@ -8,75 +7,55 @@ using SkiaSharp;
 
 namespace QuestPDF.Infrastructure
 {
+    internal record GetImageVersionRequest
+    {
+        internal ImageSize Resolution { get; set; }
+        internal ImageCompressionQuality CompressionQuality { get; set; }
+        internal ImageResizeStrategy ResizeStrategy { get; set; }
+    }
+    
     public class Image : IDisposable
     {
-        private SKImage SkImage { get; }
-        public int Width => SkImage.Width;
-        public int Height => SkImage.Height;
-
-        internal List<(Size size, SKImage image)>? ScaledImageCache { get; set; }
-        
+        internal SKImage SkImage { get; }
+        internal ImageSize Size { get; }
         internal bool IsDocumentScoped { get; set; }
-        internal bool UseOriginalImage { get; set; }
-        internal int? TargetDpi { get; set; }
-        internal ImageCompressionQuality? CompressionQuality { get; set; }
-        internal ImageScalingQuality? ScalingQuality { get; set; }
-        internal ImageScalingStrategy? ScalingStrategy { get; set; }
-
-        private const float ImageSizeSimilarityToleranceMax = 0.75f;
         
+        internal LinkedList<(GetImageVersionRequest request, SKImage image)> ScaledImageCache { get; } = new();
+
         private Image(SKImage image)
         {
             SkImage = image;
+            Size = new ImageSize(image.Width, image.Height);
         }
 
         public void Dispose()
         {
             SkImage.Dispose();
+
+            foreach (var cacheKey in ScaledImageCache)
+                cacheKey.image.Dispose();
         } 
         
         #region Scaling Image
 
-        internal SKImage GetVersionOfSize(Size availableAreaSize)
+        internal SKImage GetVersionOfSize(GetImageVersionRequest request)
         {
-            if (UseOriginalImage)
-                return SkImage;
-
-            var imageResolution = new Size(SkImage.Width, SkImage.Height);
-            var targetResolution = GetTargetResolution(ScalingStrategy.Value, imageResolution, availableAreaSize, TargetDpi ?? DocumentSettings.DefaultRasterDpi);
-            
-            return ScaleAndCompressImage(SkImage, targetResolution, ScalingQuality.Value, CompressionQuality.Value);
+            foreach (var cacheKey in ScaledImageCache)
+            {
+                if (cacheKey.request == request)
+                    return cacheKey.image;
+            }
+
+            var result = ResizeAndCompressImage(SkImage, request.Resolution, request.CompressionQuality);
+            ScaledImageCache.AddLast((request, result));
+            return result;
         }
-        
-        private static Size GetTargetResolution(ImageScalingStrategy scalingStrategy, Size imageResolution, Size availableAreaSize, int targetDpi)
-        {
-            if (scalingStrategy == ImageScalingStrategy.Never)
-                return imageResolution;
-            
-            if (scalingStrategy == ImageScalingStrategy.Always)
-                return availableAreaSize;
-            
-            var scalingFactor = targetDpi / DocumentSettings.DefaultRasterDpi;
-            var targetResolution = new Size(availableAreaSize.Width * scalingFactor, availableAreaSize.Height * scalingFactor);
-
-            var isSignificantlySmaller = IsImageSignificantlySmallerThanDrawingArea(imageResolution, targetResolution);
-            
-            if (scalingStrategy == ImageScalingStrategy.ScaleOnlyToSignificantlySmallerResolution && isSignificantlySmaller)
-                return targetResolution;
-
-            var isSmaller = IsImageSmallerThanDrawingArea(imageResolution, targetResolution);
 
-            if (scalingStrategy == ImageScalingStrategy.ScaleOnlyToSmallerResolution && isSmaller)
-                return targetResolution;
-
-            return imageResolution;
-        }
-        
         private static SKImage CompressImage(SKImage image, ImageCompressionQuality compressionQuality)
         {
             var targetFormat = image.Info.IsOpaque 
-                ? SKEncodedImageFormat.Png 
-                : SKEncodedImageFormat.Jpeg;
+                ? SKEncodedImageFormat.Jpeg 
+                : SKEncodedImageFormat.Png;
 
             if (targetFormat == SKEncodedImageFormat.Png)
                 compressionQuality = ImageCompressionQuality.Best;
@@ -85,29 +64,19 @@ namespace QuestPDF.Infrastructure
             return SKImage.FromEncodedData(data);
         }
 
-        private static SKImage ScaleAndCompressImage(SKImage image, Size targetResolution, ImageScalingQuality scalingQuality, ImageCompressionQuality compressionQuality)
+        private static SKImage ResizeAndCompressImage(SKImage image, ImageSize targetResolution, ImageCompressionQuality compressionQuality)
         {
-            var imageInfo = new SKImageInfo((int)targetResolution.Width, (int)targetResolution.Height);
+            if (image.Width == targetResolution.Width && image.Height == targetResolution.Height)
+                return CompressImage(image, compressionQuality);
+            
+            var imageInfo = new SKImageInfo(targetResolution.Width, targetResolution.Height);
             
             using var resultImage = SKImage.Create(imageInfo);
-            image.ScalePixels(resultImage.PeekPixels(), scalingQuality.ToFilterQuality());
+            image.ScalePixels(resultImage.PeekPixels(), SKFilterQuality.Medium);
             
             return CompressImage(resultImage, compressionQuality);
         }
-        
-        private static bool IsImageSmallerThanDrawingArea(Size imageResolution, Size targetResolution)
-        {
-            return imageResolution.Width < targetResolution.Width || imageResolution.Height < targetResolution.Height;
-        }
-        
-        private static bool IsImageSignificantlySmallerThanDrawingArea(Size imageResolution, Size targetResolution, float sizeSimilarityThreshold = ImageSizeSimilarityToleranceMax)
-        {
-            var widthRatio = imageResolution.Width / targetResolution.Width;
-            var heightRatio = imageResolution.Height / targetResolution.Height;
-        
-            return widthRatio < sizeSimilarityThreshold && heightRatio < sizeSimilarityThreshold;
-        }
-        
+
         #endregion
 
         #region public constructors
@@ -141,5 +110,11 @@ namespace QuestPDF.Infrastructure
         }
 
         #endregion
+
+        internal Image DisposeAfterDocumentGeneration()
+        {
+            IsDocumentScoped = true;
+            return this;
+        }
     }
 }

+ 1 - 1
Source/QuestPDF/Infrastructure/ImageScalingStrategy.cs → Source/QuestPDF/Infrastructure/ImageResizeStrategy.cs

@@ -1,6 +1,6 @@
 namespace QuestPDF.Infrastructure
 {
-    public enum ImageScalingStrategy
+    public enum ImageResizeStrategy
     {
         // TODO: add comments
         Always,

+ 0 - 15
Source/QuestPDF/Infrastructure/ImageScalingQuality.cs

@@ -1,15 +0,0 @@
-namespace QuestPDF.Infrastructure
-{
-    // https://chromium.googlesource.com/skia/+/master/include/core/SkFilterQuality.h
-    public enum ImageScalingQuality
-    {
-        // TODO: add comments
-        Low,
-        
-        Medium,
-        
-        High,
-        
-        Best
-    }
-}

+ 14 - 0
Source/QuestPDF/Infrastructure/ImageSize.cs

@@ -0,0 +1,14 @@
+namespace QuestPDF.Infrastructure
+{
+    public struct ImageSize
+    {
+        public readonly int Width;
+        public readonly int Height;
+
+        public ImageSize(int width, int height)
+        {
+            Width = width;
+            Height = height;
+        }
+    }
+}