Add page size to image metadata

This commit is contained in:
Ben Olden-Cooligan 2023-07-03 12:01:44 -07:00
parent eb3e10cdad
commit 17c138d983
17 changed files with 86 additions and 37 deletions

View File

@ -81,12 +81,14 @@ public class RecoveryStorageManagerTests : ContextualTests
ImageContext.Create(100, 100, ImagePixelFormat.RGB24),
BitDepth.Grayscale,
true,
-1));
-1,
PageSize.A4));
_imageList.Mutate(new ListMutation<UiImage>.Append(image1));
var indexFileContent = File.ReadAllText(Path.Combine(_recoveryFolder, "index.xml"));
Assert.Contains("<BitDepth>Grayscale</BitDepth>", indexFileContent);
Assert.Contains("<HighQuality>true</HighQuality>", indexFileContent);
Assert.Contains("<PageSize>210x297 mm</PageSize>", indexFileContent);
}
[Fact]

View File

@ -44,7 +44,8 @@ public class RecoverableFolder : IDisposable
}
public RecoverableFolder(ScanningContext scanningContext, ImportPostProcessor importPostProcessor,
DirectoryInfo directory, FileStream lockFile, RecoveryIndex recoveryIndex, int imageCount, DateTime scannedDateTime)
DirectoryInfo directory, FileStream lockFile, RecoveryIndex recoveryIndex, int imageCount,
DateTime scannedDateTime)
{
_scanningContext = scanningContext;
_importPostProcessor = importPostProcessor;
@ -128,7 +129,8 @@ public class RecoverableFolder : IDisposable
RecoveryIndexImage indexImage)
{
var processedImage = _scanningContext.CreateProcessedImage(storage, indexImage.BitDepth.ToBitDepth(),
indexImage.HighQuality, -1, indexImage.TransformList!.ToImmutableList());
indexImage.HighQuality, -1, PageSize.Parse(indexImage.PageSize),
indexImage.TransformList!.ToImmutableList());
// TODO: Make this take a lazy rendered image or something
processedImage = _importPostProcessor.AddPostProcessingData(processedImage,

View File

@ -11,4 +11,6 @@ public class RecoveryIndexImage
public ScanBitDepth BitDepth { get; set; }
public bool HighQuality { get; set; }
public string? PageSize { get; set; }
}

View File

@ -92,6 +92,7 @@ public class RecoveryStorageManager : IDisposable
FileName = Path.GetFileName(storage.FullPath),
BitDepth = processedImage.Metadata.BitDepth.ToScanBitDepth(),
HighQuality = processedImage.Metadata.Lossless,
PageSize = processedImage.Metadata.PageSize?.ToString(),
TransformList = processedImage.TransformState.Transforms.ToList()
};
}).ToList();

View File

@ -24,6 +24,7 @@ public class ImageSerializerTests : ContextualTests
BitDepth.Grayscale,
true,
-1,
PageSize.A4,
new[] { new BrightnessTransform(300) });
var serializedImage = ImageSerializer.Serialize(sourceImage, new SerializeImageOptions());
@ -33,6 +34,7 @@ public class ImageSerializerTests : ContextualTests
Assert.Equal(300, Assert.IsType<BrightnessTransform>(destImage.TransformState.Transforms[0]).Brightness);
Assert.True(destImage.Metadata.Lossless);
Assert.Equal(BitDepth.Grayscale, destImage.Metadata.BitDepth);
Assert.Equal(PageSize.A4, destImage.Metadata.PageSize);
ImageAsserts.Similar(ImageResources.dog_b_p300, destImage);
}

View File

@ -11,7 +11,7 @@ public class ProcessedImageTests : ContextualTests
{
var storage = LoadImage(ImageResources.dog);
var metadata1 = new ImageMetadata(BitDepth.Color, false);
var metadata1 = new ImageMetadata(BitDepth.Color, false, null);
var postProcessingData1 = new PostProcessingData();
var transformState1 = TransformState.Empty;
var image1 = new ProcessedImage(ImageContext, storage, metadata1, postProcessingData1, transformState1);
@ -20,7 +20,7 @@ public class ProcessedImageTests : ContextualTests
Assert.False(image1.Metadata.Lossless);
Assert.True(image1.TransformState.IsEmpty);
var metadata2 = new ImageMetadata(BitDepth.BlackAndWhite, true);
var metadata2 = new ImageMetadata(BitDepth.BlackAndWhite, true, null);
var postProcessingData2 = new PostProcessingData();
var transformState2 = new TransformState(ImmutableList<Transform>.Empty.Add(new CropTransform(0, 50, 0, 50)));
var image2 = new ProcessedImage(ImageContext, storage, metadata2, postProcessingData2, transformState2);
@ -39,7 +39,7 @@ public class ProcessedImageTests : ContextualTests
public void StorageDisposed()
{
var storageMock = new Mock<IImageStorage>();
var metadata = new ImageMetadata(BitDepth.Color, false);
var metadata = new ImageMetadata(BitDepth.Color, false, null);
var image = new ProcessedImage(
ImageContext, storageMock.Object, metadata, new PostProcessingData(), TransformState.Empty);
@ -52,7 +52,7 @@ public class ProcessedImageTests : ContextualTests
public void StorageDisposedOnlyAfterAllClonesDisposed()
{
var storageMock = new Mock<IImageStorage>();
var metadata = new ImageMetadata(BitDepth.Color, false);
var metadata = new ImageMetadata(BitDepth.Color, false, null);
var image = new ProcessedImage(
ImageContext, storageMock.Object, metadata, new PostProcessingData(), TransformState.Empty);
@ -80,7 +80,7 @@ public class ProcessedImageTests : ContextualTests
public void TransformSimplification()
{
var storageMock = new Mock<IImageStorage>();
var metadata = new ImageMetadata(BitDepth.Color, false);
var metadata = new ImageMetadata(BitDepth.Color, false, null);
var image = new ProcessedImage(
ImageContext, storageMock.Object, metadata, new PostProcessingData(), TransformState.Empty);
@ -118,7 +118,7 @@ public class ProcessedImageTests : ContextualTests
public void MultipleTransforms()
{
var storageMock = new Mock<IImageStorage>();
var metadata = new ImageMetadata(BitDepth.Color, false);
var metadata = new ImageMetadata(BitDepth.Color, false, null);
var image = new ProcessedImage(
ImageContext, storageMock.Object, metadata, new PostProcessingData(), TransformState.Empty);
@ -157,7 +157,7 @@ public class ProcessedImageTests : ContextualTests
public void CloneAfterDisposed()
{
var storageMock = new Mock<IImageStorage>();
var metadata = new ImageMetadata(BitDepth.Color, false);
var metadata = new ImageMetadata(BitDepth.Color, false, null);
var image = new ProcessedImage(
ImageContext, storageMock.Object, metadata, new PostProcessingData(), TransformState.Empty);

View File

@ -10,7 +10,7 @@ public class PdfBenchmarkTests : ContextualTests
{
var filePath = Path.Combine(FolderPath, "test");
using var image = ScanningContext.CreateProcessedImage(LoadImage(ImageResources.dog), BitDepth.Color,
false, -1, Enumerable.Empty<Transform>());
false, -1, null, Enumerable.Empty<Transform>());
var pdfExporter = new PdfExporter(ScanningContext);
for (int i = 0; i < 300; i++)
@ -24,7 +24,7 @@ public class PdfBenchmarkTests : ContextualTests
{
var filePath = Path.Combine(FolderPath, "test");
using var image = ScanningContext.CreateProcessedImage(LoadImage(ImageResources.dog_huge),
BitDepth.Color, false, -1, Enumerable.Empty<Transform>());
BitDepth.Color, false, -1, null, Enumerable.Empty<Transform>());
var pdfExporter = new PdfExporter(ScanningContext);
await pdfExporter.Export(filePath + ".pdf", new[] { image }, new PdfExportParams());
@ -35,7 +35,7 @@ public class PdfBenchmarkTests : ContextualTests
{
var filePath = Path.Combine(FolderPath, "test");
using var image = ScanningContext.CreateProcessedImage(LoadImage(ImageResources.dog_huge_png),
BitDepth.Color, true, -1, Enumerable.Empty<Transform>());
BitDepth.Color, true, -1, null, Enumerable.Empty<Transform>());
var pdfExporter = new PdfExporter(ScanningContext);
await pdfExporter.Export(filePath + ".pdf", new[] { image }, new PdfExportParams());
@ -46,7 +46,7 @@ public class PdfBenchmarkTests : ContextualTests
{
var filePath = Path.Combine(FolderPath, "test");
using var image = ScanningContext.CreateProcessedImage(LoadImage(ImageResources.dog), BitDepth.Color,
false, -1, Enumerable.Empty<Transform>());
false, -1, null, Enumerable.Empty<Transform>());
var pdfExporter = new PdfiumPdfExporter(ScanningContext);
for (int i = 0; i < 300; i++)
@ -60,7 +60,7 @@ public class PdfBenchmarkTests : ContextualTests
{
var filePath = Path.Combine(FolderPath, "test");
using var image = ScanningContext.CreateProcessedImage(LoadImage(ImageResources.dog_huge),
BitDepth.Color, false, -1, Enumerable.Empty<Transform>());
BitDepth.Color, false, -1, null, Enumerable.Empty<Transform>());
var pdfExporter = new PdfiumPdfExporter(ScanningContext);
await pdfExporter.Export(filePath + ".pdf", new[] { image }, new PdfExportParams());
@ -71,7 +71,7 @@ public class PdfBenchmarkTests : ContextualTests
{
var filePath = Path.Combine(FolderPath, "test");
using var image = ScanningContext.CreateProcessedImage(LoadImage(ImageResources.dog_huge_png),
BitDepth.Color, true, -1, Enumerable.Empty<Transform>());
BitDepth.Color, true, -1, null, Enumerable.Empty<Transform>());
var pdfExporter = new PdfiumPdfExporter(ScanningContext);
await pdfExporter.Export(filePath + ".pdf", new[] { image }, new PdfExportParams());

View File

@ -54,7 +54,7 @@ public class PdfExporterTests : ContextualTests
var filePath = Path.Combine(FolderPath, "test.pdf");
using var image = ScanningContext.CreateProcessedImage(
LoadImage(ImageResources.dog_png), BitDepth.Color, true, -1);
LoadImage(ImageResources.dog_png), BitDepth.Color, true, -1, null);
await _exporter.Export(filePath, new[] { image }, new PdfExportParams());
@ -71,7 +71,7 @@ public class PdfExporterTests : ContextualTests
var filePath = Path.Combine(FolderPath, "test.pdf");
// Width is 99 (not divisible by 4)
var image = ScanningContext.CreateProcessedImage(
LoadImage(ImageResources.dog_99w), BitDepth.Color, true, -1);
LoadImage(ImageResources.dog_99w), BitDepth.Color, true, -1, null);
await _exporter.Export(filePath, new[] { image }, new PdfExportParams());
@ -87,7 +87,7 @@ public class PdfExporterTests : ContextualTests
var filePath = Path.Combine(FolderPath, "test.pdf");
using var image = ScanningContext.CreateProcessedImage(
LoadImage(ImageResources.dog_alpha), BitDepth.Color, false, -1);
LoadImage(ImageResources.dog_alpha), BitDepth.Color, false, -1, null);
await _exporter.Export(filePath, new[] { image }, new PdfExportParams());
@ -104,7 +104,7 @@ public class PdfExporterTests : ContextualTests
var filePath = Path.Combine(FolderPath, "test.pdf");
using var image = ScanningContext.CreateProcessedImage(
LoadImage(ImageResources.dog_mask), BitDepth.Color, false, -1);
LoadImage(ImageResources.dog_mask), BitDepth.Color, false, -1, null);
await _exporter.Export(filePath, new[] { image }, new PdfExportParams());
@ -153,7 +153,7 @@ public class PdfExporterTests : ContextualTests
var filePath = Path.Combine(FolderPath, "test.pdf");
using var image = ScanningContext.CreateProcessedImage(LoadImage(ImageResources.dog_bw_24bit),
BitDepth.BlackAndWhite, true, -1);
BitDepth.BlackAndWhite, true, -1, null);
await _exporter.Export(filePath, new[] { image }, new PdfExportParams());

View File

@ -1,9 +1,9 @@
namespace NAPS2.Images;
public record ImageMetadata(BitDepth BitDepth, bool Lossless)
public record ImageMetadata(BitDepth BitDepth, bool Lossless, PageSize? PageSize)
{
/// <summary>
/// A default set of metadata suitable for test images. Real use cases should be explicit and not use this default value.
/// </summary>
public static readonly ImageMetadata DefaultForTesting = new(BitDepth.Color, false);
public static readonly ImageMetadata DefaultForTesting = new(BitDepth.Color, false, null);
}

View File

@ -1,6 +1,6 @@
using System.Globalization;
namespace NAPS2.Scan;
namespace NAPS2.Images;
public record PageSize
{
@ -18,6 +18,29 @@ public record PageSize
public static PageSize B4 = new("250", "353", PageSizeUnit.Millimetre);
public static PageSize? Parse(string? size)
{
if (size == null)
return null;
var parts = size.Split(' ');
if (parts.Length != 2)
return null;
var dims = parts[0].Split('x');
if (dims.Length != 2)
return null;
if (!decimal.TryParse(dims[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var width))
return null;
if (!decimal.TryParse(dims[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var height))
return null;
var unit = parts[1] switch
{
"mm" => PageSizeUnit.Millimetre,
"cm" => PageSizeUnit.Centimetre,
_ => PageSizeUnit.Inch
};
return new PageSize(width, height, unit);
}
protected PageSize()
{
}
@ -117,4 +140,15 @@ public record PageSize
}
public int HeightInThousandthsOfAnInch => (int)(HeightInInches * 1000);
public override string ToString()
{
var unit = Unit switch
{
PageSizeUnit.Centimetre => "cm",
PageSizeUnit.Millimetre => "mm",
_ => "in"
};
return $"{Width.ToString(CultureInfo.InvariantCulture)}x{Height.ToString(CultureInfo.InvariantCulture)} {unit}";
}
}

View File

@ -1,4 +1,4 @@
namespace NAPS2.Scan;
namespace NAPS2.Images;
public enum PageSizeUnit
{

View File

@ -51,7 +51,8 @@ public class ImageImporter : IImageImporter
: frame,
BitDepth.Color,
lossless,
-1);
-1,
null);
image = _importPostProcessor.AddPostProcessingData(
image,
frame,

View File

@ -111,7 +111,8 @@ public class PdfImporter : IPdfImporter
using var storage = PdfiumImageExtractor.GetSingleImage(_scanningContext.ImageContext, page);
if (storage != null)
{
var image = _scanningContext.CreateProcessedImage(storage, BitDepth.Color, false, -1);
var pageSize = new PageSize((decimal) page.Width * 72, (decimal) page.Height * 72, PageSizeUnit.Inch);
var image = _scanningContext.CreateProcessedImage(storage, BitDepth.Color, false, -1, pageSize);
return _importPostProcessor.AddPostProcessingData(
image,
storage,

View File

@ -50,7 +50,7 @@ internal class RemotePostProcessor : IRemotePostProcessor
var bitDepth = options.UseNativeUI ? BitDepth.Color : options.BitDepth;
var scannedImage = _scanningContext.CreateProcessedImage(image, bitDepth, options.MaxQuality,
options.Quality);
options.Quality, options.PageSize);
DoRevertibleTransforms(ref scannedImage, ref image, options, postProcessingContext);
postProcessingContext.TempPath = SaveForBackgroundOcr(image, options);
return scannedImage;

View File

@ -58,19 +58,20 @@ public class ScanningContext : IDisposable
IMemoryImage { LogicalPixelFormat: ImagePixelFormat.Gray8 } => BitDepth.Grayscale,
_ => BitDepth.Color
};
return CreateProcessedImage(storage, bitDepth, false, -1, transforms);
}
internal ProcessedImage CreateProcessedImage(IImageStorage storage, BitDepth bitDepth, bool lossless, int quality)
{
return CreateProcessedImage(storage, bitDepth, lossless, quality, Enumerable.Empty<Transform>());
return CreateProcessedImage(storage, bitDepth, false, -1, null, transforms);
}
internal ProcessedImage CreateProcessedImage(IImageStorage storage, BitDepth bitDepth, bool lossless, int quality,
IEnumerable<Transform> transforms)
PageSize? pageSize)
{
return CreateProcessedImage(storage, bitDepth, lossless, quality, pageSize, Enumerable.Empty<Transform>());
}
internal ProcessedImage CreateProcessedImage(IImageStorage storage, BitDepth bitDepth, bool lossless, int quality,
PageSize? pageSize, IEnumerable<Transform> transforms)
{
var convertedStorage = ConvertStorageIfNeeded(storage, bitDepth, lossless, quality);
var metadata = new ImageMetadata(bitDepth, lossless);
var metadata = new ImageMetadata(bitDepth, lossless, pageSize);
var image = new ProcessedImage(
ImageContext,
convertedStorage,

View File

@ -38,7 +38,8 @@ public static class ImageSerializer
{
TransformListXml = image.TransformState.Transforms.ToXml(),
BitDepth = (SerializedImageMetadata.Types.BitDepth) image.Metadata.BitDepth,
Lossless = image.Metadata.Lossless
Lossless = image.Metadata.Lossless,
PageSize = image.Metadata.PageSize?.ToString() ?? ""
},
Thumbnail = thumbStream != null ? ByteString.FromStream(thumbStream) : ByteString.Empty,
BarcodeDetectionXml = image.PostProcessingData.BarcodeDetection?.ToXml(),
@ -144,6 +145,7 @@ public static class ImageSerializer
(BitDepth) serializedImage.Metadata.BitDepth,
serializedImage.Metadata.Lossless,
-1,
PageSize.Parse(serializedImage.Metadata.PageSize),
serializedImage.Metadata.TransformListXml.FromXml<List<Transform>>());
var thumbnail = serializedImage.Thumbnail.ToByteArray();

View File

@ -24,4 +24,5 @@ message SerializedImageMetadata {
}
BitDepth bitDepth = 2;
bool lossless = 3;
string pageSize = 4;
}