mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-19 03:37:38 +03:00
Add page size to image metadata
This commit is contained in:
parent
eb3e10cdad
commit
17c138d983
@ -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]
|
||||
|
@ -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,
|
||||
|
@ -11,4 +11,6 @@ public class RecoveryIndexImage
|
||||
public ScanBitDepth BitDepth { get; set; }
|
||||
|
||||
public bool HighQuality { get; set; }
|
||||
|
||||
public string? PageSize { get; set; }
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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}";
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace NAPS2.Scan;
|
||||
namespace NAPS2.Images;
|
||||
|
||||
public enum PageSizeUnit
|
||||
{
|
@ -51,7 +51,8 @@ public class ImageImporter : IImageImporter
|
||||
: frame,
|
||||
BitDepth.Color,
|
||||
lossless,
|
||||
-1);
|
||||
-1,
|
||||
null);
|
||||
image = _importPostProcessor.AddPostProcessingData(
|
||||
image,
|
||||
frame,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -24,4 +24,5 @@ message SerializedImageMetadata {
|
||||
}
|
||||
BitDepth bitDepth = 2;
|
||||
bool lossless = 3;
|
||||
string pageSize = 4;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user