Added support for image DPI scaling

This commit is contained in:
MarcinZiabek
2023-04-30 11:26:55 +02:00
parent dc0a21b8e7
commit 41ba1ac1ce
6 changed files with 106 additions and 77 deletions
@@ -1,22 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples.Engine
{
public static class Helpers
{
public static void GeneratePdfAndOpen(this Document document, string? fileName = null)
{
fileName ??= $"{Guid.NewGuid():D}.pdf";
var filePath = Path.GetTempPath() + fileName;
var documentData = document.GeneratePdf();
File.WriteAllBytes(filePath, documentData);
Process.Start("explorer", filePath);
}
}
}
+3 -31
View File
@@ -66,29 +66,7 @@ namespace QuestPDF.Examples
});
}
[Test]
public void ReusingTheSameImageFileShouldBePossible()
{
var image = Image.FromBinaryData(Placeholders.Image(300, 100)).DisposeAfterDocumentGeneration();
RenderingTest
.Create()
.ProducePdf()
.PageSize(PageSizes.A4)
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Column(column =>
{
column.Spacing(20);
foreach (var i in Enumerable.Range(0, 1000))
column.Item().Image(image);
});
});
}
[Test]
public void ImageResolutionScaling()
@@ -100,18 +78,12 @@ namespace QuestPDF.Examples
{
document.Page(page =>
{
page.Size(11000, 11000);
page.Size(210, 210);
page.Margin(50);
page.Content().Image(image);
});
})
.GeneratePdfAndOpen();
//Console.WriteLine(documentData.Length);
// var filePath = Path.GetTempPath() + $"test.pdf";
// File.WriteAllBytes(filePath, documentData);
// Console.WriteLine(filePath);
.GeneratePdf($"test.pdf");
}
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
+54 -1
View File
@@ -1,7 +1,12 @@
using NUnit.Framework;
using System;
using System.Linq;
using System.Net.Mime;
using FluentAssertions;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine;
using SkiaSharp;
@@ -59,5 +64,53 @@ namespace QuestPDF.UnitTests
using var surface = SKSurface.Create(imageInfo);
return surface.Snapshot();
}
[Test]
public void UsingSharedImageShouldNotDrasticallyIncreaseDocumentSize()
{
var placeholderImage = Placeholders.Image(1000, 200);
var documentWithSingleImageSize = GetDocumentSize(container =>
{
container.Image(placeholderImage);
});
var documentWithMultipleImagesSize = GetDocumentSize(container =>
{
container.Column(column =>
{
foreach (var i in Enumerable.Range(0, 100))
column.Item().Image(placeholderImage);
});
});
var documentWithSingleImageUsedMultipleTimesSize = GetDocumentSize(container =>
{
container.Column(column =>
{
var sharedImage = Image.FromBinaryData(placeholderImage).DisposeAfterDocumentGeneration();
foreach (var i in Enumerable.Range(0, 100))
column.Item().Image(sharedImage);
});
});
(documentWithMultipleImagesSize / (float)documentWithSingleImageSize).Should().BeInRange(90, 100);
(documentWithSingleImageUsedMultipleTimesSize / (float)documentWithSingleImageSize).Should().BeInRange(1f, 1.5f);
}
private static int GetDocumentSize(Action<IContainer> container)
{
return Document
.Create(document =>
{
document.Page(page =>
{
page.Content().Element(container);
});
})
.GeneratePdf()
.Length;
}
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
+47 -21
View File
@@ -10,13 +10,17 @@ namespace QuestPDF.Infrastructure
public class Image : IDisposable
{
private SKImage SkImage { get; }
internal List<(Size size, SKImage image)>? ScaledImageCache { get; }
internal List<(Size size, SKImage image)>? ScaledImageCache { get; set; }
public int? TargetDpi { get; set; }
internal int? TargetDpi { get; set; }
internal bool PerformScalingToTargetDpi { get; set; } = true;
internal bool IsDocumentScoped { get; set; }
public int Width => SkImage.Width;
public int Height => SkImage.Height;
private const float ImageSizeSimilarityToleranceMax = 1.1f;
private const float ImageSizeSimilarityToleranceMin = 1 / ImageSizeSimilarityToleranceMax;
private Image(SKImage image)
{
@@ -31,28 +35,44 @@ namespace QuestPDF.Infrastructure
internal SKImage GetVersionOfSize(Size size)
{
if (size.Width > Width || size.Height > Height)
if (!PerformScalingToTargetDpi)
return SkImage;
var scalingFactor = TargetDpi.Value / (float)DocumentMetadata.DefaultPdfDpi;
var targetResolution = new Size(size.Width * scalingFactor, size.Height * scalingFactor);
if (targetResolution.Width > Width || targetResolution.Height > Height)
return SkImage;
var target = SKImage.Create(new SKImageInfo((int)size.Width, (int)size.Height));
SkImage.ScalePixels(target.PeekPixels(), SKFilterQuality.High);
return target;
// bool ShouldApplyScaling()
// {
// const float tolerance = 1.1f;
//
// if (width > Width / tolerance || height > Height / tolerance)
// return false;
//
// SkImage.sca
// }
ScaledImageCache ??= new List<(Size size, SKImage image)>();
static bool HasSimilarSize(Size a, Size b, float tolerance)
foreach (var imageCache in ScaledImageCache)
{
return (a.Width / b.Width > 1 / tolerance && a.Width / b.Width < tolerance) ||
(a.Height / b.Height > 1 / tolerance && a.Height / b.Height < tolerance);
if (HasSimilarSize(imageCache.size, targetResolution))
return imageCache.image;
}
var scaledImage = ScaleImage(SkImage, targetResolution);
ScaledImageCache.Add((targetResolution, scaledImage));
return scaledImage;
static SKImage ScaleImage(SKImage originalImage, Size targetSize)
{
var imageInfo = new SKImageInfo((int)targetSize.Width, (int)targetSize.Height);
var target = SKImage.Create(imageInfo);
originalImage.ScalePixels(target.PeekPixels(), SKFilterQuality.High);
return target;
}
static bool HasSimilarSize(Size a, Size b)
{
var widthRatio = a.Width / b.Width;
var heightRatio = a.Height / b.Height;
return widthRatio is > ImageSizeSimilarityToleranceMin and < ImageSizeSimilarityToleranceMax &&
heightRatio is > ImageSizeSimilarityToleranceMin and < ImageSizeSimilarityToleranceMax;
}
}
@@ -101,7 +121,13 @@ namespace QuestPDF.Infrastructure
TargetDpi = dpi;
return this;
}
public Image ScaleToTargetDpi(bool value = true)
{
PerformScalingToTargetDpi = value;
return this;
}
#endregion
}
}