Improved image handling

This commit is contained in:
MarcinZiabek
2023-05-09 00:47:28 +02:00
parent c21a335342
commit bdb61aa545
14 changed files with 273 additions and 162 deletions
@@ -51,7 +51,7 @@ namespace QuestPDF.UnitTests
[Test] [Test]
public void Draw_PassesCorrectSizeToSource() public void Draw_PassesCorrectSizeToSource()
{ {
Size passedSize = default; ImageSize passedSize = default;
TestPlan TestPlan
.For(x => new DynamicImage .For(x => new DynamicImage
@@ -69,9 +69,9 @@ namespace QuestPDF.UnitTests
passedSize.Should().BeEquivalentTo(new Size(400, 300)); 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(); return image.Encode(SKEncodedImageFormat.Png, 100).ToArray();
} }
+12 -7
View File
@@ -232,13 +232,18 @@ namespace QuestPDF.Drawing
{ {
content.VisitChildren(x => content.VisitChildren(x =>
{ {
if (x is not QuestPDF.Elements.Image { DocumentImage: { } image }) if (x is QuestPDF.Elements.Image image)
return; {
image.TargetDpi ??= settings.RasterDpi;
image.TargetDpi ??= settings.RasterDpi; image.ResizeStrategy ??= settings.ImageResizeStrategy;
image.ScalingQuality ??= settings.ImageScalingQuality; image.CompressionQuality ??= settings.ImageCompressionQuality;
image.ScalingStrategy ??= settings.ImageScalingStrategy; }
image.CompressionQuality ??= settings.ImageCompressionQuality;
if (x is QuestPDF.Elements.DynamicImage dynamicImage)
{
dynamicImage.TargetDpi ??= settings.RasterDpi;
dynamicImage.CompressionQuality ??= settings.ImageCompressionQuality;
}
}); });
} }
+7 -1
View File
@@ -8,11 +8,14 @@ namespace QuestPDF.Elements
{ {
public ContentDirection ContentDirection { get; set; } public ContentDirection ContentDirection { get; set; }
public float Ratio { get; set; } = 1; public float Ratio { get; set; }
public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth; public AspectRatioOption Option { get; set; } = AspectRatioOption.FitWidth;
internal override SpacePlan Measure(Size availableSpace) internal override SpacePlan Measure(Size availableSpace)
{ {
if (Ratio == 0)
return SpacePlan.FullRender(0, 0);
if(Child == null) if(Child == null)
return SpacePlan.FullRender(0, 0); return SpacePlan.FullRender(0, 0);
@@ -56,6 +59,9 @@ namespace QuestPDF.Elements
private Size GetTargetSize(Size availableSpace) private Size GetTargetSize(Size availableSpace)
{ {
if (Ratio == 0)
return availableSpace;
var spaceRatio = availableSpace.Width / availableSpace.Height; var spaceRatio = availableSpace.Width / availableSpace.Height;
var fitHeight = new Size(availableSpace.Height * Ratio, availableSpace.Height) ; var fitHeight = new Size(availableSpace.Height * Ratio, availableSpace.Height) ;
+18 -2
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Security;
using QuestPDF.Drawing; using QuestPDF.Drawing;
using QuestPDF.Helpers; using QuestPDF.Helpers;
using QuestPDF.Infrastructure; using QuestPDF.Infrastructure;
@@ -6,9 +7,13 @@ using SkiaSharp;
namespace QuestPDF.Elements namespace QuestPDF.Elements
{ {
public delegate byte[] GenerateDynamicImageDelegate(ImageSize size);
internal class DynamicImage : Element 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) internal override SpacePlan Measure(Size availableSpace)
{ {
@@ -19,7 +24,8 @@ namespace QuestPDF.Elements
internal override void Draw(Size availableSpace) 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) if (imageData == null)
return; return;
@@ -27,5 +33,15 @@ namespace QuestPDF.Elements
using var image = SKImage.FromEncodedData(imageData); using var image = SKImage.FromEncodedData(imageData);
Canvas.DrawImage(image, Position.Zero, availableSpace); 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
View File
@@ -9,6 +9,13 @@ namespace QuestPDF.Elements
{ {
public Infrastructure.Image? DocumentImage { get; set; } 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() ~Image()
{ {
if (DocumentImage is { IsDocumentScoped: true }) if (DocumentImage is { IsDocumentScoped: true })
@@ -27,8 +34,62 @@ namespace QuestPDF.Elements
if (DocumentImage == null) if (DocumentImage == null)
return; return;
var image = DocumentImage.GetVersionOfSize(availableSpace); var image = GetImageToDraw(availableSpace);
Canvas.DrawImage(image, Position.Zero, 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;
}
} }
} }
@@ -9,25 +9,25 @@ namespace QuestPDF.Fluent
{ {
public static class ImageExtensions 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(); 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(); 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(); 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) if (image == null)
throw new DocumentComposeException("Cannot load or decode provided image."); throw new DocumentComposeException("Cannot load or decode provided image.");
@@ -37,32 +37,64 @@ namespace QuestPDF.Fluent
DocumentImage = image DocumentImage = image
}; };
if (scaling != ImageScaling.Resize) var aspectRationElement = new AspectRatio
{ {
var aspectRatio = image.Width / (float)image.Height; Child = imageElement
parent = parent.AspectRatio(aspectRatio, Map(scaling)); };
}
parent.Element(aspectRationElement);
parent.Element(imageElement); return new ImageDescriptor(imageElement, aspectRationElement).FitWidth();
static AspectRatioOption Map(ImageScaling scaling)
{
return scaling switch
{
ImageScaling.FitWidth => AspectRatioOption.FitWidth,
ImageScaling.FitHeight => AspectRatioOption.FitHeight,
ImageScaling.FitArea => AspectRatioOption.FitArea,
_ => throw new ArgumentOutOfRangeException()
};
}
} }
public static void Image(this IContainer element, Func<Size, byte[]> imageSource) public static void Image(this IContainer element, GenerateDynamicImageDelegate dynamicImageSource)
{ {
element.Element(new DynamicImage 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
} }
} }
+55 -24
View File
@@ -1,26 +1,31 @@
using QuestPDF.Drawing.Exceptions; using QuestPDF.Drawing.Exceptions;
using QuestPDF.Elements;
using QuestPDF.Infrastructure; using QuestPDF.Infrastructure;
namespace QuestPDF.Fluent namespace QuestPDF.Fluent
{ {
public static class ImageModelExtensions public class ImageDescriptor
{ {
/// <summary> private Elements.Image ImageElement { get; }
/// When enabled, the image object is disposed automatically after document generation, and you don't need to call the Dispose method yourself. private AspectRatio AspectRatioElement { get; }
/// </summary> private float ImageAspectRatio { get; }
public static Image DisposeAfterDocumentGeneration(this Image image)
{
image.IsDocumentScoped = true;
return image;
}
internal ImageDescriptor(Elements.Image imageElement, Elements.AspectRatio aspectRatioElement)
{
ImageElement = imageElement;
AspectRatioElement = aspectRatioElement;
var imageSize = ImageElement.DocumentImage.Size;
ImageAspectRatio = imageSize.Width / (float)imageSize.Height;
}
/// <summary> /// <summary>
/// When enabled, the library will not attempt to resize the image to fit the target DPI, nor save it with target image quality. /// When enabled, the library will not attempt to resize the image to fit the target DPI, nor save it with target image quality.
/// </summary> /// </summary>
public static Image UseOriginalImage(this Image image) public ImageDescriptor UseOriginalImage(bool value = true)
{ {
image.UseOriginalImage = true; ImageElement.UseOriginalImage = value;
return image; return this;
} }
/// <summary> /// <summary>
@@ -29,10 +34,10 @@ namespace QuestPDF.Fluent
/// When generating images, this parameter also controls the resolution of the generated content. /// When generating images, this parameter also controls the resolution of the generated content.
/// Default value is 72. /// Default value is 72.
/// </summary> /// </summary>
public static Image WithRasterDpi(this Image image, int dpi) public ImageDescriptor WithRasterDpi(int dpi)
{ {
image.TargetDpi = dpi; ImageElement.TargetDpi = dpi;
return image; return this;
} }
/// <summary> /// <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. /// 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). /// The default value is 90 (very high quality).
/// </summary> /// </summary>
public static Image WithCompressionQuality(this Image image, ImageCompressionQuality quality) public ImageDescriptor WithCompressionQuality(ImageCompressionQuality quality)
{ {
image.CompressionQuality = quality; ImageElement.CompressionQuality = quality;
return image; return this;
}
public ImageDescriptor WithResizeStrategy(ImageResizeStrategy strategy)
{
ImageElement.ResizeStrategy = strategy;
return this;
} }
public static Image WithScalingQuality(this Image image, ImageScalingQuality strategy) #region Aspect Ratio
public ImageDescriptor FitWidth()
{ {
image.ScalingQuality = strategy; return SetAspectRatio(AspectRatioOption.FitWidth);
return image;
} }
public static Image WithScalingStrategy(this Image image, ImageScalingStrategy strategy) public ImageDescriptor FitHeight()
{ {
image.ScalingStrategy = strategy; return SetAspectRatio(AspectRatioOption.FitHeight);
return image;
} }
public ImageDescriptor FitArea()
{
return SetAspectRatio(AspectRatioOption.FitArea);
}
public ImageDescriptor FitUnproportionally()
{
AspectRatioElement.Ratio = 0;
return this;
}
private ImageDescriptor SetAspectRatio(AspectRatioOption option)
{
AspectRatioElement.Ratio = ImageAspectRatio;
AspectRatioElement.Option = option;
return this;
}
#endregion
} }
} }
-12
View File
@@ -75,17 +75,5 @@ namespace QuestPDF.Helpers
_ => throw new ArgumentOutOfRangeException(nameof(quality), quality, null) _ => 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
View File
@@ -213,10 +213,10 @@ namespace QuestPDF.Helpers
public static byte[] Image(int width, int height) 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 // shuffle corner positions
var targetPositions = new[] var targetPositions = new[]
@@ -239,7 +239,7 @@ namespace QuestPDF.Helpers
.ToArray(); .ToArray();
// create image with white background // 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 surface = SKSurface.Create(imageInfo);
using var backgroundPaint = new SKPaint using var backgroundPaint = new SKPaint
@@ -20,9 +20,7 @@
public ImageCompressionQuality ImageCompressionQuality { get; set; } = ImageCompressionQuality.VeryHigh; public ImageCompressionQuality ImageCompressionQuality { get; set; } = ImageCompressionQuality.VeryHigh;
// TODO: add comments // TODO: add comments
public ImageScalingStrategy ImageScalingStrategy { get; set; } = ImageScalingStrategy.ScaleOnlyToSignificantlySmallerResolution; public ImageResizeStrategy ImageResizeStrategy { get; set; } = ImageResizeStrategy.ScaleOnlyToSignificantlySmallerResolution;
public ImageScalingQuality ImageScalingQuality { get; set; } = ImageScalingQuality.High;
/// <summary> /// <summary>
/// The DPI (pixels-per-inch) at which images and features without native PDF support will be rasterized. /// The DPI (pixels-per-inch) at which images and features without native PDF support will be rasterized.
+39 -64
View File
@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using QuestPDF.Drawing.Exceptions; using QuestPDF.Drawing.Exceptions;
using QuestPDF.Helpers; using QuestPDF.Helpers;
@@ -8,75 +7,55 @@ using SkiaSharp;
namespace QuestPDF.Infrastructure 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 public class Image : IDisposable
{ {
private SKImage SkImage { get; } internal SKImage SkImage { get; }
public int Width => SkImage.Width; internal ImageSize Size { get; }
public int Height => SkImage.Height;
internal List<(Size size, SKImage image)>? ScaledImageCache { get; set; }
internal bool IsDocumentScoped { get; set; } 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) private Image(SKImage image)
{ {
SkImage = image; SkImage = image;
Size = new ImageSize(image.Width, image.Height);
} }
public void Dispose() public void Dispose()
{ {
SkImage.Dispose(); SkImage.Dispose();
foreach (var cacheKey in ScaledImageCache)
cacheKey.image.Dispose();
} }
#region Scaling Image #region Scaling Image
internal SKImage GetVersionOfSize(Size availableAreaSize) internal SKImage GetVersionOfSize(GetImageVersionRequest request)
{ {
if (UseOriginalImage) foreach (var cacheKey in ScaledImageCache)
return SkImage; {
if (cacheKey.request == request)
return cacheKey.image;
}
var imageResolution = new Size(SkImage.Width, SkImage.Height); var result = ResizeAndCompressImage(SkImage, request.Resolution, request.CompressionQuality);
var targetResolution = GetTargetResolution(ScalingStrategy.Value, imageResolution, availableAreaSize, TargetDpi ?? DocumentSettings.DefaultRasterDpi); ScaledImageCache.AddLast((request, result));
return result;
return ScaleAndCompressImage(SkImage, targetResolution, ScalingQuality.Value, CompressionQuality.Value);
} }
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) private static SKImage CompressImage(SKImage image, ImageCompressionQuality compressionQuality)
{ {
var targetFormat = image.Info.IsOpaque var targetFormat = image.Info.IsOpaque
? SKEncodedImageFormat.Png ? SKEncodedImageFormat.Jpeg
: SKEncodedImageFormat.Jpeg; : SKEncodedImageFormat.Png;
if (targetFormat == SKEncodedImageFormat.Png) if (targetFormat == SKEncodedImageFormat.Png)
compressionQuality = ImageCompressionQuality.Best; compressionQuality = ImageCompressionQuality.Best;
@@ -85,29 +64,19 @@ namespace QuestPDF.Infrastructure
return SKImage.FromEncodedData(data); 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); using var resultImage = SKImage.Create(imageInfo);
image.ScalePixels(resultImage.PeekPixels(), scalingQuality.ToFilterQuality()); image.ScalePixels(resultImage.PeekPixels(), SKFilterQuality.Medium);
return CompressImage(resultImage, compressionQuality); 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 #endregion
#region public constructors #region public constructors
@@ -141,5 +110,11 @@ namespace QuestPDF.Infrastructure
} }
#endregion #endregion
internal Image DisposeAfterDocumentGeneration()
{
IsDocumentScoped = true;
return this;
}
} }
} }
@@ -1,6 +1,6 @@
namespace QuestPDF.Infrastructure namespace QuestPDF.Infrastructure
{ {
public enum ImageScalingStrategy public enum ImageResizeStrategy
{ {
// TODO: add comments // TODO: add comments
Always, Always,
@@ -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
}
}
@@ -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;
}
}
}